xuquan-nikkkki_FolderFile-S.../src/components/Folder/index.tsx

173 lines
4.3 KiB
TypeScript

import { useDraggable, useDroppable } from "@dnd-kit/core";
import classNames from "classnames";
import { useEffect } from "react";
import { useShallow } from "zustand/react/shallow";
import { FFS_DRAG_FILE, FFS_DRAG_FOLDER } from "src/assets/constants";
import { useExplorer } from "src/hooks/useExplorer";
import { ExplorerStore } from "src/store";
import { pluralize } from "src/utils";
import FolderContent, { FolderProps } from "./Content";
import FolderExpandIcon from "./ExpandIcon";
type Props = FolderProps & {
onOpenFilesPane: () => void;
disableDrag?: boolean;
hideExpandIcon?: boolean;
disableHoverIndent?: boolean;
};
const Folder = ({
folder,
onOpenFilesPane,
hideExpandIcon = false,
disableDrag = false,
disableHoverIndent = false,
}: Props) => {
const { useExplorerStore, plugin } = useExplorer();
const { language } = plugin;
const {
setFocusedFolder,
expandFolder,
collapseFolder,
expandedFolderPaths,
focusedFolder,
hasFolderChildren,
getFilesinFolder,
getFoldersByParent,
changeViewMode,
} = useExplorerStore(
useShallow((store: ExplorerStore) => ({
setFocusedFolder: store.setFocusedFolder,
hasFolderChildren: store.hasFolderChildren,
expandedFolderPaths: store.expandedFolderPaths,
expandFolder: store.expandFolder,
collapseFolder: store.collapseFolder,
focusedFolder: store.focusedFolder,
getFilesinFolder: store.getFilesInFolder,
getFoldersByParent: store.getFoldersByParent,
changeViewMode: store.changeViewMode,
}))
);
const isFocused = folder.path == focusedFolder?.path;
const isRoot = folder.isRoot();
const {
setNodeRef: setDragRef,
attributes,
listeners,
isDragging,
} = useDraggable({
id: folder.path,
data: { type: FFS_DRAG_FOLDER, item: folder },
disabled: disableDrag,
});
const { setNodeRef: setDropRef, isOver } = useDroppable({
id: `drop-${folder.path}`,
data: {
accepts: [FFS_DRAG_FILE, FFS_DRAG_FOLDER],
item: folder,
},
disabled: disableDrag,
});
useEffect(() => {
if (!isOver) return;
const timer = setTimeout(() => {
if (!expandedFolderPaths.includes(folder.path)) {
expandFolder(folder);
}
}, 800);
return () => clearTimeout(timer);
}, [isOver]);
useEffect(() => {
window.addEventListener("dblclick", onOpenFilesPane);
return () => {
window.removeEventListener("dblclick", onOpenFilesPane);
};
}, [onOpenFilesPane]);
const onToggleExpandState = (): void => {
const isFolderExpanded = expandedFolderPaths.includes(folder.path);
if (isFolderExpanded) {
collapseFolder(folder);
} else {
expandFolder(folder);
}
};
const maybeRenderExpandIcon = () => {
if (isRoot || hideExpandIcon) return null;
return <FolderExpandIcon folder={folder} />;
};
const getAriaLabel = () => {
const { name } = folder;
const filesCount = getFilesinFolder(folder).length;
const foldersCount = getFoldersByParent(folder).length;
const filesCountInfo = pluralize(filesCount, "file");
const foldersCountInfo = pluralize(foldersCount, "folder");
if (language === "zh") {
return `${name}\n${filesCount} 条笔记,${foldersCount} 个文件夹`;
}
return `${name}\n${filesCountInfo}, ${foldersCountInfo}`;
};
const getIndentStyle = () => {
if (disableHoverIndent) return undefined;
const folderLevel = isRoot ? 0 : folder.path.split("/").length - 1;
return {
marginInlineStart: -17 * folderLevel,
paddingInlineStart: 24 + 17 * folderLevel,
};
};
const getClassNames = () => {
return classNames(
"ffs__folder-container tree-item-self nav-folder-title is-clickable",
{
"mod-collapsible": hasFolderChildren(folder),
"is-active": isFocused,
"ffs__is-over": isOver && !disableDrag,
}
);
};
return (
<div
className={getClassNames()}
ref={setDropRef}
onClick={async () => {
onToggleExpandState();
setFocusedFolder(folder);
await changeViewMode("folder");
}}
style={getIndentStyle()}
data-tooltip-position="right"
aria-label={getAriaLabel()}
>
{maybeRenderExpandIcon()}
<div
className="ffs__draggable-container tree-item-inner nav-folder-title-content"
style={{ opacity: isDragging ? 0.5 : 1 }}
ref={setDragRef}
{...attributes}
{...listeners}
>
<FolderContent
folder={folder}
onToggleExpandState={onToggleExpandState}
/>
</div>
</div>
);
};
export default Folder;