Compare commits

...

4 Commits

Author SHA1 Message Date
IRBorisov
d7ee1db6a9 Fix centering when list is hidden
Some checks are pending
Frontend CI / build (18.x) (push) Waiting to run
2024-06-20 14:35:09 +03:00
IRBorisov
7d41953a8d Fix folder switch 2024-06-20 11:50:26 +03:00
IRBorisov
fdbb05df70 Update icons and help 2024-06-20 11:28:22 +03:00
IRBorisov
142f3882bb Fix folder naming 2024-06-20 11:06:47 +03:00
9 changed files with 88 additions and 37 deletions

View File

@ -32,9 +32,10 @@ export { RiMenuUnfoldFill as IconMenuUnfold } from 'react-icons/ri';
export { LuMoon as IconDarkTheme } from 'react-icons/lu'; export { LuMoon as IconDarkTheme } from 'react-icons/lu';
export { LuSun as IconLightTheme } from 'react-icons/lu'; export { LuSun as IconLightTheme } from 'react-icons/lu';
export { LuFolderTree as IconFolderTree } from 'react-icons/lu'; export { LuFolderTree as IconFolderTree } from 'react-icons/lu';
export { FaRegFolder as IconFolder } from 'react-icons/fa6'; export { LuFolder as IconFolder } from 'react-icons/lu';
export { FaRegFolderOpen as IconFolderOpened } from 'react-icons/fa6'; export { LuFolderOpen as IconFolderOpened } from 'react-icons/lu';
export { FaRegFolderClosed as IconFolderClosed } from 'react-icons/fa6'; export { LuFolderClosed as IconFolderClosed } from 'react-icons/lu';
export { LuFolderDot as IconFolderEmpty } from 'react-icons/lu';
export { LuLightbulb as IconHelp } from 'react-icons/lu'; export { LuLightbulb as IconHelp } from 'react-icons/lu';
export { LuLightbulbOff as IconHelpOff } 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';

View File

@ -77,11 +77,12 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
const [cachedTemplates, setCachedTemplates] = useState<IRSForm[]>([]); const [cachedTemplates, setCachedTemplates] = useState<IRSForm[]>([]);
const folders = useMemo(() => { const folders = useMemo(() => {
const result = new FolderTree(items.map(item => item.location)); const result = new FolderTree();
result.addPath(LocationHead.USER, 0); result.addPath(LocationHead.USER, 0);
result.addPath(LocationHead.COMMON, 0); result.addPath(LocationHead.COMMON, 0);
result.addPath(LocationHead.LIBRARY, 0); result.addPath(LocationHead.LIBRARY, 0);
result.addPath(LocationHead.PROJECTS, 0); result.addPath(LocationHead.PROJECTS, 0);
items.forEach(item => result.addPath(item.location));
return result; return result;
}, [items]); }, [items]);

View File

