ConceptPortal-public/rsconcept/frontend/src/features/library/pages/library-page/toolbar-search.tsx

205 lines
7.7 KiB
TypeScript
Raw Normal View History

'use client';
2023-08-26 19:39:49 +03:00
import clsx from 'clsx';
2023-08-27 15:39:49 +03:00
2025-02-26 00:16:41 +03:00
import { SelectUser } from '@/features/users/components';
2025-02-12 21:36:25 +03:00
2025-03-12 12:04:50 +03:00
import { MiniButton, SelectorButton } from '@/components/control';
import { Dropdown, DropdownButton, useDropdown } from '@/components/dropdown';
2024-09-27 12:04:10 +03:00
import {
IconEditor,
IconFilterReset,
IconFolder,
IconFolderSearch,
IconFolderTree,
IconOwner,
IconUserSearch
2025-03-12 12:04:50 +03:00
} from '@/components/icons';
import { SearchBar } from '@/components/input';
import { cn } from '@/components/utils';
2024-06-21 11:17:50 +03:00
import { prefixes } from '@/utils/constants';
import { tripleToggleColor } from '@/utils/utils';
2023-08-26 19:39:49 +03:00
2025-06-11 11:01:00 +03:00
import { useLibrarySuspense } from '../../backend/use-library';
2025-03-12 11:55:43 +03:00
import { IconItemVisibility } from '../../components/icon-item-visibility';
import { IconLocationHead } from '../../components/icon-location-head';
2025-02-11 20:56:24 +03:00
import { describeLocationHead, labelLocationHead } from '../../labels';
import { LocationHead } from '../../models/library';
2025-03-12 11:55:43 +03:00
import { useHasCustomFilter, useLibrarySearchStore } from '../../stores/library-search';
interface ToolbarSearchProps {
2025-03-11 14:42:41 +03:00
className?: string;
2023-12-28 14:04:44 +03:00
total: number;
filtered: number;
2023-08-26 19:39:49 +03:00
}
2025-03-11 14:42:41 +03:00
export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps) {
2025-06-11 11:01:00 +03:00
const { items } = useLibrarySuspense();
2024-09-27 12:04:10 +03:00
const userMenu = useDropdown();
const headMenu = useDropdown();
2024-09-27 12:04:10 +03:00
const query = useLibrarySearchStore(state => state.query);
const setQuery = useLibrarySearchStore(state => state.setQuery);
const path = useLibrarySearchStore(state => state.path);
const setPath = useLibrarySearchStore(state => state.setPath);
const head = useLibrarySearchStore(state => state.head);
const setHead = useLibrarySearchStore(state => state.setHead);
const folderMode = useLibrarySearchStore(state => state.folderMode);
const toggleFolderMode = useLibrarySearchStore(state => state.toggleFolderMode);
const isOwned = useLibrarySearchStore(state => state.isOwned);
const toggleOwned = useLibrarySearchStore(state => state.toggleOwned);
const isEditor = useLibrarySearchStore(state => state.isEditor);
const toggleEditor = useLibrarySearchStore(state => state.toggleEditor);
const isVisible = useLibrarySearchStore(state => state.isVisible);
const toggleVisible = useLibrarySearchStore(state => state.toggleVisible);
const filterUser = useLibrarySearchStore(state => state.filterUser);
const setFilterUser = useLibrarySearchStore(state => state.setFilterUser);
const resetFilter = useLibrarySearchStore(state => state.resetFilter);
const hasCustomFilter = useHasCustomFilter();
const userActive = isOwned !== null || isEditor !== null || filterUser !== null;
2023-08-27 15:39:49 +03:00
2025-06-11 11:01:00 +03:00
function filterNonEmptyUsers(userID: number): boolean {
return items.some(item => item.owner === userID);
}
function handleChange(newValue: LocationHead | null) {
headMenu.hide();
setHead(newValue);
}
2024-05-02 17:04:18 +03:00
function handleToggleFolder() {
2024-06-19 22:10:15 +03:00
headMenu.hide();
toggleFolderMode();
}
2025-02-22 14:04:01 +03:00
function handleFolderClick(event: React.MouseEvent<Element>) {
if (event.ctrlKey || event.metaKey) {
toggleFolderMode();
} else {
headMenu.toggle();
}
}
2024-06-19 22:10:15 +03:00
2023-08-26 19:39:49 +03:00
return (
<div className={cn('flex gap-3 border-b text-sm bg-input items-center', className)}>
2025-03-10 16:02:53 +03:00
<div className='ml-3 min-w-18 sm:min-w-30 select-none whitespace-nowrap'>
2024-06-05 19:42:48 +03:00
{filtered} из {total}
2023-12-28 14:04:44 +03:00
</div>
2025-03-10 16:02:53 +03:00
<div className='cc-icons h-full items-center'>
2024-09-27 12:04:10 +03:00
<MiniButton
title='Видимость'
2025-02-26 00:16:41 +03:00
icon={<IconItemVisibility value={true} className={tripleToggleColor(isVisible)} />}
2024-09-27 12:04:10 +03:00
onClick={toggleVisible}
/>
2024-06-18 15:07:41 +03:00
<div ref={userMenu.ref} onBlur={userMenu.handleBlur} className='relative flex'>
2024-06-18 15:07:41 +03:00
<MiniButton
2024-09-27 12:04:10 +03:00
title='Поиск пользователя'
hideTitle={userMenu.isOpen}
icon={<IconUserSearch size='1.25rem' className={userActive ? 'icon-green' : 'icon-primary'} />}
onClick={userMenu.toggle}
2024-06-18 15:07:41 +03:00
/>
2025-03-10 16:02:53 +03:00
<Dropdown isOpen={userMenu.isOpen}>
2024-09-27 12:04:10 +03:00
<DropdownButton
text='Я - Владелец'
2025-03-20 11:33:42 +03:00
title='Фильтровать схемы, в которых текущий пользователь является владельцем'
2024-09-27 12:04:10 +03:00
icon={<IconOwner size='1.25rem' className={tripleToggleColor(isOwned)} />}
onClick={toggleOwned}
/>
<DropdownButton
text='Я - Редактор'
2025-03-20 11:33:42 +03:00
title='Фильтровать схемы, в которых текущий пользователя является редактором'
2024-09-27 12:04:10 +03:00
icon={<IconEditor size='1.25rem' className={tripleToggleColor(isEditor)} />}
onClick={toggleEditor}
/>
<SelectUser
2025-03-20 11:33:42 +03:00
aria-label='Выбор пользователя для фильтра по владельцу'
placeholder='Выберите владельца'
2025-03-20 11:33:42 +03:00
noBorder
2025-04-11 20:00:42 +03:00
className='min-w-60 mx-1 mb-1'
2025-06-11 11:01:00 +03:00
filter={filterNonEmptyUsers}
value={filterUser}
onChange={setFilterUser}
/>
2024-09-27 12:04:10 +03:00
</Dropdown>
2024-06-14 21:43:37 +03:00
</div>
2024-09-27 12:04:10 +03:00
<MiniButton
title='Сбросить фильтры'
icon={<IconFilterReset size='1.25rem' className='icon-primary' />}
onClick={resetFilter}
disabled={!hasCustomFilter}
/>
</div>
2025-04-29 21:42:28 +03:00
<div className='flex h-full grow pr-4 sm:pr-12'>
<SearchBar
id='library_search'
2024-06-05 19:42:48 +03:00
placeholder='Поиск'
noBorder
2025-03-09 21:59:21 +03:00
className={clsx('min-w-28 sm:min-w-40 max-w-80', folderMode && 'grow')}
query={query}
onChangeQuery={setQuery}
/>
2024-06-19 22:10:15 +03:00
{!folderMode ? (
<div
ref={headMenu.ref}
onBlur={headMenu.handleBlur}
className='relative flex items-center h-full select-none'
>
2024-06-19 22:10:15 +03:00
<SelectorButton
2025-03-10 16:02:53 +03:00
className='rounded-lg py-1'
2025-03-19 23:28:52 +03:00
titleHtml={
(head ? describeLocationHead(head) : 'Выберите каталог') + '<br/><kbd>Ctrl + клик</kbd> - Проводник'
}
2024-06-19 22:10:15 +03:00
hideTitle={headMenu.isOpen}
2025-06-19 17:50:02 +03:00
icon={head ? <IconLocationHead value={head} size='1.25rem' /> : <IconFolderSearch size='1.25rem' />}
2024-06-19 22:10:15 +03:00
onClick={handleFolderClick}
/>
2025-03-07 20:38:40 +03:00
<Dropdown isOpen={headMenu.isOpen} stretchLeft>
2025-03-09 21:59:21 +03:00
<DropdownButton
text='проводник...'
2025-03-20 11:33:42 +03:00
title='Переключение в режим Проводник'
2025-06-18 22:31:23 +03:00
icon={<IconFolderTree size='1rem' className='icon-primary' />}
2025-03-09 21:59:21 +03:00
onClick={handleToggleFolder}
/>
<DropdownButton
text='отображать все'
2025-03-20 11:33:42 +03:00
title='Очистить фильтр по расположению'
2025-06-18 22:31:23 +03:00
icon={<IconFolder size='1rem' className='icon-primary' />}
2025-03-09 21:59:21 +03:00
onClick={() => handleChange(null)}
/>
2024-06-19 22:10:15 +03:00
{Object.values(LocationHead).map((head, index) => {
return (
<DropdownButton
key={`${prefixes.location_head_list}${index}`}
2025-03-09 21:59:21 +03:00
text={labelLocationHead(head)}
2025-03-20 11:33:42 +03:00
title={describeLocationHead(head)}
onClick={() => handleChange(head)}
2025-03-09 21:59:21 +03:00
icon={<IconLocationHead value={head} size='1rem' />}
/>
2024-06-19 22:10:15 +03:00
);
})}
</Dropdown>
</div>
) : null}
{!folderMode ? (
<SearchBar
id='path_search'
placeholder='Путь'
noIcon
noBorder
2025-06-19 17:50:02 +03:00
className='w-18 sm:w-20 grow ml-1'
query={path}
onChangeQuery={setPath}
/>
2024-06-19 22:10:15 +03:00
) : null}
</div>
</div>
2023-12-28 14:04:44 +03:00
);
2023-08-26 19:39:49 +03:00
}