mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-08-14 12:50:37 +03:00
F: Improve folders UI
This commit is contained in:
parent
ad3a4159c0
commit
08dda1dbf7
|
@ -5,8 +5,7 @@ import {
|
|||
IconFolderEdit,
|
||||
IconFolderEmpty,
|
||||
IconFolderOpened,
|
||||
IconFolderSearch,
|
||||
IconFolderTree,
|
||||
IconLeftClose,
|
||||
IconOSS,
|
||||
IconRSForm,
|
||||
IconSearch,
|
||||
|
@ -45,28 +44,25 @@ export function HelpLibrary() {
|
|||
<li>
|
||||
<IconShow size='1rem' className='inline-icon' /> фильтры атрибутов применяются по клику
|
||||
</li>
|
||||
<li>
|
||||
<IconSortAsc size='1rem' className='inline-icon' />
|
||||
<IconSortDesc size='1rem' className='inline-icon' /> сортировка по клику на заголовок таблицы
|
||||
</li>
|
||||
<li>
|
||||
<IconUserSearch size='1rem' className='inline-icon' /> фильтр по пользователю
|
||||
</li>
|
||||
<li>
|
||||
<IconSearch size='1rem' className='inline-icon' /> фильтр по названию и шифру
|
||||
</li>
|
||||
<li>
|
||||
<IconFolderSearch size='1rem' className='inline-icon' /> фильтр по расположению
|
||||
</li>
|
||||
<li>
|
||||
<IconFilterReset size='1rem' className='inline-icon' /> сбросить фильтры
|
||||
</li>
|
||||
<li>
|
||||
<IconFolderTree size='1rem' className='inline-icon' /> переключение между Проводник и Таблица
|
||||
<IconLeftClose size='1rem' className='inline-icon' /> отображение Проводника
|
||||
</li>
|
||||
<li>
|
||||
<IconSortAsc size='1rem' className='inline-icon' />
|
||||
<IconSortDesc size='1rem' className='inline-icon' /> сортировка по клику на заголовок таблицы
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>Режим: Проводник</h2>
|
||||
<h2>Проводник</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<IconFolderEdit size='1rem' className='inline-icon' /> переименовать выбранную
|
||||
|
|
|
@ -6,7 +6,7 @@ export function IconShowSidebar({
|
|||
size = '1.25rem',
|
||||
className,
|
||||
isBottom
|
||||
}: DomIconProps<boolean> & { isBottom: boolean }) {
|
||||
}: DomIconProps<boolean> & { isBottom?: boolean }) {
|
||||
if (isBottom) {
|
||||
if (value) {
|
||||
return <IconBottomClose size={size} className={className ?? 'icon-primary'} />;
|
||||
|
|
|
@ -4,27 +4,16 @@ import clsx from 'clsx';
|
|||
|
||||
import { SelectUser } from '@/features/users/components/select-user';
|
||||
|
||||
import { MiniButton, SelectorButton } from '@/components/control';
|
||||
import { MiniButton } from '@/components/control';
|
||||
import { Dropdown, DropdownButton, useDropdown } from '@/components/dropdown';
|
||||
import {
|
||||
IconEditor,
|
||||
IconFilterReset,
|
||||
IconFolder,
|
||||
IconFolderSearch,
|
||||
IconFolderTree,
|
||||
IconOwner,
|
||||
IconUserSearch
|
||||
} from '@/components/icons';
|
||||
import { IconEditor, IconFilterReset, IconOwner, IconUserSearch } from '@/components/icons';
|
||||
import { SearchBar } from '@/components/input';
|
||||
import { cn } from '@/components/utils';
|
||||
import { prefixes } from '@/utils/constants';
|
||||
import { tripleToggleColor } from '@/utils/utils';
|
||||
|
||||
import { useLibrarySuspense } from '../../backend/use-library';
|
||||
import { IconItemVisibility } from '../../components/icon-item-visibility';
|
||||
import { IconLocationHead } from '../../components/icon-location-head';
|
||||
import { describeLocationHead, labelLocationHead } from '../../labels';
|
||||
import { LocationHead } from '../../models/library';
|
||||
import { IconShowSidebar } from '../../components/icon-show-sidebar';
|
||||
import { useHasCustomFilter, useLibrarySearchStore } from '../../stores/library-search';
|
||||
|
||||
interface ToolbarSearchProps {
|
||||
|
@ -36,14 +25,9 @@ interface ToolbarSearchProps {
|
|||
export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps) {
|
||||
const { items } = useLibrarySuspense();
|
||||
const userMenu = useDropdown();
|
||||
const headMenu = useDropdown();
|
||||
|
||||
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);
|
||||
|
@ -64,24 +48,6 @@ export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps
|
|||
return items.some(item => item.owner === userID);
|
||||
}
|
||||
|
||||
function handleChange(newValue: LocationHead | null) {
|
||||
headMenu.hide();
|
||||
setHead(newValue);
|
||||
}
|
||||
|
||||
function handleToggleFolder() {
|
||||
headMenu.hide();
|
||||
toggleFolderMode();
|
||||
}
|
||||
|
||||
function handleFolderClick(event: React.MouseEvent<Element>) {
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
toggleFolderMode();
|
||||
} else {
|
||||
headMenu.toggle();
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn('flex gap-3 border-b text-sm bg-input items-center', className)}>
|
||||
<div className='ml-3 min-w-18 sm:min-w-30 select-none whitespace-nowrap'>
|
||||
|
@ -89,6 +55,11 @@ export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps
|
|||
</div>
|
||||
|
||||
<div className='cc-icons h-full items-center'>
|
||||
<MiniButton
|
||||
title='Отображение проводника'
|
||||
icon={<IconShowSidebar value={!folderMode} size='1.25rem' />}
|
||||
onClick={toggleFolderMode}
|
||||
/>
|
||||
<MiniButton
|
||||
title='Видимость'
|
||||
icon={<IconItemVisibility value={true} className={tripleToggleColor(isVisible)} />}
|
||||
|
@ -119,7 +90,7 @@ export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps
|
|||
aria-label='Выбор пользователя для фильтра по владельцу'
|
||||
placeholder='Выберите владельца'
|
||||
noBorder
|
||||
className='min-w-60 mx-1 mb-1'
|
||||
className='min-w-60 mx-1 mb-1 cc-hover-bg'
|
||||
filter={filterNonEmptyUsers}
|
||||
value={filterUser}
|
||||
onChange={setFilterUser}
|
||||
|
@ -144,60 +115,6 @@ export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps
|
|||
query={query}
|
||||
onChangeQuery={setQuery}
|
||||
/>
|
||||
{!folderMode ? (
|
||||
<div
|
||||
ref={headMenu.ref}
|
||||
onBlur={headMenu.handleBlur}
|
||||
className='relative flex items-center h-full select-none'
|
||||
>
|
||||
<SelectorButton
|
||||
className='rounded-lg py-1'
|
||||
titleHtml={
|
||||
(head ? describeLocationHead(head) : 'Выберите каталог') + '<br/><kbd>Ctrl + клик</kbd> - Проводник'
|
||||
}
|
||||
hideTitle={headMenu.isOpen}
|
||||
icon={head ? <IconLocationHead value={head} size='1.25rem' /> : <IconFolderSearch size='1.25rem' />}
|
||||
onClick={handleFolderClick}
|
||||
/>
|
||||
|
||||
<Dropdown isOpen={headMenu.isOpen} stretchLeft>
|
||||
<DropdownButton
|
||||
text='проводник...'
|
||||
title='Переключение в режим Проводник'
|
||||
icon={<IconFolderTree size='1rem' className='icon-primary' />}
|
||||
onClick={handleToggleFolder}
|
||||
/>
|
||||
<DropdownButton
|
||||
text='отображать все'
|
||||
title='Очистить фильтр по расположению'
|
||||
icon={<IconFolder size='1rem' className='icon-primary' />}
|
||||
onClick={() => handleChange(null)}
|
||||
/>
|
||||
{Object.values(LocationHead).map((head, index) => {
|
||||
return (
|
||||
<DropdownButton
|
||||
key={`${prefixes.location_head_list}${index}`}
|
||||
text={labelLocationHead(head)}
|
||||
title={describeLocationHead(head)}
|
||||
onClick={() => handleChange(head)}
|
||||
icon={<IconLocationHead value={head} size='1rem' />}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Dropdown>
|
||||
</div>
|
||||
) : null}
|
||||
{!folderMode ? (
|
||||
<SearchBar
|
||||
id='path_search'
|
||||
placeholder='Путь'
|
||||
noIcon
|
||||
noBorder
|
||||
className='w-18 sm:w-20 grow ml-1'
|
||||
query={path}
|
||||
onChangeQuery={setPath}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -2,9 +2,7 @@ import { useIntl } from 'react-intl';
|
|||
|
||||
import { useLabelUser } from '@/features/users';
|
||||
|
||||
import { MiniButton } from '@/components/control';
|
||||
import { createColumnHelper } from '@/components/data-table';
|
||||
import { IconFolderTree } from '@/components/icons';
|
||||
import { useWindowSize } from '@/hooks/use-window-size';
|
||||
import { type RO } from '@/utils/meta';
|
||||
|
||||
|
@ -20,13 +18,6 @@ export function useLibraryColumns() {
|
|||
|
||||
const getUserLabel = useLabelUser();
|
||||
const folderMode = useLibrarySearchStore(state => state.folderMode);
|
||||
const toggleFolderMode = useLibrarySearchStore(state => state.toggleFolderMode);
|
||||
|
||||
function handleToggleFolder(event: React.MouseEvent<Element>) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
toggleFolderMode();
|
||||
}
|
||||
|
||||
return [
|
||||
...(folderMode
|
||||
|
@ -34,16 +25,7 @@ export function useLibraryColumns() {
|
|||
: [
|
||||
columnHelper.accessor('location', {
|
||||
id: 'location',
|
||||
header: () => (
|
||||
<MiniButton
|
||||
titleHtml='Переключение в режим Проводник'
|
||||
aria-label='Переключатель режима Проводник'
|
||||
noPadding
|
||||
className='ml-2 max-h-4 -translate-y-0.5'
|
||||
onClick={handleToggleFolder}
|
||||
icon={<IconFolderTree size='1.25rem' className='text-primary' />}
|
||||
/>
|
||||
),
|
||||
header: 'Путь',
|
||||
size: 50,
|
||||
minSize: 50,
|
||||
maxSize: 50,
|
||||
|
|
|
@ -6,7 +6,7 @@ import { HelpTopic } from '@/features/help';
|
|||
import { BadgeHelp } from '@/features/help/components/badge-help';
|
||||
|
||||
import { MiniButton } from '@/components/control';
|
||||
import { IconFolderEdit, IconFolderTree } from '@/components/icons';
|
||||
import { IconFolderEdit } from '@/components/icons';
|
||||
import { useFitHeight } from '@/stores/app-layout';
|
||||
import { prefixes } from '@/utils/constants';
|
||||
import { infoMsg } from '@/utils/labels';
|
||||
|
@ -28,7 +28,6 @@ export function ViewSideLocation({ isVisible, onRenameLocation }: ViewSideLocati
|
|||
|
||||
const location = useLibrarySearchStore(state => state.location);
|
||||
const setLocation = useLibrarySearchStore(state => state.setLocation);
|
||||
const toggleFolderMode = useLibrarySearchStore(state => state.toggleFolderMode);
|
||||
const subfolders = useLibrarySearchStore(state => state.subfolders);
|
||||
const toggleSubfolders = useLibrarySearchStore(state => state.toggleSubfolders);
|
||||
|
||||
|
@ -69,27 +68,19 @@ export function ViewSideLocation({ isVisible, onRenameLocation }: ViewSideLocati
|
|||
<div className='h-8 flex justify-between items-center pr-1 pl-0.5'>
|
||||
<BadgeHelp topic={HelpTopic.UI_LIBRARY} contentClass='text-sm' offset={5} place='right-start' />
|
||||
<div className='cc-icons'>
|
||||
{canRename ? (
|
||||
<MiniButton
|
||||
titleHtml='<b>Редактирование пути</b><br/>Перемещаются только Ваши схемы<br/>в указанной папке (и подпапках)'
|
||||
aria-label='Редактирование расположения'
|
||||
icon={<IconFolderEdit size='1.25rem' className='icon-primary' />}
|
||||
onClick={onRenameLocation}
|
||||
disabled={!canRename}
|
||||
/>
|
||||
) : null}
|
||||
{!!location ? (
|
||||
<MiniButton
|
||||
title={subfolders ? 'Вложенные папки: Вкл' : 'Вложенные папки: Выкл'}
|
||||
aria-label='Переключатель отображения вложенных папок'
|
||||
icon={<IconShowSubfolders value={subfolders} />}
|
||||
onClick={toggleSubfolders}
|
||||
/>
|
||||
) : null}
|
||||
<MiniButton
|
||||
title='Переключение в режим Таблица'
|
||||
icon={<IconFolderTree size='1.25rem' className='text-primary' />}
|
||||
onClick={toggleFolderMode}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<SelectLocation
|
||||
|
|
Loading…
Reference in New Issue
Block a user