mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Improve selectors and Refactor Icons
This commit is contained in:
parent
a3d62d60ec
commit
f68484c834
|
@ -1,9 +1,7 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { FaSquarePlus } from 'react-icons/fa6';
|
|
||||||
import { IoLibrary } from 'react-icons/io5';
|
|
||||||
|
|
||||||
import { IconManuals } from '@/components/Icons';
|
import { IconLibrary2, IconManuals, IconNewItem2 } from '@/components/Icons';
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||||
import { useConceptOptions } from '@/context/OptionsContext';
|
import { useConceptOptions } from '@/context/OptionsContext';
|
||||||
|
@ -51,13 +49,13 @@ function Navigation() {
|
||||||
<NavigationButton
|
<NavigationButton
|
||||||
text='Новая схема'
|
text='Новая схема'
|
||||||
title='Создать новую схему'
|
title='Создать новую схему'
|
||||||
icon={<FaSquarePlus size='1.5rem' />}
|
icon={<IconNewItem2 size='1.5rem' />}
|
||||||
onClick={navigateCreateNew}
|
onClick={navigateCreateNew}
|
||||||
/>
|
/>
|
||||||
<NavigationButton
|
<NavigationButton
|
||||||
text='Библиотека'
|
text='Библиотека'
|
||||||
title='Список схем'
|
title='Список схем'
|
||||||
icon={<IoLibrary size='1.5rem' />}
|
icon={<IconLibrary2 size='1.5rem' />}
|
||||||
onClick={navigateLibrary}
|
onClick={navigateLibrary}
|
||||||
/>
|
/>
|
||||||
<NavigationButton text='Справка' title='Справочные материалы' icon={<IconManuals />} onClick={navigateHelp} />
|
<NavigationButton text='Справка' title='Справочные материалы' icon={<IconManuals />} onClick={navigateHelp} />
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
import { LuMoon, LuSun } from 'react-icons/lu';
|
import {
|
||||||
|
IconAdmin,
|
||||||
import { IconAdmin, IconAdminOff, IconDatabase, IconHelp, IconHelpOff, IconLogout, IconUser } from '@/components/Icons';
|
IconAdminOff,
|
||||||
|
IconDarkTheme,
|
||||||
|
IconDatabase,
|
||||||
|
IconHelp,
|
||||||
|
IconHelpOff,
|
||||||
|
IconLightTheme,
|
||||||
|
IconLogout,
|
||||||
|
IconUser
|
||||||
|
} from '@/components/Icons';
|
||||||
import { CProps } from '@/components/props';
|
import { CProps } from '@/components/props';
|
||||||
import Dropdown from '@/components/ui/Dropdown';
|
import Dropdown from '@/components/ui/Dropdown';
|
||||||
import DropdownButton from '@/components/ui/DropdownButton';
|
import DropdownButton from '@/components/ui/DropdownButton';
|
||||||
|
@ -50,7 +58,7 @@ function UserDropdown({ isOpen, hideDropdown }: UserDropdownProps) {
|
||||||
/>
|
/>
|
||||||
<DropdownButton
|
<DropdownButton
|
||||||
text={darkMode ? 'Тема: Темная' : 'Тема: Светлая'}
|
text={darkMode ? 'Тема: Темная' : 'Тема: Светлая'}
|
||||||
icon={darkMode ? <LuMoon size='1rem' /> : <LuSun size='1rem' />}
|
icon={darkMode ? <IconDarkTheme size='1rem' /> : <IconLightTheme size='1rem' />}
|
||||||
title='Переключение темы оформления'
|
title='Переключение темы оформления'
|
||||||
onClick={handleToggleDarkMode}
|
onClick={handleToggleDarkMode}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { AnimatePresence } from 'framer-motion';
|
import { AnimatePresence } from 'framer-motion';
|
||||||
import { FaCircleUser } from 'react-icons/fa6';
|
|
||||||
|
|
||||||
import { IconLogin } from '@/components/Icons';
|
import { IconLogin, IconUser2 } from '@/components/Icons';
|
||||||
import Loader from '@/components/ui/Loader';
|
import Loader from '@/components/ui/Loader';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
import AnimateFade from '@/components/wrap/AnimateFade';
|
||||||
import { useAuth } from '@/context/AuthContext';
|
import { useAuth } from '@/context/AuthContext';
|
||||||
|
@ -40,7 +39,7 @@ function UserMenu() {
|
||||||
{user ? (
|
{user ? (
|
||||||
<AnimateFade key='nav_user_badge_profile' className='h-full'>
|
<AnimateFade key='nav_user_badge_profile' className='h-full'>
|
||||||
<NavigationButton
|
<NavigationButton
|
||||||
icon={<FaCircleUser size='1.5rem' className={adminMode && user.is_staff ? 'icon-primary' : ''} />}
|
icon={<IconUser2 size='1.5rem' className={adminMode && user.is_staff ? 'icon-primary' : ''} />}
|
||||||
onClick={menu.toggle}
|
onClick={menu.toggle}
|
||||||
/>
|
/>
|
||||||
</AnimateFade>
|
</AnimateFade>
|
||||||
|
|
|
@ -1,62 +1,86 @@
|
||||||
// Search new icons at https://reactsvgicons.com/
|
// Search new icons at https://reactsvgicons.com/
|
||||||
|
|
||||||
|
// ==== General actions =======
|
||||||
export { LuLogOut as IconLogout } from 'react-icons/lu';
|
export { LuLogOut as IconLogout } from 'react-icons/lu';
|
||||||
export { FiSave as IconSave } from 'react-icons/fi';
|
export { FiSave as IconSave } from 'react-icons/fi';
|
||||||
export { BiCheck as IconAccept } from 'react-icons/bi';
|
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 { BiX as IconRemove } from 'react-icons/bi';
|
||||||
export { BiTrash as IconDestroy } from 'react-icons/bi';
|
export { BiTrash as IconDestroy } from 'react-icons/bi';
|
||||||
export { BiReset as IconReset } from 'react-icons/bi';
|
export { BiReset as IconReset } from 'react-icons/bi';
|
||||||
export { BiPlusCircle as IconNewItem } from 'react-icons/bi';
|
export { LiaEdit as IconEdit } from 'react-icons/lia';
|
||||||
export { BiDuplicate as IconClone } from 'react-icons/bi';
|
export { BiSearchAlt2 as IconSearch } from 'react-icons/bi';
|
||||||
export { LuReplace as IconReplace } from 'react-icons/lu';
|
|
||||||
export { BiDownload as IconDownload } from 'react-icons/bi';
|
export { BiDownload as IconDownload } from 'react-icons/bi';
|
||||||
export { BiUpload as IconUpload } from 'react-icons/bi';
|
export { BiUpload as IconUpload } from 'react-icons/bi';
|
||||||
export { LiaEdit as IconEdit } from 'react-icons/lia';
|
export { BiCog as IconSettings } from 'react-icons/bi';
|
||||||
|
export { BiShareAlt as IconShare } from 'react-icons/bi';
|
||||||
|
export { BiFilterAlt as IconFilter } from 'react-icons/bi';
|
||||||
|
export { BiDownArrowCircle as IconOpenList } from 'react-icons/bi';
|
||||||
|
|
||||||
|
// ===== UI elements =======
|
||||||
|
export { BiX as IconClose } from 'react-icons/bi';
|
||||||
|
export { LuChevronDown as IconDropArrow } from 'react-icons/lu';
|
||||||
|
export { LuChevronUp as IconDropArrowUp } from 'react-icons/lu';
|
||||||
|
export { RiMenuFoldFill as IconMenuFold } from 'react-icons/ri';
|
||||||
|
export { RiMenuUnfoldFill as IconMenuUnfold } from 'react-icons/ri';
|
||||||
|
export { LuMoon as IconDarkTheme } from 'react-icons/lu';
|
||||||
|
export { LuSun as IconLightTheme } from 'react-icons/lu';
|
||||||
|
export { LuLightbulb as IconHelp } from 'react-icons/lu';
|
||||||
|
export { LuLightbulbOff as IconHelpOff } from 'react-icons/lu';
|
||||||
export { RiPushpinFill as IconPin } from 'react-icons/ri';
|
export { RiPushpinFill as IconPin } from 'react-icons/ri';
|
||||||
export { RiUnpinLine as IconUnpin } from 'react-icons/ri';
|
export { RiUnpinLine as IconUnpin } from 'react-icons/ri';
|
||||||
export { BiCog as IconSettings } from 'react-icons/bi';
|
export { BiCaretDown as IconSortDesc } from 'react-icons/bi';
|
||||||
|
export { BiCaretUp as IconSortAsc } from 'react-icons/bi';
|
||||||
|
|
||||||
|
// ==== User status =======
|
||||||
export { LuUserCircle2 as IconUser } from 'react-icons/lu';
|
export { LuUserCircle2 as IconUser } from 'react-icons/lu';
|
||||||
|
export { FaCircleUser as IconUser2 } from 'react-icons/fa6';
|
||||||
export { LuCrown as IconOwner } from 'react-icons/lu';
|
export { LuCrown as IconOwner } from 'react-icons/lu';
|
||||||
export { TbMeteor as IconAdmin } from 'react-icons/tb';
|
export { TbMeteor as IconAdmin } from 'react-icons/tb';
|
||||||
export { TbMeteorOff as IconAdminOff } from 'react-icons/tb';
|
export { TbMeteorOff as IconAdminOff } from 'react-icons/tb';
|
||||||
export { LuGlasses as IconReader } from 'react-icons/lu';
|
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 { FaSortAmountDownAlt as IconSortText } from 'react-icons/fa';
|
|
||||||
|
|
||||||
export { LuChevronDown as IconDropArrow } from 'react-icons/lu';
|
|
||||||
export { LuChevronUp as IconDropArrowUp } from 'react-icons/lu';
|
|
||||||
|
|
||||||
|
// ===== Domain entities =======
|
||||||
|
export { IoLibrary as IconLibrary2 } from 'react-icons/io5';
|
||||||
export { LuDatabase as IconDatabase } from 'react-icons/lu';
|
export { LuDatabase as IconDatabase } from 'react-icons/lu';
|
||||||
export { LuImage as IconImage } from 'react-icons/lu';
|
export { LuImage as IconImage } from 'react-icons/lu';
|
||||||
export { TbColumns as IconList } from 'react-icons/tb';
|
export { TbColumns as IconList } from 'react-icons/tb';
|
||||||
export { TbColumnsOff as IconListOff } from 'react-icons/tb';
|
export { TbColumnsOff as IconListOff } from 'react-icons/tb';
|
||||||
|
export { LuAtSign as IconTerm } from 'react-icons/lu';
|
||||||
|
export { LuSubscript as IconAlias } from 'react-icons/lu';
|
||||||
|
export { TbMathFunction as IconFormula } from 'react-icons/tb';
|
||||||
export { BiFontFamily as IconText } from 'react-icons/bi';
|
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 { RiTreeLine as IconTree } from 'react-icons/ri';
|
export { RiTreeLine as IconTree } from 'react-icons/ri';
|
||||||
|
export { FaRegKeyboard as IconControls } from 'react-icons/fa6';
|
||||||
|
export { BiCheckShield as IconImmutable } from 'react-icons/bi';
|
||||||
|
export { RiOpenSourceLine as IconPublic } from 'react-icons/ri';
|
||||||
|
|
||||||
|
// ===== Domain actions =====
|
||||||
|
export { BiUpvote as IconMoveUp } from 'react-icons/bi';
|
||||||
|
export { BiDownvote as IconMoveDown } from 'react-icons/bi';
|
||||||
|
export { BiRightArrow as IconMoveRight } from 'react-icons/bi';
|
||||||
|
export { BiLeftArrow as IconMoveLeft } from 'react-icons/bi';
|
||||||
|
export { FiBell as IconFollow } from 'react-icons/fi';
|
||||||
|
export { FiBellOff as IconFollowOff } from 'react-icons/fi';
|
||||||
|
export { FaSortAmountDownAlt as IconSortList } from 'react-icons/fa';
|
||||||
|
export { BiPlusCircle as IconNewItem } from 'react-icons/bi';
|
||||||
|
export { FaSquarePlus as IconNewItem2 } from 'react-icons/fa6';
|
||||||
|
export { BiDuplicate as IconClone } from 'react-icons/bi';
|
||||||
|
export { LuReplace as IconReplace } from 'react-icons/lu';
|
||||||
|
|
||||||
|
// ======== Graph UI =======
|
||||||
export { BiCollapse as IconGraphCollapse } from 'react-icons/bi';
|
export { BiCollapse as IconGraphCollapse } from 'react-icons/bi';
|
||||||
export { BiExpand as IconGraphExpand } from 'react-icons/bi';
|
export { BiExpand as IconGraphExpand } from 'react-icons/bi';
|
||||||
export { LuMaximize as IconGraphMaximize } from 'react-icons/lu';
|
export { LuMaximize as IconGraphMaximize } from 'react-icons/lu';
|
||||||
export { BiGitBranch as IconGraphInputs } from 'react-icons/bi';
|
export { BiGitBranch as IconGraphInputs } from 'react-icons/bi';
|
||||||
export { BiGitMerge as IconGraphOutputs } from 'react-icons/bi';
|
export { BiGitMerge as IconGraphOutputs } from 'react-icons/bi';
|
||||||
export { LuAtom as IconGraphCore } from 'react-icons/lu';
|
export { LuAtom as IconGraphCore } from 'react-icons/lu';
|
||||||
|
|
||||||
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 { LuRotate3D as IconRotate3D } from 'react-icons/lu';
|
||||||
export { MdOutlineFitScreen as IconFitImage } from 'react-icons/md';
|
export { MdOutlineFitScreen as IconFitImage } from 'react-icons/md';
|
||||||
export { LuSparkles as IconClustering } from 'react-icons/lu';
|
export { LuSparkles as IconClustering } from 'react-icons/lu';
|
||||||
export { LuSparkle as IconClusteringOff } from 'react-icons/lu';
|
export { LuSparkle as IconClusteringOff } from 'react-icons/lu';
|
||||||
|
|
||||||
|
// ===== Custom elements ======
|
||||||
interface IconSVGProps {
|
interface IconSVGProps {
|
||||||
viewBox: string;
|
viewBox: string;
|
||||||
size?: string;
|
size?: string;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { IconFollow, IconImmutable, IconPublic } from '../Icons';
|
import { IconImmutable, IconPublic } from '../Icons';
|
||||||
|
|
||||||
function HelpLibrary() {
|
function HelpLibrary() {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
|
@ -9,10 +9,6 @@ function HelpLibrary() {
|
||||||
<p>Фильтрация с помощью инструментов в верхней части страницы</p>
|
<p>Фильтрация с помощью инструментов в верхней части страницы</p>
|
||||||
<p>Сортировка по клику на заголовок таблицы</p>
|
<p>Сортировка по клику на заголовок таблицы</p>
|
||||||
<h2>Отображение статусов</h2>
|
<h2>Отображение статусов</h2>
|
||||||
<div className='flex items-center gap-2'>
|
|
||||||
<IconFollow size='1rem'/>
|
|
||||||
<p><b>отслеживаемая</b> обозначает отслеживание схемы</p>
|
|
||||||
</div>
|
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2'>
|
||||||
<IconPublic size='1rem'/>
|
<IconPublic size='1rem'/>
|
||||||
<p><b>общедоступная</b> отображает схему всем пользователям</p>
|
<p><b>общедоступная</b> отображает схему всем пользователям</p>
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { describeConstituenta } from '@/utils/labels';
|
||||||
|
|
||||||
import ConstituentaBadge from '../info/ConstituentaBadge';
|
import ConstituentaBadge from '../info/ConstituentaBadge';
|
||||||
import FlexColumn from '../ui/FlexColumn';
|
import FlexColumn from '../ui/FlexColumn';
|
||||||
import SelectGraphToolbar from './SelectGraphToolbar';
|
import GraphSelectionToolbar from './GraphSelectionToolbar';
|
||||||
|
|
||||||
interface ConstituentaMultiPickerProps {
|
interface ConstituentaMultiPickerProps {
|
||||||
id?: string;
|
id?: string;
|
||||||
|
@ -80,7 +80,7 @@ function ConstituentaMultiPicker({ id, schema, prefixID, rows, selected, setSele
|
||||||
Выбраны {selected.length} из {schema?.items.length ?? 0}
|
Выбраны {selected.length} из {schema?.items.length ?? 0}
|
||||||
</span>
|
</span>
|
||||||
{schema ? (
|
{schema ? (
|
||||||
<SelectGraphToolbar
|
<GraphSelectionToolbar
|
||||||
graph={schema.graph}
|
graph={schema.graph}
|
||||||
core={schema.items.filter(cst => isBasicConcept(cst.cst_type)).map(cst => cst.id)}
|
core={schema.items.filter(cst => isBasicConcept(cst.cst_type)).map(cst => cst.id)}
|
||||||
setSelected={setSelected}
|
setSelected={setSelected}
|
||||||
|
|
|
@ -14,13 +14,13 @@ import {
|
||||||
import { CProps } from '../props';
|
import { CProps } from '../props';
|
||||||
import MiniButton from '../ui/MiniButton';
|
import MiniButton from '../ui/MiniButton';
|
||||||
|
|
||||||
interface SelectGraphToolbarProps extends CProps.Styling {
|
interface GraphSelectionToolbarProps extends CProps.Styling {
|
||||||
graph: Graph;
|
graph: Graph;
|
||||||
core: number[];
|
core: number[];
|
||||||
setSelected: React.Dispatch<React.SetStateAction<number[]>>;
|
setSelected: React.Dispatch<React.SetStateAction<number[]>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectGraphToolbar({ className, graph, core, setSelected, ...restProps }: SelectGraphToolbarProps) {
|
function GraphSelectionToolbar({ className, graph, core, setSelected, ...restProps }: GraphSelectionToolbarProps) {
|
||||||
return (
|
return (
|
||||||
<div className={clsx('cc-icons', className)} {...restProps}>
|
<div className={clsx('cc-icons', className)} {...restProps}>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
|
@ -62,4 +62,4 @@ function SelectGraphToolbar({ className, graph, core, setSelected, ...restProps
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SelectGraphToolbar;
|
export default GraphSelectionToolbar;
|
|
@ -0,0 +1,93 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
|
import { IconFilter, IconFollow, IconImmutable, IconOwner, IconPublic } from '@/components/Icons';
|
||||||
|
import Dropdown from '@/components/ui/Dropdown';
|
||||||
|
import SelectorButton from '@/components/ui/SelectorButton';
|
||||||
|
import { useAuth } from '@/context/AuthContext';
|
||||||
|
import useDropdown from '@/hooks/useDropdown';
|
||||||
|
import useWindowSize from '@/hooks/useWindowSize';
|
||||||
|
import { LibraryFilterStrategy } from '@/models/miscellaneous';
|
||||||
|
import { prefixes } from '@/utils/constants';
|
||||||
|
import { describeLibraryFilter, labelLibraryFilter } from '@/utils/labels';
|
||||||
|
|
||||||
|
import DropdownButton from '../ui/DropdownButton';
|
||||||
|
|
||||||
|
function StrategyIcon(strategy: LibraryFilterStrategy, size: string, color?: string) {
|
||||||
|
switch (strategy) {
|
||||||
|
case LibraryFilterStrategy.MANUAL:
|
||||||
|
return <IconFilter size={size} className={color} />;
|
||||||
|
case LibraryFilterStrategy.CANONICAL:
|
||||||
|
return <IconImmutable size={size} className={color} />;
|
||||||
|
case LibraryFilterStrategy.COMMON:
|
||||||
|
return <IconPublic size={size} className={color} />;
|
||||||
|
case LibraryFilterStrategy.OWNED:
|
||||||
|
return <IconOwner size={size} className={color} />;
|
||||||
|
case LibraryFilterStrategy.SUBSCRIBE:
|
||||||
|
return <IconFollow size={size} className={color} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SelectFilterStrategyProps {
|
||||||
|
value: LibraryFilterStrategy;
|
||||||
|
onChange: (value: LibraryFilterStrategy) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectFilterStrategy({ value, onChange }: SelectFilterStrategyProps) {
|
||||||
|
const menu = useDropdown();
|
||||||
|
const { user } = useAuth();
|
||||||
|
const size = useWindowSize();
|
||||||
|
|
||||||
|
const handleChange = useCallback(
|
||||||
|
(newValue: LibraryFilterStrategy) => {
|
||||||
|
menu.hide();
|
||||||
|
onChange(newValue);
|
||||||
|
},
|
||||||
|
[menu, onChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
function isStrategyDisabled(strategy: LibraryFilterStrategy): boolean {
|
||||||
|
if (strategy === LibraryFilterStrategy.SUBSCRIBE || strategy === LibraryFilterStrategy.OWNED) {
|
||||||
|
return !user;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={menu.ref} className='h-full text-right'>
|
||||||
|
<SelectorButton
|
||||||
|
transparent
|
||||||
|
tabIndex={-1}
|
||||||
|
title={describeLibraryFilter(value)}
|
||||||
|
hideTitle={menu.isOpen}
|
||||||
|
className='h-full'
|
||||||
|
icon={StrategyIcon(value, '1rem', value !== LibraryFilterStrategy.MANUAL ? 'icon-primary' : '')}
|
||||||
|
text={size.isSmall ? undefined : labelLibraryFilter(value)}
|
||||||
|
onClick={menu.toggle}
|
||||||
|
/>
|
||||||
|
<Dropdown isOpen={menu.isOpen}>
|
||||||
|
{Object.values(LibraryFilterStrategy).map((enumValue, index) => {
|
||||||
|
const strategy = enumValue as LibraryFilterStrategy;
|
||||||
|
return (
|
||||||
|
<DropdownButton
|
||||||
|
className='w-[10rem]'
|
||||||
|
key={`${prefixes.library_filters_list}${index}`}
|
||||||
|
onClick={() => handleChange(strategy)}
|
||||||
|
title={describeLibraryFilter(strategy)}
|
||||||
|
disabled={isStrategyDisabled(strategy)}
|
||||||
|
>
|
||||||
|
<div className='inline-flex items-center gap-3'>
|
||||||
|
{StrategyIcon(strategy, '1rem')}
|
||||||
|
{labelLibraryFilter(strategy)}
|
||||||
|
</div>
|
||||||
|
</DropdownButton>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SelectFilterStrategy;
|
|
@ -0,0 +1,91 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
|
import Dropdown from '@/components/ui/Dropdown';
|
||||||
|
import SelectorButton from '@/components/ui/SelectorButton';
|
||||||
|
import useDropdown from '@/hooks/useDropdown';
|
||||||
|
import useWindowSize from '@/hooks/useWindowSize';
|
||||||
|
import { DependencyMode } from '@/models/miscellaneous';
|
||||||
|
import { prefixes } from '@/utils/constants';
|
||||||
|
import { describeCstSource, labelCstSource } from '@/utils/labels';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IconGraphCollapse,
|
||||||
|
IconGraphExpand,
|
||||||
|
IconGraphInputs,
|
||||||
|
IconGraphOutputs,
|
||||||
|
IconSettings,
|
||||||
|
IconText
|
||||||
|
} from '../Icons';
|
||||||
|
import DropdownButton from '../ui/DropdownButton';
|
||||||
|
|
||||||
|
function DependencyIcon(mode: DependencyMode, size: string, color?: string) {
|
||||||
|
switch (mode) {
|
||||||
|
case DependencyMode.ALL:
|
||||||
|
return <IconSettings size={size} className={color} />;
|
||||||
|
case DependencyMode.EXPRESSION:
|
||||||
|
return <IconText size={size} className={color} />;
|
||||||
|
case DependencyMode.OUTPUTS:
|
||||||
|
return <IconGraphOutputs size={size} className={color} />;
|
||||||
|
case DependencyMode.INPUTS:
|
||||||
|
return <IconGraphInputs size={size} className={color} />;
|
||||||
|
case DependencyMode.EXPAND_OUTPUTS:
|
||||||
|
return <IconGraphExpand size={size} className={color} />;
|
||||||
|
case DependencyMode.EXPAND_INPUTS:
|
||||||
|
return <IconGraphCollapse size={size} className={color} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
interface SelectGraphFilterProps {
|
||||||
|
value: DependencyMode;
|
||||||
|
onChange: (value: DependencyMode) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectGraphFilter({ value, onChange }: SelectGraphFilterProps) {
|
||||||
|
const menu = useDropdown();
|
||||||
|
const size = useWindowSize();
|
||||||
|
|
||||||
|
const handleChange = useCallback(
|
||||||
|
(newValue: DependencyMode) => {
|
||||||
|
menu.hide();
|
||||||
|
onChange(newValue);
|
||||||
|
},
|
||||||
|
[menu, onChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={menu.ref}>
|
||||||
|
<SelectorButton
|
||||||
|
transparent
|
||||||
|
tabIndex={-1}
|
||||||
|
title='Настройка фильтрации по графу термов'
|
||||||
|
hideTitle={menu.isOpen}
|
||||||
|
className='h-full pr-2'
|
||||||
|
icon={DependencyIcon(value, '1rem', value !== DependencyMode.ALL ? 'icon-primary' : '')}
|
||||||
|
text={size.isSmall ? undefined : labelCstSource(value)}
|
||||||
|
onClick={menu.toggle}
|
||||||
|
/>
|
||||||
|
<Dropdown stretchLeft isOpen={menu.isOpen}>
|
||||||
|
{Object.values(DependencyMode)
|
||||||
|
.filter(value => !isNaN(Number(value)))
|
||||||
|
.map((value, index) => {
|
||||||
|
const source = value as DependencyMode;
|
||||||
|
return (
|
||||||
|
<DropdownButton
|
||||||
|
className='w-[18rem]'
|
||||||
|
key={`${prefixes.cst_source_list}${index}`}
|
||||||
|
onClick={() => handleChange(source)}
|
||||||
|
>
|
||||||
|
<div className='inline-flex items-center gap-1'>
|
||||||
|
{DependencyIcon(source, '1rem')}
|
||||||
|
<b>{labelCstSource(source)}:</b> {describeCstSource(source)}
|
||||||
|
</div>
|
||||||
|
</DropdownButton>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SelectGraphFilter;
|
82
rsconcept/frontend/src/components/select/SelectMatchMode.tsx
Normal file
82
rsconcept/frontend/src/components/select/SelectMatchMode.tsx
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
|
import Dropdown from '@/components/ui/Dropdown';
|
||||||
|
import SelectorButton from '@/components/ui/SelectorButton';
|
||||||
|
import useDropdown from '@/hooks/useDropdown';
|
||||||
|
import useWindowSize from '@/hooks/useWindowSize';
|
||||||
|
import { CstMatchMode } from '@/models/miscellaneous';
|
||||||
|
import { prefixes } from '@/utils/constants';
|
||||||
|
import { describeCstMatchMode, labelCstMatchMode } from '@/utils/labels';
|
||||||
|
|
||||||
|
import { IconAlias, IconTerm, IconFilter, IconFormula, IconText } from '../Icons';
|
||||||
|
import DropdownButton from '../ui/DropdownButton';
|
||||||
|
|
||||||
|
function MatchModeIcon(mode: CstMatchMode, size: string, color?: string) {
|
||||||
|
switch (mode) {
|
||||||
|
case CstMatchMode.ALL:
|
||||||
|
return <IconFilter size={size} className={color} />;
|
||||||
|
case CstMatchMode.TEXT:
|
||||||
|
return <IconText size={size} className={color} />;
|
||||||
|
case CstMatchMode.EXPR:
|
||||||
|
return <IconFormula size={size} className={color} />;
|
||||||
|
case CstMatchMode.TERM:
|
||||||
|
return <IconTerm size={size} className={color} />;
|
||||||
|
case CstMatchMode.NAME:
|
||||||
|
return <IconAlias size={size} className={color} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
interface SelectMatchModeProps {
|
||||||
|
value: CstMatchMode;
|
||||||
|
onChange: (value: CstMatchMode) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectMatchMode({ value, onChange }: SelectMatchModeProps) {
|
||||||
|
const menu = useDropdown();
|
||||||
|
const size = useWindowSize();
|
||||||
|
|
||||||
|
const handleChange = useCallback(
|
||||||
|
(newValue: CstMatchMode) => {
|
||||||
|
menu.hide();
|
||||||
|
onChange(newValue);
|
||||||
|
},
|
||||||
|
[menu, onChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={menu.ref}>
|
||||||
|
<SelectorButton
|
||||||
|
transparent
|
||||||
|
tabIndex={-1}
|
||||||
|
title='Настройка фильтрации по проверяемым атрибутам'
|
||||||
|
hideTitle={menu.isOpen}
|
||||||
|
className='h-full pr-2'
|
||||||
|
icon={MatchModeIcon(value, '1rem', value !== CstMatchMode.ALL ? 'icon-primary' : '')}
|
||||||
|
text={size.isSmall ? undefined : labelCstMatchMode(value)}
|
||||||
|
onClick={menu.toggle}
|
||||||
|
/>
|
||||||
|
<Dropdown stretchLeft isOpen={menu.isOpen}>
|
||||||
|
{Object.values(CstMatchMode)
|
||||||
|
.filter(value => !isNaN(Number(value)))
|
||||||
|
.map((value, index) => {
|
||||||
|
const matchMode = value as CstMatchMode;
|
||||||
|
return (
|
||||||
|
<DropdownButton
|
||||||
|
className='w-[20rem]'
|
||||||
|
key={`${prefixes.cst_source_list}${index}`}
|
||||||
|
onClick={() => handleChange(matchMode)}
|
||||||
|
>
|
||||||
|
<div className='inline-flex items-center gap-1'>
|
||||||
|
{MatchModeIcon(matchMode, '1rem')}
|
||||||
|
<b>{labelCstMatchMode(matchMode)}:</b> {describeCstMatchMode(matchMode)}
|
||||||
|
</div>
|
||||||
|
</DropdownButton>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SelectMatchMode;
|
|
@ -1,5 +1,6 @@
|
||||||
import { Column } from '@tanstack/react-table';
|
import { Column } from '@tanstack/react-table';
|
||||||
import { BiCaretDown, BiCaretUp } from 'react-icons/bi';
|
|
||||||
|
import { IconSortAsc, IconSortDesc } from '@/components/Icons';
|
||||||
|
|
||||||
interface SortingIconProps<TData> {
|
interface SortingIconProps<TData> {
|
||||||
column: Column<TData>;
|
column: Column<TData>;
|
||||||
|
@ -9,9 +10,9 @@ function SortingIcon<TData>({ column }: SortingIconProps<TData>) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{{
|
{{
|
||||||
desc: <BiCaretDown size='1rem' />,
|
desc: <IconSortDesc size='1rem' />,
|
||||||
asc: <BiCaretUp size='1rem' />
|
asc: <IconSortAsc size='1rem' />
|
||||||
}[column.getIsSorted() as string] ?? <BiCaretDown size='1rem' className='opacity-0 hover:opacity-50' />}
|
}[column.getIsSorted() as string] ?? <IconSortDesc size='1rem' className='opacity-0 hover:opacity-50' />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { BiSearchAlt2 } from 'react-icons/bi';
|
import { IconSearch } from '../Icons';
|
||||||
|
|
||||||
import { CProps } from '../props';
|
import { CProps } from '../props';
|
||||||
import Overlay from './Overlay';
|
import Overlay from './Overlay';
|
||||||
import TextInput from './TextInput';
|
import TextInput from './TextInput';
|
||||||
|
@ -15,7 +14,7 @@ function SearchBar({ id, value, onChange, noBorder, ...restProps }: SearchBarPro
|
||||||
return (
|
return (
|
||||||
<div {...restProps}>
|
<div {...restProps}>
|
||||||
<Overlay position='top-[-0.125rem] left-3 translate-y-1/2' className='pointer-events-none clr-text-controls'>
|
<Overlay position='top-[-0.125rem] left-3 translate-y-1/2' className='pointer-events-none clr-text-controls'>
|
||||||
<BiSearchAlt2 size='1.25rem' />
|
<IconSearch size='1.25rem' />
|
||||||
</Overlay>
|
</Overlay>
|
||||||
<TextInput
|
<TextInput
|
||||||
id={id}
|
id={id}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {
|
||||||
postNewRSForm
|
postNewRSForm
|
||||||
} from '@/app/backendAPI';
|
} from '@/app/backendAPI';
|
||||||
import { ErrorData } from '@/components/info/InfoError';
|
import { ErrorData } from '@/components/info/InfoError';
|
||||||
import { ILibraryItem } from '@/models/library';
|
import { ILibraryItem, LibraryItemID } from '@/models/library';
|
||||||
import { matchLibraryItem } from '@/models/libraryAPI';
|
import { matchLibraryItem } from '@/models/libraryAPI';
|
||||||
import { ILibraryFilter } from '@/models/miscellaneous';
|
import { ILibraryFilter } from '@/models/miscellaneous';
|
||||||
import { IRSForm, IRSFormCloneData, IRSFormCreateData, IRSFormData } from '@/models/rsform';
|
import { IRSForm, IRSFormCloneData, IRSFormCreateData, IRSFormData } from '@/models/rsform';
|
||||||
|
@ -31,13 +31,13 @@ interface ILibraryContext {
|
||||||
setError: (error: ErrorData) => void;
|
setError: (error: ErrorData) => void;
|
||||||
|
|
||||||
applyFilter: (params: ILibraryFilter) => ILibraryItem[];
|
applyFilter: (params: ILibraryFilter) => ILibraryItem[];
|
||||||
retrieveTemplate: (templateID: number, callback: (schema: IRSForm) => void) => void;
|
retrieveTemplate: (templateID: LibraryItemID, callback: (schema: IRSForm) => void) => void;
|
||||||
createItem: (data: IRSFormCreateData, callback?: DataCallback<ILibraryItem>) => void;
|
createItem: (data: IRSFormCreateData, callback?: DataCallback<ILibraryItem>) => void;
|
||||||
cloneItem: (target: number, data: IRSFormCloneData, callback: DataCallback<IRSFormData>) => void;
|
cloneItem: (target: LibraryItemID, data: IRSFormCloneData, callback: DataCallback<IRSFormData>) => void;
|
||||||
destroyItem: (target: number, callback?: () => void) => void;
|
destroyItem: (target: LibraryItemID, callback?: () => void) => void;
|
||||||
|
|
||||||
localUpdateItem: (data: ILibraryItem) => void;
|
localUpdateItem: (data: ILibraryItem) => void;
|
||||||
localUpdateTimestamp: (target: number) => void;
|
localUpdateTimestamp: (target: LibraryItemID) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const LibraryContext = createContext<ILibraryContext | null>(null);
|
const LibraryContext = createContext<ILibraryContext | null>(null);
|
||||||
|
@ -79,9 +79,6 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
||||||
if (params.is_subscribed !== undefined) {
|
if (params.is_subscribed !== undefined) {
|
||||||
result = result.filter(item => user?.subscriptions.includes(item.id));
|
result = result.filter(item => user?.subscriptions.includes(item.id));
|
||||||
}
|
}
|
||||||
if (params.is_personal !== undefined) {
|
|
||||||
result = result.filter(item => user?.subscriptions.includes(item.id) || item.owner === user?.id);
|
|
||||||
}
|
|
||||||
if (params.query) {
|
if (params.query) {
|
||||||
result = result.filter(item => matchLibraryItem(item, params.query!));
|
result = result.filter(item => matchLibraryItem(item, params.query!));
|
||||||
}
|
}
|
||||||
|
@ -91,7 +88,7 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
const retrieveTemplate = useCallback(
|
const retrieveTemplate = useCallback(
|
||||||
(templateID: number, callback: (schema: IRSForm) => void) => {
|
(templateID: LibraryItemID, callback: (schema: IRSForm) => void) => {
|
||||||
const cached = cachedTemplates.find(schema => schema.id == templateID);
|
const cached = cachedTemplates.find(schema => schema.id == templateID);
|
||||||
if (cached) {
|
if (cached) {
|
||||||
callback(cached);
|
callback(cached);
|
||||||
|
@ -166,7 +163,7 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
const localUpdateTimestamp = useCallback(
|
const localUpdateTimestamp = useCallback(
|
||||||
(target: number) => {
|
(target: LibraryItemID) => {
|
||||||
const libraryItem = items.find(item => item.id === target);
|
const libraryItem = items.find(item => item.id === target);
|
||||||
if (libraryItem) {
|
if (libraryItem) {
|
||||||
libraryItem.time_update = Date();
|
libraryItem.time_update = Date();
|
||||||
|
@ -196,7 +193,7 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
const destroyItem = useCallback(
|
const destroyItem = useCallback(
|
||||||
(target: number, callback?: () => void) => {
|
(target: LibraryItemID, callback?: () => void) => {
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
deleteLibraryItem(String(target), {
|
deleteLibraryItem(String(target), {
|
||||||
showError: true,
|
showError: true,
|
||||||
|
@ -218,7 +215,7 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
const cloneItem = useCallback(
|
const cloneItem = useCallback(
|
||||||
(target: number, data: IRSFormCloneData, callback: DataCallback<IRSFormData>) => {
|
(target: LibraryItemID, data: IRSFormCloneData, callback: DataCallback<IRSFormData>) => {
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
import { createContext, useCallback, useContext, useEffect, useState } from 'react';
|
import { createContext, useCallback, useContext, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { DataCallback, getProfile, patchProfile } from '@/app/backendAPI';
|
||||||
import { ErrorData } from '@/components/info/InfoError';
|
import { ErrorData } from '@/components/info/InfoError';
|
||||||
import { IUserProfile } from '@/models/library';
|
import { IUserProfile } from '@/models/library';
|
||||||
import { IUserUpdateData } from '@/models/library';
|
import { IUserUpdateData } from '@/models/library';
|
||||||
import { DataCallback, getProfile, patchProfile } from '@/app/backendAPI';
|
|
||||||
|
|
||||||
import { useUsers } from './UsersContext';
|
import { useUsers } from './UsersContext';
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,8 @@
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useLayoutEffect, useState } from 'react';
|
import { useLayoutEffect, useState } from 'react';
|
||||||
import { BiChevronsDown, BiLeftArrow, BiRightArrow } from 'react-icons/bi';
|
|
||||||
|
|
||||||
import { IconAccept, IconRemove } from '@/components/Icons';
|
import { IconAccept, IconMoveDown, IconMoveLeft, IconMoveRight, IconRemove } from '@/components/Icons';
|
||||||
import BadgeHelp from '@/components/man/BadgeHelp';
|
import BadgeHelp from '@/components/man/BadgeHelp';
|
||||||
import SelectGrammeme from '@/components/select/SelectGrammeme';
|
import SelectGrammeme from '@/components/select/SelectGrammeme';
|
||||||
import Label from '@/components/ui/Label';
|
import Label from '@/components/ui/Label';
|
||||||
|
@ -159,14 +158,14 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
|
||||||
<MiniButton
|
<MiniButton
|
||||||
noHover
|
noHover
|
||||||
title='Определить граммемы'
|
title='Определить граммемы'
|
||||||
icon={<BiRightArrow size='1.25rem' className='icon-primary' />}
|
icon={<IconMoveRight size='1.25rem' className='icon-primary' />}
|
||||||
disabled={textProcessor.loading || !inputText}
|
disabled={textProcessor.loading || !inputText}
|
||||||
onClick={handleParse}
|
onClick={handleParse}
|
||||||
/>
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
noHover
|
noHover
|
||||||
title='Генерировать словоформу'
|
title='Генерировать словоформу'
|
||||||
icon={<BiLeftArrow size='1.25rem' className='icon-primary' />}
|
icon={<IconMoveLeft size='1.25rem' className='icon-primary' />}
|
||||||
disabled={textProcessor.loading || inputGrams.length == 0}
|
disabled={textProcessor.loading || inputGrams.length == 0}
|
||||||
onClick={handleInflect}
|
onClick={handleInflect}
|
||||||
/>
|
/>
|
||||||
|
@ -190,7 +189,7 @@ function DlgEditWordForms({ hideWindow, target, onSave }: DlgEditWordFormsProps)
|
||||||
<MiniButton
|
<MiniButton
|
||||||
noHover
|
noHover
|
||||||
title='Генерировать стандартные словоформы'
|
title='Генерировать стандартные словоформы'
|
||||||
icon={<BiChevronsDown size='1.5rem' className='icon-primary' />}
|
icon={<IconMoveDown size='1.5rem' className='icon-primary' />}
|
||||||
disabled={textProcessor.loading || !inputText}
|
disabled={textProcessor.loading || !inputText}
|
||||||
onClick={handleGenerateLexeme}
|
onClick={handleGenerateLexeme}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -74,7 +74,6 @@ export enum CstMatchMode {
|
||||||
*/
|
*/
|
||||||
export interface ILibraryFilter {
|
export interface ILibraryFilter {
|
||||||
query?: string;
|
query?: string;
|
||||||
is_personal?: boolean;
|
|
||||||
is_owned?: boolean;
|
is_owned?: boolean;
|
||||||
is_common?: boolean;
|
is_common?: boolean;
|
||||||
is_canonical?: boolean;
|
is_canonical?: boolean;
|
||||||
|
@ -86,7 +85,6 @@ export interface ILibraryFilter {
|
||||||
*/
|
*/
|
||||||
export enum LibraryFilterStrategy {
|
export enum LibraryFilterStrategy {
|
||||||
MANUAL = 'manual',
|
MANUAL = 'manual',
|
||||||
PERSONAL = 'personal',
|
|
||||||
COMMON = 'common',
|
COMMON = 'common',
|
||||||
SUBSCRIBE = 'subscribe',
|
SUBSCRIBE = 'subscribe',
|
||||||
CANONICAL = 'canonical',
|
CANONICAL = 'canonical',
|
||||||
|
|
|
@ -51,7 +51,6 @@ export function filterFromStrategy(strategy: LibraryFilterStrategy): ILibraryFil
|
||||||
case LibraryFilterStrategy.MANUAL: return {};
|
case LibraryFilterStrategy.MANUAL: return {};
|
||||||
case LibraryFilterStrategy.COMMON: return { is_common: true };
|
case LibraryFilterStrategy.COMMON: return { is_common: true };
|
||||||
case LibraryFilterStrategy.CANONICAL: return { is_canonical: true };
|
case LibraryFilterStrategy.CANONICAL: return { is_canonical: true };
|
||||||
case LibraryFilterStrategy.PERSONAL: return { is_personal: true };
|
|
||||||
case LibraryFilterStrategy.SUBSCRIBE: return { is_subscribed: true };
|
case LibraryFilterStrategy.SUBSCRIBE: return { is_subscribed: true };
|
||||||
case LibraryFilterStrategy.OWNED: return { is_owned: true };
|
case LibraryFilterStrategy.OWNED: return { is_owned: true };
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { IconFollow, IconImmutable, IconPublic } from '@/components/Icons';
|
import { IconImmutable, IconPublic } from '@/components/Icons';
|
||||||
import { ICurrentUser, ILibraryItem } from '@/models/library';
|
import { ICurrentUser, ILibraryItem } from '@/models/library';
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
|
|
||||||
|
@ -9,14 +9,9 @@ interface ItemIconsProps {
|
||||||
item: ILibraryItem;
|
item: ILibraryItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ItemIcons({ user, item }: ItemIconsProps) {
|
function ItemIcons({ item }: ItemIconsProps) {
|
||||||
return (
|
return (
|
||||||
<div className={clsx('min-w-[3.3rem]', 'inline-flex gap-1 align-middle')} id={`${prefixes.library_list}${item.id}`}>
|
<div className={clsx('min-w-[2.2rem]', 'inline-flex gap-1 align-middle')} id={`${prefixes.library_list}${item.id}`}>
|
||||||
{user && user.subscriptions.includes(item.id) ? (
|
|
||||||
<span title='Отслеживаемая'>
|
|
||||||
<IconFollow size='1rem' />
|
|
||||||
</span>
|
|
||||||
) : null}
|
|
||||||
{item.is_common ? (
|
{item.is_common ? (
|
||||||
<span title='Общедоступная'>
|
<span title='Общедоступная'>
|
||||||
<IconPublic size='1rem' />
|
<IconPublic size='1rem' />
|
||||||
|
|
|
@ -1,75 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { useCallback } from 'react';
|
|
||||||
|
|
||||||
import { IconFilter } from '@/components/Icons';
|
|
||||||
import Dropdown from '@/components/ui/Dropdown';
|
|
||||||
import DropdownCheckbox from '@/components/ui/DropdownCheckbox';
|
|
||||||
import SelectorButton from '@/components/ui/SelectorButton';
|
|
||||||
import { useAuth } from '@/context/AuthContext';
|
|
||||||
import useDropdown from '@/hooks/useDropdown';
|
|
||||||
import { LibraryFilterStrategy } from '@/models/miscellaneous';
|
|
||||||
import { prefixes } from '@/utils/constants';
|
|
||||||
import { describeLibraryFilter, labelLibraryFilter } from '@/utils/labels';
|
|
||||||
|
|
||||||
interface PickerStrategyProps {
|
|
||||||
value: LibraryFilterStrategy;
|
|
||||||
onChange: (value: LibraryFilterStrategy) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
function PickerStrategy({ value, onChange }: PickerStrategyProps) {
|
|
||||||
const strategyMenu = useDropdown();
|
|
||||||
const { user } = useAuth();
|
|
||||||
|
|
||||||
const handleChange = useCallback(
|
|
||||||
(newValue: LibraryFilterStrategy) => {
|
|
||||||
strategyMenu.hide();
|
|
||||||
onChange(newValue);
|
|
||||||
},
|
|
||||||
[strategyMenu, onChange]
|
|
||||||
);
|
|
||||||
|
|
||||||
function isStrategyDisabled(strategy: LibraryFilterStrategy): boolean {
|
|
||||||
if (
|
|
||||||
strategy === LibraryFilterStrategy.PERSONAL ||
|
|
||||||
strategy === LibraryFilterStrategy.SUBSCRIBE ||
|
|
||||||
strategy === LibraryFilterStrategy.OWNED
|
|
||||||
) {
|
|
||||||
return !user;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div ref={strategyMenu.ref} className='h-full text-right'>
|
|
||||||
<SelectorButton
|
|
||||||
transparent
|
|
||||||
tabIndex={-1}
|
|
||||||
title='Список фильтров'
|
|
||||||
hideTitle={strategyMenu.isOpen}
|
|
||||||
className='h-full'
|
|
||||||
icon={<IconFilter size='1.25rem' />}
|
|
||||||
text={labelLibraryFilter(value)}
|
|
||||||
onClick={strategyMenu.toggle}
|
|
||||||
/>
|
|
||||||
<Dropdown isOpen={strategyMenu.isOpen}>
|
|
||||||
{Object.values(LibraryFilterStrategy).map((enumValue, index) => {
|
|
||||||
const strategy = enumValue as LibraryFilterStrategy;
|
|
||||||
return (
|
|
||||||
<DropdownCheckbox
|
|
||||||
key={`${prefixes.library_filters_list}${index}`}
|
|
||||||
value={value === strategy}
|
|
||||||
setValue={() => handleChange(strategy)}
|
|
||||||
label={labelLibraryFilter(strategy)}
|
|
||||||
title={describeLibraryFilter(strategy)}
|
|
||||||
disabled={isStrategyDisabled(strategy)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Dropdown>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default PickerStrategy;
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useCallback } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
import { urls } from '@/app/urls';
|
import { urls } from '@/app/urls';
|
||||||
import SearchBar from '@/components/ui/SearchBar';
|
import SearchBar from '@/components/ui/SearchBar';
|
||||||
|
@ -9,7 +9,7 @@ import { useConceptNavigation } from '@/context/NavigationContext';
|
||||||
import { ILibraryFilter } from '@/models/miscellaneous';
|
import { ILibraryFilter } from '@/models/miscellaneous';
|
||||||
import { LibraryFilterStrategy } from '@/models/miscellaneous';
|
import { LibraryFilterStrategy } from '@/models/miscellaneous';
|
||||||
|
|
||||||
import PickerStrategy from './PickerStrategy';
|
import SelectFilterStrategy from '../../components/select/SelectFilterStrategy';
|
||||||
|
|
||||||
interface SearchPanelProps {
|
interface SearchPanelProps {
|
||||||
total: number;
|
total: number;
|
||||||
|
@ -30,8 +30,7 @@ function SearchPanel({ total, filtered, query, setQuery, strategy, setFilter }:
|
||||||
is_owned: prev.is_owned,
|
is_owned: prev.is_owned,
|
||||||
is_common: prev.is_common,
|
is_common: prev.is_common,
|
||||||
is_canonical: prev.is_canonical,
|
is_canonical: prev.is_canonical,
|
||||||
is_subscribed: prev.is_subscribed,
|
is_subscribed: prev.is_subscribed
|
||||||
is_personal: prev.is_personal
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +43,11 @@ function SearchPanel({ total, filtered, query, setQuery, strategy, setFilter }:
|
||||||
[strategy, router]
|
[strategy, router]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const selectStrategy = useMemo(
|
||||||
|
() => <SelectFilterStrategy value={strategy} onChange={handleChangeStrategy} />,
|
||||||
|
[strategy, handleChangeStrategy]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
|
@ -68,7 +72,7 @@ function SearchPanel({ total, filtered, query, setQuery, strategy, setFilter }:
|
||||||
{filtered} из {total}
|
{filtered} из {total}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<PickerStrategy value={strategy} onChange={handleChangeStrategy} />
|
{selectStrategy}
|
||||||
<SearchBar
|
<SearchBar
|
||||||
id='library_search'
|
id='library_search'
|
||||||
noBorder
|
noBorder
|
||||||
|
|
|
@ -10,7 +10,6 @@ import { CProps } from '@/components/props';
|
||||||
import DataTable, { createColumnHelper, VisibilityState } from '@/components/ui/DataTable';
|
import DataTable, { createColumnHelper, VisibilityState } from '@/components/ui/DataTable';
|
||||||
import FlexColumn from '@/components/ui/FlexColumn';
|
import FlexColumn from '@/components/ui/FlexColumn';
|
||||||
import TextURL from '@/components/ui/TextURL';
|
import TextURL from '@/components/ui/TextURL';
|
||||||
import { useAuth } from '@/context/AuthContext';
|
|
||||||
import { useConceptNavigation } from '@/context/NavigationContext';
|
import { useConceptNavigation } from '@/context/NavigationContext';
|
||||||
import { useUsers } from '@/context/UsersContext';
|
import { useUsers } from '@/context/UsersContext';
|
||||||
import useLocalStorage from '@/hooks/useLocalStorage';
|
import useLocalStorage from '@/hooks/useLocalStorage';
|
||||||
|
@ -31,7 +30,6 @@ const columnHelper = createColumnHelper<ILibraryItem>();
|
||||||
function ViewLibrary({ items, resetQuery }: ViewLibraryProps) {
|
function ViewLibrary({ items, resetQuery }: ViewLibraryProps) {
|
||||||
const router = useConceptNavigation();
|
const router = useConceptNavigation();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { user } = useAuth();
|
|
||||||
const { getUserLabel } = useUsers();
|
const { getUserLabel } = useUsers();
|
||||||
const [itemsPerPage, setItemsPerPage] = useLocalStorage<number>(storage.libraryPagination, 50);
|
const [itemsPerPage, setItemsPerPage] = useLocalStorage<number>(storage.libraryPagination, 50);
|
||||||
|
|
||||||
|
@ -57,7 +55,7 @@ function ViewLibrary({ items, resetQuery }: ViewLibraryProps) {
|
||||||
size: 60,
|
size: 60,
|
||||||
minSize: 60,
|
minSize: 60,
|
||||||
maxSize: 60,
|
maxSize: 60,
|
||||||
cell: props => <ItemIcons item={props.row.original} user={user} />
|
cell: props => <ItemIcons item={props.row.original} />
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor('alias', {
|
columnHelper.accessor('alias', {
|
||||||
id: 'alias',
|
id: 'alias',
|
||||||
|
@ -108,7 +106,7 @@ function ViewLibrary({ items, resetQuery }: ViewLibraryProps) {
|
||||||
sortDescFirst: true
|
sortDescFirst: true
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
[intl, getUserLabel, user, windowSize]
|
[intl, getUserLabel, windowSize]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { RiMenuFoldFill, RiMenuUnfoldFill } from 'react-icons/ri';
|
|
||||||
|
|
||||||
|
import { IconMenuFold, IconMenuUnfold } from '@/components/Icons';
|
||||||
import Button from '@/components/ui/Button';
|
import Button from '@/components/ui/Button';
|
||||||
import { useConceptOptions } from '@/context/OptionsContext';
|
import { useConceptOptions } from '@/context/OptionsContext';
|
||||||
import useDropdown from '@/hooks/useDropdown';
|
import useDropdown from '@/hooks/useDropdown';
|
||||||
|
@ -50,7 +50,7 @@ function TopicsListDropDown({ activeTopic, onChangeTopic }: TopicsListDropDownPr
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
title='Список тем'
|
title='Список тем'
|
||||||
hideTitle={menu.isOpen}
|
hideTitle={menu.isOpen}
|
||||||
icon={!menu.isOpen ? <RiMenuUnfoldFill size='1.25rem' /> : <RiMenuFoldFill size='1.25rem' />}
|
icon={!menu.isOpen ? <IconMenuUnfold size='1.25rem' /> : <IconMenuFold size='1.25rem' />}
|
||||||
className='w-[3rem] h-7'
|
className='w-[3rem] h-7'
|
||||||
onClick={menu.toggle}
|
onClick={menu.toggle}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -3,10 +3,9 @@
|
||||||
import { ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
import { ReactCodeMirrorRef } from '@uiw/react-codemirror';
|
||||||
import { AnimatePresence } from 'framer-motion';
|
import { AnimatePresence } from 'framer-motion';
|
||||||
import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { FaRegKeyboard } from 'react-icons/fa6';
|
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import { IconList, IconListOff, IconText, IconTextOff, IconTree } from '@/components/Icons';
|
import { IconControls, IconList, IconListOff, IconText, IconTextOff, IconTree } from '@/components/Icons';
|
||||||
import BadgeHelp from '@/components/man/BadgeHelp';
|
import BadgeHelp from '@/components/man/BadgeHelp';
|
||||||
import RSInput from '@/components/RSInput';
|
import RSInput from '@/components/RSInput';
|
||||||
import { RSTextWrapper } from '@/components/RSInput/textEditing';
|
import { RSTextWrapper } from '@/components/RSInput/textEditing';
|
||||||
|
@ -182,7 +181,7 @@ function EditorRSExpression({
|
||||||
{!disabled || model.processing ? (
|
{!disabled || model.processing ? (
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Отображение специальной клавиатуры'
|
title='Отображение специальной клавиатуры'
|
||||||
icon={<FaRegKeyboard size='1.25rem' className={showControls ? 'icon-primary' : ''} />}
|
icon={<IconControls size='1.25rem' className={showControls ? 'icon-primary' : ''} />}
|
||||||
onClick={() => setShowControls(prev => !prev)}
|
onClick={() => setShowControls(prev => !prev)}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import { BiDownArrowCircle } from 'react-icons/bi';
|
import { IconClone, IconDestroy, IconMoveDown, IconMoveUp, IconNewItem, IconOpenList } from '@/components/Icons';
|
||||||
|
|
||||||
import { IconClone, IconDestroy, IconMoveDown, IconMoveUp, IconNewItem } from '@/components/Icons';
|
|
||||||
import BadgeHelp from '@/components/man/BadgeHelp';
|
import BadgeHelp from '@/components/man/BadgeHelp';
|
||||||
import Dropdown from '@/components/ui/Dropdown';
|
import Dropdown from '@/components/ui/Dropdown';
|
||||||
import DropdownButton from '@/components/ui/DropdownButton';
|
import DropdownButton from '@/components/ui/DropdownButton';
|
||||||
|
@ -49,7 +47,7 @@ function RSListToolbar() {
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Добавить пустую конституенту'
|
title='Добавить пустую конституенту'
|
||||||
hideTitle={insertMenu.isOpen}
|
hideTitle={insertMenu.isOpen}
|
||||||
icon={<BiDownArrowCircle size='1.25rem' className='icon-green' />}
|
icon={<IconOpenList size='1.25rem' className='icon-green' />}
|
||||||
disabled={controller.isProcessing}
|
disabled={controller.isProcessing}
|
||||||
onClick={insertMenu.toggle}
|
onClick={insertMenu.toggle}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { useDebounce } from 'use-debounce';
|
||||||
|
|
||||||
import InfoConstituenta from '@/components/info/InfoConstituenta';
|
import InfoConstituenta from '@/components/info/InfoConstituenta';
|
||||||
import SelectedCounter from '@/components/info/SelectedCounter';
|
import SelectedCounter from '@/components/info/SelectedCounter';
|
||||||
import SelectGraphToolbar from '@/components/select/SelectGraphToolbar';
|
import GraphSelectionToolbar from '@/components/select/GraphSelectionToolbar';
|
||||||
import { GraphCanvasRef, GraphEdge, GraphLayout, GraphNode } from '@/components/ui/GraphUI';
|
import { GraphCanvasRef, GraphEdge, GraphLayout, GraphNode } from '@/components/ui/GraphUI';
|
||||||
import Overlay from '@/components/ui/Overlay';
|
import Overlay from '@/components/ui/Overlay';
|
||||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
import AnimateFade from '@/components/wrap/AnimateFade';
|
||||||
|
@ -311,7 +311,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{!focusCst ? (
|
{!focusCst ? (
|
||||||
<SelectGraphToolbar
|
<GraphSelectionToolbar
|
||||||
graph={controller.schema!.graph}
|
graph={controller.schema!.graph}
|
||||||
core={controller.schema!.items.filter(cst => isBasicConcept(cst.cst_type)).map(cst => cst.id)}
|
core={controller.schema!.items.filter(cst => isBasicConcept(cst.cst_type)).map(cst => cst.id)}
|
||||||
setSelected={controller.setSelected}
|
setSelected={controller.setSelected}
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {
|
||||||
IconReader,
|
IconReader,
|
||||||
IconReplace,
|
IconReplace,
|
||||||
IconShare,
|
IconShare,
|
||||||
IconSortText,
|
IconSortList,
|
||||||
IconUpload
|
IconUpload
|
||||||
} from '@/components/Icons';
|
} from '@/components/Icons';
|
||||||
import Button from '@/components/ui/Button';
|
import Button from '@/components/ui/Button';
|
||||||
|
@ -226,7 +226,7 @@ function RSTabsMenu({ onDestroy }: RSTabsMenuProps) {
|
||||||
className='border-t-2'
|
className='border-t-2'
|
||||||
text='Упорядочить список'
|
text='Упорядочить список'
|
||||||
title='Упорядочить список конституент исходя из логики типов и связей конституент'
|
title='Упорядочить список конституент исходя из логики типов и связей конституент'
|
||||||
icon={<IconSortText size='1rem' className='icon-primary' />}
|
icon={<IconSortList size='1rem' className='icon-primary' />}
|
||||||
disabled={!controller.isContentEditable || controller.isProcessing}
|
disabled={!controller.isContentEditable || controller.isProcessing}
|
||||||
onClick={handleRestoreOrder}
|
onClick={handleRestoreOrder}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,29 +1,17 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useCallback, useLayoutEffect, useState } from 'react';
|
import { useLayoutEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import {
|
import SelectGraphFilter from '@/components/select/SelectGraphFilter';
|
||||||
IconFilter,
|
import SelectMatchMode from '@/components/select/SelectMatchMode';
|
||||||
IconGraphCollapse,
|
|
||||||
IconGraphExpand,
|
|
||||||
IconGraphInputs,
|
|
||||||
IconGraphOutputs,
|
|
||||||
IconSettings,
|
|
||||||
IconText
|
|
||||||
} from '@/components/Icons';
|
|
||||||
import Dropdown from '@/components/ui/Dropdown';
|
|
||||||
import DropdownButton from '@/components/ui/DropdownButton';
|
|
||||||
import SearchBar from '@/components/ui/SearchBar';
|
import SearchBar from '@/components/ui/SearchBar';
|
||||||
import SelectorButton from '@/components/ui/SelectorButton';
|
|
||||||
import useDropdown from '@/hooks/useDropdown';
|
|
||||||
import useLocalStorage from '@/hooks/useLocalStorage';
|
import useLocalStorage from '@/hooks/useLocalStorage';
|
||||||
import { CstMatchMode, DependencyMode } from '@/models/miscellaneous';
|
import { CstMatchMode, DependencyMode } from '@/models/miscellaneous';
|
||||||
import { applyGraphFilter } from '@/models/miscellaneousAPI';
|
import { applyGraphFilter } from '@/models/miscellaneousAPI';
|
||||||
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
|
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
|
||||||
import { createMockConstituenta, matchConstituenta } from '@/models/rsformAPI';
|
import { createMockConstituenta, matchConstituenta } from '@/models/rsformAPI';
|
||||||
import { extractGlobals } from '@/models/rslangAPI';
|
import { extractGlobals } from '@/models/rslangAPI';
|
||||||
import { prefixes, storage } from '@/utils/constants';
|
import { storage } from '@/utils/constants';
|
||||||
import { describeCstMatchMode, describeCstSource, labelCstMatchMode, labelCstSource } from '@/utils/labels';
|
|
||||||
|
|
||||||
interface ConstituentsSearchProps {
|
interface ConstituentsSearchProps {
|
||||||
schema?: IRSForm;
|
schema?: IRSForm;
|
||||||
|
@ -32,31 +20,11 @@ interface ConstituentsSearchProps {
|
||||||
setFiltered: React.Dispatch<React.SetStateAction<IConstituenta[]>>;
|
setFiltered: React.Dispatch<React.SetStateAction<IConstituenta[]>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function DependencyIcon(mode: DependencyMode, size: string) {
|
|
||||||
switch (mode) {
|
|
||||||
case DependencyMode.ALL:
|
|
||||||
return <IconSettings size={size} />;
|
|
||||||
case DependencyMode.EXPRESSION:
|
|
||||||
return <IconText size={size} />;
|
|
||||||
case DependencyMode.OUTPUTS:
|
|
||||||
return <IconGraphOutputs size={size} />;
|
|
||||||
case DependencyMode.INPUTS:
|
|
||||||
return <IconGraphInputs size={size} />;
|
|
||||||
case DependencyMode.EXPAND_OUTPUTS:
|
|
||||||
return <IconGraphExpand size={size} />;
|
|
||||||
case DependencyMode.EXPAND_INPUTS:
|
|
||||||
return <IconGraphCollapse size={size} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }: ConstituentsSearchProps) {
|
function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }: ConstituentsSearchProps) {
|
||||||
const [filterMatch, setFilterMatch] = useLocalStorage(storage.cstFilterMatch, CstMatchMode.ALL);
|
const [filterMatch, setFilterMatch] = useLocalStorage(storage.cstFilterMatch, CstMatchMode.ALL);
|
||||||
const [filterSource, setFilterSource] = useLocalStorage(storage.cstFilterGraph, DependencyMode.ALL);
|
const [filterSource, setFilterSource] = useLocalStorage(storage.cstFilterGraph, DependencyMode.ALL);
|
||||||
const [filterText, setFilterText] = useState('');
|
const [filterText, setFilterText] = useState('');
|
||||||
|
|
||||||
const matchModeMenu = useDropdown();
|
|
||||||
const sourceMenu = useDropdown();
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (!schema || schema.items.length === 0) {
|
if (!schema || schema.items.length === 0) {
|
||||||
setFiltered([]);
|
setFiltered([]);
|
||||||
|
@ -82,20 +50,14 @@ function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }:
|
||||||
setFiltered(result);
|
setFiltered(result);
|
||||||
}, [filterText, setFiltered, filterSource, activeExpression, schema?.items, schema, filterMatch, activeID]);
|
}, [filterText, setFiltered, filterSource, activeExpression, schema?.items, schema, filterMatch, activeID]);
|
||||||
|
|
||||||
const handleMatchModeChange = useCallback(
|
const selectGraph = useMemo(
|
||||||
(newValue: CstMatchMode) => {
|
() => <SelectGraphFilter value={filterSource} onChange={newValue => setFilterSource(newValue)} />,
|
||||||
matchModeMenu.hide();
|
[filterSource, setFilterSource]
|
||||||
setFilterMatch(newValue);
|
|
||||||
},
|
|
||||||
[matchModeMenu, setFilterMatch]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSourceChange = useCallback(
|
const selectMatchMode = useMemo(
|
||||||
(newValue: DependencyMode) => {
|
() => <SelectMatchMode value={filterMatch} onChange={newValue => setFilterMatch(newValue)} />,
|
||||||
sourceMenu.hide();
|
[filterMatch, setFilterMatch]
|
||||||
setFilterSource(newValue);
|
|
||||||
},
|
|
||||||
[sourceMenu, setFilterSource]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -107,69 +69,8 @@ function ConstituentsSearch({ schema, activeID, activeExpression, setFiltered }:
|
||||||
value={filterText}
|
value={filterText}
|
||||||
onChange={setFilterText}
|
onChange={setFilterText}
|
||||||
/>
|
/>
|
||||||
|
{selectMatchMode}
|
||||||
<div ref={matchModeMenu.ref}>
|
{selectGraph}
|
||||||
<SelectorButton
|
|
||||||
transparent
|
|
||||||
tabIndex={-1}
|
|
||||||
title='Настройка атрибутов для фильтрации'
|
|
||||||
hideTitle={matchModeMenu.isOpen}
|
|
||||||
className='h-full'
|
|
||||||
icon={<IconFilter size='1.25rem' />}
|
|
||||||
text={labelCstMatchMode(filterMatch)}
|
|
||||||
onClick={matchModeMenu.toggle}
|
|
||||||
/>
|
|
||||||
<Dropdown stretchLeft isOpen={matchModeMenu.isOpen}>
|
|
||||||
{Object.values(CstMatchMode)
|
|
||||||
.filter(value => !isNaN(Number(value)))
|
|
||||||
.map((value, index) => {
|
|
||||||
const matchMode = value as CstMatchMode;
|
|
||||||
return (
|
|
||||||
<DropdownButton
|
|
||||||
className='w-[22rem]'
|
|
||||||
key={`${prefixes.cst_match_mode_list}${index}`}
|
|
||||||
onClick={() => handleMatchModeChange(matchMode)}
|
|
||||||
>
|
|
||||||
<p>
|
|
||||||
<b>{labelCstMatchMode(matchMode)}:</b> {describeCstMatchMode(matchMode)}
|
|
||||||
</p>
|
|
||||||
</DropdownButton>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Dropdown>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div ref={sourceMenu.ref}>
|
|
||||||
<SelectorButton
|
|
||||||
transparent
|
|
||||||
tabIndex={-1}
|
|
||||||
title='Настройка фильтрации по графу термов'
|
|
||||||
hideTitle={sourceMenu.isOpen}
|
|
||||||
className='h-full pr-2'
|
|
||||||
icon={DependencyIcon(filterSource, '1.25rem')}
|
|
||||||
text={labelCstSource(filterSource)}
|
|
||||||
onClick={sourceMenu.toggle}
|
|
||||||
/>
|
|
||||||
<Dropdown stretchLeft isOpen={sourceMenu.isOpen}>
|
|
||||||
{Object.values(DependencyMode)
|
|
||||||
.filter(value => !isNaN(Number(value)))
|
|
||||||
.map((value, index) => {
|
|
||||||
const source = value as DependencyMode;
|
|
||||||
return (
|
|
||||||
<DropdownButton
|
|
||||||
className='w-[18rem]'
|
|
||||||
key={`${prefixes.cst_source_list}${index}`}
|
|
||||||
onClick={() => handleSourceChange(source)}
|
|
||||||
>
|
|
||||||
<div className='inline-flex items-center gap-1'>
|
|
||||||
{DependencyIcon(source, '1.25rem')}
|
|
||||||
<b>{labelCstSource(source)}:</b> {describeCstSource(source)}
|
|
||||||
</div>
|
|
||||||
</DropdownButton>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Dropdown>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ function UserTabs() {
|
||||||
onClick={() => setShowSubs(prev => !prev)}
|
onClick={() => setShowSubs(prev => !prev)}
|
||||||
/>
|
/>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
<h1 className='mb-4'>Учетные данные пользователя</h1>
|
<h1 className='mb-4 select-none'>Учетные данные пользователя</h1>
|
||||||
<div className='flex py-2'>
|
<div className='flex py-2'>
|
||||||
<EditorProfile />
|
<EditorProfile />
|
||||||
<EditorPassword />
|
<EditorPassword />
|
||||||
|
|
|
@ -36,8 +36,8 @@ function ViewSubscriptions({ items }: ViewSubscriptionsProps) {
|
||||||
id: 'title',
|
id: 'title',
|
||||||
header: 'Название',
|
header: 'Название',
|
||||||
minSize: 200,
|
minSize: 200,
|
||||||
size: 800,
|
size: 2000,
|
||||||
maxSize: 800,
|
maxSize: 2000,
|
||||||
enableSorting: true
|
enableSorting: true
|
||||||
}),
|
}),
|
||||||
columnHelper.accessor('time_update', {
|
columnHelper.accessor('time_update', {
|
||||||
|
@ -61,7 +61,7 @@ function ViewSubscriptions({ items }: ViewSubscriptionsProps) {
|
||||||
animate={{ ...animateSideView.animate }}
|
animate={{ ...animateSideView.animate }}
|
||||||
exit={{ ...animateSideView.exit }}
|
exit={{ ...animateSideView.exit }}
|
||||||
>
|
>
|
||||||
<h1 className='mb-6'>Отслеживаемые схемы</h1>
|
<h1 className='mb-6 select-none'>Отслеживаемые схемы</h1>
|
||||||
<DataTable
|
<DataTable
|
||||||
dense
|
dense
|
||||||
noFooter
|
noFooter
|
||||||
|
|
|
@ -222,11 +222,11 @@ export function labelCstMatchMode(mode: CstMatchMode): string {
|
||||||
export function describeCstMatchMode(mode: CstMatchMode): string {
|
export function describeCstMatchMode(mode: CstMatchMode): string {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case CstMatchMode.ALL: return 'искать во всех атрибутах';
|
case CstMatchMode.ALL: return 'все атрибуты';
|
||||||
case CstMatchMode.EXPR: return 'искать в формальных выражениях';
|
case CstMatchMode.EXPR: return 'формальное определение';
|
||||||
case CstMatchMode.TERM: return 'искать в терминах';
|
case CstMatchMode.TERM: return 'термин';
|
||||||
case CstMatchMode.TEXT: return 'искать в определениях и конвенциях';
|
case CstMatchMode.TEXT: return 'определение и конвенция';
|
||||||
case CstMatchMode.NAME: return 'искать в идентификаторах конституент';
|
case CstMatchMode.NAME: return 'только имена';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,7 +269,6 @@ export function labelLibraryFilter(strategy: LibraryFilterStrategy): string {
|
||||||
case LibraryFilterStrategy.MANUAL: return 'отображать все';
|
case LibraryFilterStrategy.MANUAL: return 'отображать все';
|
||||||
case LibraryFilterStrategy.COMMON: return 'общедоступные';
|
case LibraryFilterStrategy.COMMON: return 'общедоступные';
|
||||||
case LibraryFilterStrategy.CANONICAL: return 'неизменные';
|
case LibraryFilterStrategy.CANONICAL: return 'неизменные';
|
||||||
case LibraryFilterStrategy.PERSONAL: return 'личные';
|
|
||||||
case LibraryFilterStrategy.SUBSCRIBE: return 'подписки';
|
case LibraryFilterStrategy.SUBSCRIBE: return 'подписки';
|
||||||
case LibraryFilterStrategy.OWNED: return 'владелец';
|
case LibraryFilterStrategy.OWNED: return 'владелец';
|
||||||
}
|
}
|
||||||
|
@ -284,7 +283,6 @@ export function describeLibraryFilter(strategy: LibraryFilterStrategy): string {
|
||||||
case LibraryFilterStrategy.MANUAL: return 'Отображать все схемы';
|
case LibraryFilterStrategy.MANUAL: return 'Отображать все схемы';
|
||||||
case LibraryFilterStrategy.COMMON: return 'Отображать общедоступные схемы';
|
case LibraryFilterStrategy.COMMON: return 'Отображать общедоступные схемы';
|
||||||
case LibraryFilterStrategy.CANONICAL: return 'Отображать стандартные схемы';
|
case LibraryFilterStrategy.CANONICAL: return 'Отображать стандартные схемы';
|
||||||
case LibraryFilterStrategy.PERSONAL: return 'Отображать подписки и собственные схемы';
|
|
||||||
case LibraryFilterStrategy.SUBSCRIBE: return 'Отображать подписки';
|
case LibraryFilterStrategy.SUBSCRIBE: return 'Отображать подписки';
|
||||||
case LibraryFilterStrategy.OWNED: return 'Отображать собственные схемы';
|
case LibraryFilterStrategy.OWNED: return 'Отображать собственные схемы';
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user