F: Improve folders UI
This commit is contained in:
parent
cd62ad574f
commit
161a51fc45
|
@ -5,8 +5,7 @@ import {
|
||||||
IconFolderEdit,
|
IconFolderEdit,
|
||||||
IconFolderEmpty,
|
IconFolderEmpty,
|
||||||
IconFolderOpened,
|
IconFolderOpened,
|
||||||
IconFolderSearch,
|
IconLeftClose,
|
||||||
IconFolderTree,
|
|
||||||
IconOSS,
|
IconOSS,
|
||||||
IconRSForm,
|
IconRSForm,
|
||||||
IconSearch,
|
IconSearch,
|
||||||
|
@ -45,28 +44,25 @@ export function HelpLibrary() {
|
||||||
<li>
|
<li>
|
||||||
<IconShow size='1rem' className='inline-icon' /> фильтры атрибутов применяются по клику
|
<IconShow size='1rem' className='inline-icon' /> фильтры атрибутов применяются по клику
|
||||||
</li>
|
</li>
|
||||||
<li>
|
|
||||||
<IconSortAsc size='1rem' className='inline-icon' />
|
|
||||||
<IconSortDesc size='1rem' className='inline-icon' /> сортировка по клику на заголовок таблицы
|
|
||||||
</li>
|
|
||||||
<li>
|
<li>
|
||||||
<IconUserSearch size='1rem' className='inline-icon' /> фильтр по пользователю
|
<IconUserSearch size='1rem' className='inline-icon' /> фильтр по пользователю
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<IconSearch size='1rem' className='inline-icon' /> фильтр по названию и шифру
|
<IconSearch size='1rem' className='inline-icon' /> фильтр по названию и шифру
|
||||||
</li>
|
</li>
|
||||||
<li>
|
|
||||||
<IconFolderSearch size='1rem' className='inline-icon' /> фильтр по расположению
|
|
||||||
</li>
|
|
||||||
<li>
|
<li>
|
||||||
<IconFilterReset size='1rem' className='inline-icon' /> сбросить фильтры
|
<IconFilterReset size='1rem' className='inline-icon' /> сбросить фильтры
|
||||||
</li>
|
</li>
|
||||||
<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>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>Режим: Проводник</h2>
|
<h2>Проводник</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<IconFolderEdit size='1rem' className='inline-icon' /> переименовать выбранную
|
<IconFolderEdit size='1rem' className='inline-icon' /> переименовать выбранную
|
||||||
|
|
|
@ -6,7 +6,7 @@ export function IconShowSidebar({
|
||||||
size = '1.25rem',
|
size = '1.25rem',
|
||||||
className,
|
className,
|
||||||
isBottom
|
isBottom
|
||||||
}: DomIconProps<boolean> & { isBottom: boolean }) {
|
}: DomIconProps<boolean> & { isBottom?: boolean }) {
|
||||||
if (isBottom) {
|
if (isBottom) {
|
||||||
if (value) {
|
if (value) {
|
||||||
return <IconBottomClose size={size} className={className ?? 'icon-primary'} />;
|
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 { 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 { Dropdown, DropdownButton, useDropdown } from '@/components/dropdown';
|
||||||
import {
|
import { IconEditor, IconFilterReset, IconOwner, IconUserSearch } from '@/components/icons';
|
||||||
IconEditor,
|
|
||||||
IconFilterReset,
|
|
||||||
IconFolder,
|
|
||||||
IconFolderSearch,
|
|
||||||
IconFolderTree,
|
|
||||||
IconOwner,
|
|
||||||
IconUserSearch
|
|
||||||
} from '@/components/icons';
|
|
||||||
import { SearchBar } from '@/components/input';
|
import { SearchBar } from '@/components/input';
|
||||||
import { cn } from '@/components/utils';
|
import { cn } from '@/components/utils';
|
||||||
import { prefixes } from '@/utils/constants';
|
|
||||||
import { tripleToggleColor } from '@/utils/utils';
|
import { tripleToggleColor } from '@/utils/utils';
|
||||||
|
|
||||||
import { useLibrarySuspense } from '../../backend/use-library';
|
import { useLibrarySuspense } from '../../backend/use-library';
|
||||||
import { IconItemVisibility } from '../../components/icon-item-visibility';
|
import { IconItemVisibility } from '../../components/icon-item-visibility';
|
||||||
import { IconLocationHead } from '../../components/icon-location-head';
|
import { IconShowSidebar } from '../../components/icon-show-sidebar';
|
||||||
import { describeLocationHead, labelLocationHead } from '../../labels';
|
|
||||||
import { LocationHead } from '../../models/library';
|
|
||||||
import { useHasCustomFilter, useLibrarySearchStore } from '../../stores/library-search';
|
import { useHasCustomFilter, useLibrarySearchStore } from '../../stores/library-search';
|
||||||
|
|
||||||
interface ToolbarSearchProps {
|
interface ToolbarSearchProps {
|
||||||
|
@ -36,14 +25,9 @@ interface ToolbarSearchProps {
|
||||||
export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps) {
|
export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps) {
|
||||||
const { items } = useLibrarySuspense();
|
const { items } = useLibrarySuspense();
|
||||||
const userMenu = useDropdown();
|
const userMenu = useDropdown();
|
||||||
const headMenu = useDropdown();
|
|
||||||
|
|
||||||
const query = useLibrarySearchStore(state => state.query);
|
const query = useLibrarySearchStore(state => state.query);
|
||||||
const setQuery = useLibrarySearchStore(state => state.setQuery);
|
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 folderMode = useLibrarySearchStore(state => state.folderMode);
|
||||||
const toggleFolderMode = useLibrarySearchStore(state => state.toggleFolderMode);
|
const toggleFolderMode = useLibrarySearchStore(state => state.toggleFolderMode);
|
||||||
const isOwned = useLibrarySearchStore(state => state.isOwned);
|
const isOwned = useLibrarySearchStore(state => state.isOwned);
|
||||||
|
@ -64,24 +48,6 @@ export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps
|
||||||
return items.some(item => item.owner === userID);
|
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 (
|
return (
|
||||||
<div className={cn('flex gap-3 border-b text-sm bg-input items-center', className)}>
|
<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'>
|
<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>
|
||||||
|
|
||||||
<div className='cc-icons h-full items-center'>
|
<div className='cc-icons h-full items-center'>
|
||||||
|
<MiniButton
|
||||||
|
title='Отображение проводника'
|
||||||
|
icon={<IconShowSidebar value={!folderMode} size='1.25rem' />}
|
||||||
|
onClick={toggleFolderMode}
|
||||||
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title='Видимость'
|
title='Видимость'
|
||||||
icon={<IconItemVisibility value={true} className={tripleToggleColor(isVisible)} />}
|
icon={<IconItemVisibility value={true} className={tripleToggleColor(isVisible)} />}
|
||||||
|
@ -119,7 +90,7 @@ export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps
|
||||||
aria-label='Выбор пользователя для фильтра по владельцу'
|
aria-label='Выбор пользователя для фильтра по владельцу'
|
||||||
placeholder='Выберите владельца'
|
placeholder='Выберите владельца'
|
||||||
noBorder
|
noBorder
|
||||||
className='min-w-60 mx-1 mb-1'
|
className='min-w-60 mx-1 mb-1 cc-hover-bg'
|
||||||
filter={filterNonEmptyUsers}
|
filter={filterNonEmptyUsers}
|
||||||
value={filterUser}
|
value={filterUser}
|
||||||
onChange={setFilterUser}
|
onChange={setFilterUser}
|
||||||
|
@ -144,60 +115,6 @@ export function ToolbarSearch({ className, total, filtered }: ToolbarSearchProps
|
||||||
query={query}
|
query={query}
|
||||||
onChangeQuery={setQuery}
|
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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,9 +2,7 @@ import { useIntl } from 'react-intl';
|
||||||
|
|
||||||
import { useLabelUser } from '@/features/users';
|
import { useLabelUser } from '@/features/users';
|
||||||
|
|
||||||
import { MiniButton } from '@/components/control';
|
|
||||||
import { createColumnHelper } from '@/components/data-table';
|
import { createColumnHelper } from '@/components/data-table';
|
||||||
import { IconFolderTree } from '@/components/icons';
|
|
||||||
import { useWindowSize } from '@/hooks/use-window-size';
|
import { useWindowSize } from '@/hooks/use-window-size';
|
||||||
import { type RO } from '@/utils/meta';
|
import { type RO } from '@/utils/meta';
|
||||||
|
|
||||||
|
@ -20,13 +18,6 @@ export function useLibraryColumns() {
|
||||||
|
|
||||||
const getUserLabel = useLabelUser();
|
const getUserLabel = useLabelUser();
|
||||||
const folderMode = useLibrarySearchStore(state => state.folderMode);
|
const folderMode = useLibrarySearchStore(state => state.folderMode);
|
||||||
const toggleFolderMode = useLibrarySearchStore(state => state.toggleFolderMode);
|
|
||||||
|
|
||||||
function handleToggleFolder(event: React.MouseEvent<Element>) {
|
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
toggleFolderMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
...(folderMode
|
...(folderMode
|
||||||
|
@ -34,16 +25,7 @@ export function useLibraryColumns() {
|
||||||
: [
|
: [
|
||||||
columnHelper.accessor('location', {
|
columnHelper.accessor('location', {
|
||||||
id: 'location',
|
id: 'location',
|
||||||
header: () => (
|
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' />}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
size: 50,
|
size: 50,
|
||||||
minSize: 50,
|
minSize: 50,
|
||||||
maxSize: 50,
|
maxSize: 50,
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { HelpTopic } from '@/features/help';
|
||||||
import { BadgeHelp } from '@/features/help/components/badge-help';
|
import { BadgeHelp } from '@/features/help/components/badge-help';
|
||||||
|
|
||||||
import { MiniButton } from '@/components/control';
|
import { MiniButton } from '@/components/control';
|
||||||
import { IconFolderEdit, IconFolderTree } from '@/components/icons';
|
import { IconFolderEdit } from '@/components/icons';
|
||||||
import { useFitHeight } from '@/stores/app-layout';
|
import { useFitHeight } from '@/stores/app-layout';
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
import { infoMsg } from '@/utils/labels';
|
import { infoMsg } from '@/utils/labels';
|
||||||
|
@ -28,7 +28,6 @@ export function ViewSideLocation({ isVisible, onRenameLocation }: ViewSideLocati
|
||||||
|
|
||||||
const location = useLibrarySearchStore(state => state.location);
|
const location = useLibrarySearchStore(state => state.location);
|
||||||
const setLocation = useLibrarySearchStore(state => state.setLocation);
|
const setLocation = useLibrarySearchStore(state => state.setLocation);
|
||||||
const toggleFolderMode = useLibrarySearchStore(state => state.toggleFolderMode);
|
|
||||||
const subfolders = useLibrarySearchStore(state => state.subfolders);
|
const subfolders = useLibrarySearchStore(state => state.subfolders);
|
||||||
const toggleSubfolders = useLibrarySearchStore(state => state.toggleSubfolders);
|
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'>
|
<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' />
|
<BadgeHelp topic={HelpTopic.UI_LIBRARY} contentClass='text-sm' offset={5} place='right-start' />
|
||||||
<div className='cc-icons'>
|
<div className='cc-icons'>
|
||||||
{canRename ? (
|
|
||||||
<MiniButton
|
<MiniButton
|
||||||
titleHtml='<b>Редактирование пути</b><br/>Перемещаются только Ваши схемы<br/>в указанной папке (и подпапках)'
|
titleHtml='<b>Редактирование пути</b><br/>Перемещаются только Ваши схемы<br/>в указанной папке (и подпапках)'
|
||||||
aria-label='Редактирование расположения'
|
aria-label='Редактирование расположения'
|
||||||
icon={<IconFolderEdit size='1.25rem' className='icon-primary' />}
|
icon={<IconFolderEdit size='1.25rem' className='icon-primary' />}
|
||||||
onClick={onRenameLocation}
|
onClick={onRenameLocation}
|
||||||
|
disabled={!canRename}
|
||||||
/>
|
/>
|
||||||
) : null}
|
|
||||||
{!!location ? (
|
|
||||||
<MiniButton
|
<MiniButton
|
||||||
title={subfolders ? 'Вложенные папки: Вкл' : 'Вложенные папки: Выкл'}
|
title={subfolders ? 'Вложенные папки: Вкл' : 'Вложенные папки: Выкл'}
|
||||||
aria-label='Переключатель отображения вложенных папок'
|
aria-label='Переключатель отображения вложенных папок'
|
||||||
icon={<IconShowSubfolders value={subfolders} />}
|
icon={<IconShowSubfolders value={subfolders} />}
|
||||||
onClick={toggleSubfolders}
|
onClick={toggleSubfolders}
|
||||||
/>
|
/>
|
||||||
) : null}
|
|
||||||
<MiniButton
|
|
||||||
title='Переключение в режим Таблица'
|
|
||||||
icon={<IconFolderTree size='1.25rem' className='text-primary' />}
|
|
||||||
onClick={toggleFolderMode}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<SelectLocation
|
<SelectLocation
|
||||||
|
|
Loading…
Reference in New Issue
Block a user