Skip to content

Commit

Permalink
feat: Basic file selection feature & file icons
Browse files Browse the repository at this point in the history
  • Loading branch information
NriotHrreion committed Jul 25, 2024
1 parent f3a3cf6 commit 3352553
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 16 deletions.
107 changes: 94 additions & 13 deletions components/explorer/explorer-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,32 @@

import type { DirectoryItem } from "@/types";

import React, { ReactNode, useMemo } from "react";
import React, { useState, useMemo, useEffect } from "react";
import { Divider } from "@nextui-org/divider";
import { Checkbox } from "@nextui-org/checkbox";
import { File } from "lucide-react";
import {
Folder,
FolderGit2,
FolderDot,
FolderHeart,
Music,
Film,
File,
FileArchive,
FileText,
FilePieChart,
FileSpreadsheet,
FileType2,
FileImage,
Images,
Film
FileVideo,
FileAudio
} from "lucide-react";

import { useExplorer } from "@/hooks/useExplorer";
import { formatSize, getFileTypeName } from "@/lib/utils";

export function getIcon(folderName: string, size: number = 18, color?: string): React.ReactNode {
export function getFolderIcon(folderName: string, size: number = 18, color?: string): React.ReactNode {
const folderNameLowered = folderName.toLowerCase();

if(folderNameLowered === ".git") return <FolderGit2 size={size} color={color}/>;
Expand All @@ -32,35 +40,108 @@ export function getIcon(folderName: string, size: number = 18, color?: string):
return <Folder size={size} color={color}/>;
}

export function getFileIcon(extname: string, size: number = 18, color?: string): React.ReactNode {
const extnameLowered = extname.toLowerCase();
const className = "min-w-[20px]";

if(
extnameLowered === "zip" ||
extnameLowered === "rar" ||
extnameLowered === "tar" ||
extnameLowered === "7z" ||
extnameLowered === "gz" ||
extnameLowered === "bz2" ||
extnameLowered === "xz"
) return <FileArchive size={size} color={color} className={className}/>
if(
extnameLowered === "mp3" ||
extnameLowered === "wav" ||
extnameLowered === "ogg" ||
extnameLowered === "flac"
) return <FileAudio size={size} color={color} className={className}/>
if(
extnameLowered === "jpg" ||
extnameLowered === "jpeg" ||
extnameLowered === "png" ||
extnameLowered === "gif" ||
extnameLowered === "bmp" ||
extnameLowered === "webp" ||
extnameLowered === "svg" ||
extnameLowered === "ico"
) return <FileImage size={size} color={color} className={className}/>
if(
extnameLowered == "mp4" ||
extnameLowered == "mkv" ||
extnameLowered == "avi" ||
extnameLowered == "mov" ||
extnameLowered == "mpg" ||
extnameLowered == "mpeg" ||
extnameLowered == "wmv" ||
extnameLowered == "webm" ||
extnameLowered == "flv"
) return <FileVideo size={size} color={color} className={className}/>
if(extnameLowered === "doc" || extnameLowered === "docx") return <FileText size={size} color={color} className={className}/>
if(extnameLowered === "ppt" || extnameLowered === "pptx") return <FilePieChart size={size} color={color} className={className}/>
if(extnameLowered === "xls" || extnameLowered === "xlsx") return <FileSpreadsheet size={size} color={color} className={className}/>
if(
extnameLowered === "ttf" ||
extnameLowered === "otf" ||
extnameLowered === "woff" ||
extnameLowered === "woff2"
) return <FileType2 size={size} color={color} className={className}/>

return <File size={size} color={color} className={className}/>;
}

interface ExplorerItemProps extends DirectoryItem {}

const ExplorerItem: React.FC<ExplorerItemProps> = (props) => {
const extname = props.name.split(".").findLast(() => true);
const extname = useMemo(() => props.name.split(".").findLast(() => true), [props.name]);
const size = useMemo(() => formatSize(props.size), [props.size]);

const [selected, setSelected] = useState<boolean>(false);

const explorer = useExplorer();

const handleClick = () => {
const handleClick = (e: React.MouseEvent) => {
setSelected(false);
explorer.enterPath(props.name);

// To prevent the click event of the parent element
// which will select the checkbox of the item
e.stopPropagation();
};

useEffect(() => {
setSelected(false);
}, [explorer.path]);

useEffect(() => {
document.body.addEventListener("keydown", (e) => {
if(e.key === "a" && e.ctrlKey) {
e.preventDefault();
setSelected(true);
}
});
}, []);

return (
<div className="w-full min-h-8 text-md flex items-center gap-4">
<div className="w-full min-h-8 text-md flex items-center gap-4" onClick={() => setSelected((s) => !s)}>

Check warning on line 129 in components/explorer/explorer-item.tsx

View workflow job for this annotation

GitHub Actions / eslint (20.x)

Visible, non-interactive elements with click handlers must have at least one keyboard listener

Check failure on line 129 in components/explorer/explorer-item.tsx

View workflow job for this annotation

GitHub Actions / eslint (20.x)

Avoid non-native interactive elements. If using native HTML is not possible, add an appropriate role and support for tabbing, mouse, keyboard, and touch inputs to an interactive content element
<div className="w-[2%] flex items-center">
<Checkbox
className=""
size="sm"/>
size="sm"
isSelected={selected}
onValueChange={(value) => setSelected(value)}/>
</div>

<div className="flex-[2] min-w-0 flex items-center gap-2">
{(
props.type === "folder" ? getIcon(props.name, 20, "#9e9e9e") : (
<File size={20} color="#9e9e9e" className="min-w-[20px]"/>
)
) as ReactNode}
props.type === "folder" ? getFolderIcon(props.name, 20, "#9e9e9e") : getFileIcon(extname ?? "txt", 20, "#9e9e9e")
) as React.ReactNode}
<button
className="text-ellipsis whitespace-nowrap cursor-pointer overflow-hidden hover:underline hover:text-primary-500"
onClick={() => handleClick()}>
onClick={(e) => handleClick(e)}>
{props.name}
</button>
</div>
Expand Down
4 changes: 2 additions & 2 deletions components/explorer/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Dropdown, DropdownMenu, DropdownTrigger, DropdownItem } from "@nextui-o
import { ArrowLeft, HardDrive, Home, FolderRoot } from "lucide-react";
import { to } from "preps";

import { getIcon } from "./explorer-item";
import { getFolderIcon } from "./explorer-item";
import DiskItem from "./disk-item";

import { parseStringPath, useExplorer } from "@/hooks/useExplorer";
Expand Down Expand Up @@ -142,7 +142,7 @@ const Navbar: React.FC = () => {
{
index === 0
? <FolderRoot size={18}/>
: getIcon(folderName, 18)
: getFolderIcon(folderName, 18)
}
{
index === 0
Expand Down
4 changes: 3 additions & 1 deletion hooks/useExplorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ export const useExplorer = create<ExplorerStore>((set, get) => ({
const { path } = get();

if(path.length === 1) return;
set({ path: to(path).remove(path.length - 1).f() });

// Use `Array.from` to clone the array
set({ path: Array.from(to(path).remove(path.length - 1).f()) });
}
}));

0 comments on commit 3352553

Please sign in to comment.