@ -102,7 +102,9 @@ export class FolderTree {
private visitNode(target: FolderNode, result: FolderNode[]) { private visitNode(target: FolderNode, result: FolderNode[]) {
result.push(target); result.push(target);
target.children.forEach(child => this.visitNode(child, result)); [...target.children.keys()]
.sort((a, b) => a.localeCompare(b))
.forEach(key => this.visitNode(target.children.get(key)!, result));
} }
addPath(path: string, filesCount: number = 1): FolderNode { addPath(path: string, filesCount: number = 1): FolderNode {

View File

@ -4,12 +4,14 @@ import clsx from 'clsx';
import { AnimatePresence, motion } from 'framer-motion'; import { AnimatePresence, motion } from 'framer-motion';
import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
import { IconFolder, IconFolderClosed, IconFolderOpened, IconFolderTree } from '@/components/Icons'; import { IconFolder, IconFolderClosed, IconFolderEmpty, IconFolderOpened, IconFolderTree } from '@/components/Icons';
import BadgeHelp from '@/components/info/BadgeHelp';
import { CProps } from '@/components/props'; import { CProps } from '@/components/props';
import MiniButton from '@/components/ui/MiniButton'; import MiniButton from '@/components/ui/MiniButton';
import { FolderNode, FolderTree } from '@/models/FolderTree'; import { FolderNode, FolderTree } from '@/models/FolderTree';
import { HelpTopic } from '@/models/miscellaneous';
import { animateSideAppear, animateSideView } from '@/styling/animations'; import { animateSideAppear, animateSideView } from '@/styling/animations';
import { globals, prefixes } from '@/utils/constants'; import { globals, PARAMETER, prefixes } from '@/utils/constants';
import { describeFolderNode, labelFolderNode } from '@/utils/labels'; import { describeFolderNode, labelFolderNode } from '@/utils/labels';
interface LibraryTableProps { interface LibraryTableProps {
@ -72,10 +74,16 @@ function LibraryFolders({ folders, currentFolder, setFolder, toggleFolderMode }:
animate={{ ...animateSideView.animate }} animate={{ ...animateSideView.animate }}
exit={{ ...animateSideView.exit }} exit={{ ...animateSideView.exit }}
> >
<div className='h-[2.08rem] flex justify-end pr-1'> <div className='h-[2.08rem] flex justify-between items-center pr-1'>
<BadgeHelp
topic={HelpTopic.UI_LIBRARY}
className={clsx(PARAMETER.TOOLTIP_WIDTH, 'text-sm')}
offset={5}
place='right-start'
/>
<MiniButton <MiniButton
icon={<IconFolderTree size='1.25rem' className='icon-green' />} icon={<IconFolderTree size='1.25rem' className='icon-green' />}
title='Режим: проводник' title='Переключение в режим Поиск'
onClick={toggleFolderMode} onClick={toggleFolderMode}
/> />
</div> </div>
@ -114,7 +122,11 @@ function LibraryFolders({ folders, currentFolder, setFolder, toggleFolderMode }:
noHover noHover
icon={ icon={
folded.includes(item) ? ( folded.includes(item) ? (
item.filesInside ? (
<IconFolderClosed size='1rem' className='icon-primary' /> <IconFolderClosed size='1rem' className='icon-primary' />
) : (
<IconFolderEmpty size='1rem' className='icon-primary' />
)
) : ( ) : (
<IconFolderOpened size='1rem' className='icon-green' /> <IconFolderOpened size='1rem' className='icon-green' />
) )
@ -123,7 +135,11 @@ function LibraryFolders({ folders, currentFolder, setFolder, toggleFolderMode }:
/> />
) : ( ) : (
<div> <div>
<IconFolder size='1rem' /> {item.filesInside ? (
<IconFolder size='1rem' className='clr-text-default' />
) : (
<IconFolderEmpty size='1rem' className='clr-text-controls' />
)}
</div> </div>
)} )}
<div className='self-center'>{labelFolderNode(item)}</div> <div className='self-center'>{labelFolderNode(item)}</div>

View File

@ -5,7 +5,7 @@ import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
import { urls } from '@/app/urls'; import { urls } from '@/app/urls';
import { IconFolder } from '@/components/Icons'; import { IconFolderTree } from '@/components/Icons';
import BadgeLocation from '@/components/info/BadgeLocation'; import BadgeLocation from '@/components/info/BadgeLocation';
import { CProps } from '@/components/props'; import { CProps } from '@/components/props';
import DataTable, { createColumnHelper, IConditionalStyle, VisibilityState } from '@/components/ui/DataTable'; import DataTable, { createColumnHelper, IConditionalStyle, VisibilityState } from '@/components/ui/DataTable';
@ -56,11 +56,9 @@ function LibraryTable({ items, resetQuery, folderMode, toggleFolderMode }: Libra
const handleToggleFolder = useCallback( const handleToggleFolder = useCallback(
(event: CProps.EventMouse) => { (event: CProps.EventMouse) => {
if (event.ctrlKey) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
toggleFolderMode(); toggleFolderMode();
}
}, },
[toggleFolderMode] [toggleFolderMode]
); );
@ -74,12 +72,12 @@ function LibraryTable({ items, resetQuery, folderMode, toggleFolderMode }: Libra
id: 'location', id: 'location',
header: () => ( header: () => (
<MiniButton <MiniButton
noHover
noPadding noPadding
noHover
className='pl-2 max-h-[1rem] translate-y-[-0.125rem]' className='pl-2 max-h-[1rem] translate-y-[-0.125rem]'
onClick={handleToggleFolder} onClick={handleToggleFolder}
titleHtml='Ctrl + клик для переключения </br>в режим папок' titleHtml='Переключение в режим Проводник'
icon={<IconFolder size='1.25rem' className='clr-text-controls' />} icon={<IconFolderTree size='1.25rem' className='clr-text-controls' />}
/> />
), ),
size: 50, size: 50,

View File

@ -176,6 +176,12 @@ function SearchPanel({
/> />
<Dropdown isOpen={headMenu.isOpen} stretchLeft className='z-modalTooltip'> <Dropdown isOpen={headMenu.isOpen} stretchLeft className='z-modalTooltip'>
<DropdownButton className='w-[10rem]' title='Переключение в режим Проводник' onClick={handleToggleFolder}>
<div className='inline-flex items-center gap-3'>
<IconFolderTree size='1rem' className='clr-text-controls' />
<span>проводник...</span>
</div>
</DropdownButton>
<DropdownButton className='w-[10rem]' onClick={() => handleChange(undefined)}> <DropdownButton className='w-[10rem]' onClick={() => handleChange(undefined)}>
<div className='inline-flex items-center gap-3'> <div className='inline-flex items-center gap-3'>
<IconFolder size='1rem' className='clr-text-controls' /> <IconFolder size='1rem' className='clr-text-controls' />
@ -197,16 +203,6 @@ function SearchPanel({
</DropdownButton> </DropdownButton>
); );
})} })}
<DropdownButton
className='w-[10rem]'
title='переключение в режим выбора папок'
onClick={handleToggleFolder}
>
<div className='inline-flex items-center gap-3'>
<IconFolderTree size='1rem' className='clr-text-controls' />
<span>проводник...</span>
</div>
</DropdownButton>
</Dropdown> </Dropdown>
</div> </div>
) : null} ) : null}

