Portal/rsconcept/frontend/src/features/library/components/select-location.tsx

105 lines
3.4 KiB
TypeScript
Raw Normal View History

2024-06-21 19:27:51 +03:00
'use client';
import { useEffect, useState } from 'react';
2025-02-12 21:36:03 +03:00
import clsx from 'clsx';
2024-06-21 19:27:51 +03:00
2025-03-12 12:04:23 +03:00
import { MiniButton } from '@/components/control';
import { IconFolder, IconFolderClosed, IconFolderEmpty, IconFolderOpened } from '@/components/icons';
2025-02-22 14:03:13 +03:00
import { type Styling } from '@/components/props';
2024-06-21 19:27:51 +03:00
2025-03-12 11:54:32 +03:00
import { useFolders } from '../backend/use-folders';
2025-02-11 20:56:11 +03:00
import { labelFolderNode } from '../labels';
2025-03-12 11:54:32 +03:00
import { type FolderNode } from '../models/folder-tree';
2025-02-20 20:22:05 +03:00
interface SelectLocationProps extends Styling {
2024-06-21 19:27:51 +03:00
value: string;
2025-02-22 14:03:13 +03:00
onClick: (event: React.MouseEvent<Element>, target: FolderNode) => void;
2024-06-26 18:59:49 +03:00
prefix: string;
dense?: boolean;
2024-06-21 19:27:51 +03:00
}
2025-02-19 23:29:45 +03:00
export function SelectLocation({ value, dense, prefix, onClick, className, style }: SelectLocationProps) {
2025-01-23 19:41:31 +03:00
const { folders } = useFolders();
const activeNode = folders.at(value);
const items = folders.getTree();
2024-06-26 18:59:49 +03:00
const [folded, setFolded] = useState<FolderNode[]>(items);
useEffect(() => {
2024-08-06 14:38:10 +03:00
setFolded(items.filter(item => item !== activeNode && !activeNode?.hasPredecessor(item)));
2024-06-26 18:59:49 +03:00
}, [items, activeNode]);
function onFoldItem(target: FolderNode, showChildren: boolean) {
setFolded(prev =>
items.filter(item => {
if (item === target) {
return !showChildren;
}
if (!showChildren && item.hasPredecessor(target)) {
return true;
} else {
return prev.includes(item);
}
})
);
}
2024-06-21 19:27:51 +03:00
2025-02-22 14:03:13 +03:00
function handleClickFold(event: React.MouseEvent<Element>, target: FolderNode, showChildren: boolean) {
event.preventDefault();
event.stopPropagation();
onFoldItem(target, showChildren);
}
2024-06-21 19:27:51 +03:00
return (
2025-03-10 16:01:40 +03:00
<div className={clsx('flex flex-col cc-scroll-y', className)} style={style}>
2024-06-26 18:59:49 +03:00
{items.map((item, index) =>
!item.parent || !folded.includes(item.parent) ? (
<div
tabIndex={-1}
key={`${prefix}${index}`}
className={clsx(
2025-03-10 16:01:40 +03:00
!dense && 'h-7 sm:h-8',
2024-06-26 18:59:49 +03:00
'pr-3 py-1 flex items-center gap-2',
'cc-scroll-row',
2024-12-17 11:37:42 +03:00
'clr-hover cc-animate-color',
2024-06-26 18:59:49 +03:00
'cursor-pointer',
'leading-3 sm:leading-4',
activeNode === item && 'clr-selected'
)}
style={{ paddingLeft: `${(item.rank > 5 ? 5 : item.rank) * 0.5 + 0.5}rem` }}
onClick={event => onClick(event, item)}
>
{item.children.size > 0 ? (
<MiniButton
noPadding
noHover
icon={
folded.includes(item) ? (
item.filesInside ? (
<IconFolderClosed size='1rem' className='icon-primary' />
) : (
<IconFolderEmpty size='1rem' className='icon-primary' />
)
) : (
<IconFolderOpened size='1rem' className='icon-green' />
)
}
onClick={event => handleClickFold(event, item, folded.includes(item))}
/>
) : (
<div>
{item.filesInside ? (
<IconFolder size='1rem' className='clr-text-default' />
) : (
<IconFolderEmpty size='1rem' className='clr-text-controls' />
)}
</div>
)}
<div className='self-center text-start'>{labelFolderNode(item)}</div>
</div>
) : null
)}
2024-06-21 19:27:51 +03:00
</div>
);
}