View File

@ -1,4 +1,14 @@
import { IconFolder, IconSearch, IconShow, IconSortAsc, IconSortDesc } from '@/components/Icons'; import {
IconFolder,
IconFolderClosed,
IconFolderEmpty,
IconFolderOpened,
IconFolderTree,
IconSearch,
IconShow,
IconSortAsc,
IconSortDesc
} from '@/components/Icons';
function HelpLibrary() { function HelpLibrary() {
return ( return (
@ -21,6 +31,28 @@ function HelpLibrary() {
<li> <li>
<IconFolder size='1rem' className='inline-icon' /> фильтр по расположению <IconFolder size='1rem' className='inline-icon' /> фильтр по расположению
</li> </li>
<h2>Проводник</h2>
<li>клик по папке отображает справа файлы в ней</li>
<li>клик по иконке сворачивает/разворачивает вложенные</li>
<li>
<IconFolderTree size='1rem' className='inline-icon' /> переключение между Проводник и Поиск
</li>
<li>
<IconFolderEmpty size='1rem' className='inline-icon clr-text-default' /> папка без файлов
</li>
<li>
<IconFolderEmpty size='1rem' className='inline-icon' /> папка с вложенными без файлов
</li>
<li>
<IconFolder size='1rem' className='inline-icon' /> папка без вложенных
</li>
<li>
<IconFolderClosed size='1rem' className='inline-icon' /> папка с вложенными и файлами
</li>
<li>
<IconFolderOpened size='1rem' className='inline-icon icon-green' /> развернутая папка
</li>
</div> </div>
); );
} }

View File

@ -125,7 +125,7 @@ function FormConstituenta({
} }
return ( return (
<AnimateFade> <AnimateFade className='mx-0 md:mx-auto'>
<ControlsOverlay <ControlsOverlay
disabled={disabled} disabled={disabled}
modified={isModified} modified={isModified}

View File

@ -8,6 +8,7 @@ import { GraphLayout } from '@/components/ui/GraphUI';
import { FolderNode } from '@/models/FolderTree'; import { FolderNode } from '@/models/FolderTree';
import { GramData, Grammeme, ReferenceType } from '@/models/language'; import { GramData, Grammeme, ReferenceType } from '@/models/language';
import { AccessPolicy, LibraryItemType, LocationHead } from '@/models/library'; import { AccessPolicy, LibraryItemType, LocationHead } from '@/models/library';
import { validateLocation } from '@/models/libraryAPI';
import { CstMatchMode, DependencyMode, GraphColoring, GraphSizing, HelpTopic } from '@/models/miscellaneous'; import { CstMatchMode, DependencyMode, GraphColoring, GraphSizing, HelpTopic } from '@/models/miscellaneous';
import { CstClass, CstType, ExpressionStatus, IConstituenta, IRSForm } from '@/models/rsform'; import { CstClass, CstType, ExpressionStatus, IConstituenta, IRSForm } from '@/models/rsform';
import { import {
@ -271,10 +272,10 @@ export function describeCstSource(mode: DependencyMode): string {
export function labelLocationHead(head: LocationHead): string { export function labelLocationHead(head: LocationHead): string {
// prettier-ignore // prettier-ignore
switch (head) { switch (head) {
case LocationHead.USER: return 'личные (/U)'; case LocationHead.USER: return '/U : личные';
case LocationHead.COMMON: return 'общие (/S)'; case LocationHead.COMMON: return '/S : общие';
case LocationHead.LIBRARY: return 'примеры (/L)'; case LocationHead.LIBRARY: return '/L : примеры';
case LocationHead.PROJECTS: return 'проекты (/P)'; case LocationHead.PROJECTS: return '/P : проекты';
} }
} }
@ -825,7 +826,11 @@ export function describeAccessMode(mode: UserLevel): string {
* Retrieves label for {@link FolderNode}. * Retrieves label for {@link FolderNode}.
*/ */
export function labelFolderNode(node: FolderNode): string { export function labelFolderNode(node: FolderNode): string {
if (node.parent || !validateLocation('/' + node.text)) {
return node.text; return node.text;
} else {
return labelLocationHead(('/' + node.text) as LocationHead);
}
} }
/** /**