124 lines
		
	
	
	
		
			3.3 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			124 lines
		
	
	
	
		
			3.3 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import { asset, Head } from "$fresh/runtime.ts";
 | |
| import { useState } from "preact/hooks";
 | |
| import { extname, join } from "path/posix.ts";
 | |
| import { ComponentChild } from "preact";
 | |
| import UpList from "./UpList.tsx";
 | |
| import { extToIcon } from "../src/media.ts";
 | |
| import natsort from "../src/natsort.ts";
 | |
| import { encodePath } from "../util/util.ts";
 | |
| 
 | |
| function ListItem(props: {
 | |
|   href: string;
 | |
|   icon: string;
 | |
|   children: ComponentChild;
 | |
| }) {
 | |
|   return (
 | |
|     <li class="p-1 hover:bg-gray-400 transition-colors">
 | |
|       <a class="flex gap-2" href={props.href}>
 | |
|         <div class="bg-black dark:bg-white w-4 h-4" style={`mask: url(${asset(props.icon)})`}></div>  
 | |
|         <p class="">{props.children}</p>
 | |
|       </a>
 | |
|     </li>
 | |
|   );
 | |
| }
 | |
| 
 | |
| export interface EntryInfo {
 | |
|   name: string;
 | |
|   isFile: boolean;
 | |
|   isDirectory: boolean;
 | |
|   isSymlink: boolean;
 | |
|   size: number;
 | |
|   lastModified?: Date;
 | |
| }
 | |
| 
 | |
| interface DirListProps {
 | |
|   path: string;
 | |
|   files: EntryInfo[];
 | |
| }
 | |
| const natsortCompare = natsort();
 | |
| 
 | |
| function toSorted<T>(arr: T[], compareFn: (a:T,b:T) => number): T[]{
 | |
|   const ret = Array.from(arr);
 | |
|   ret.sort(compareFn);
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| export function DirList(props: DirListProps) {
 | |
|   const data = props;
 | |
|   const [files, setFiles] = useState(
 | |
|     toSorted(data.files,(
 | |
|       (a,b)=> natsortCompare(a.name,b.name)
 | |
|       ))
 | |
|     );
 | |
| 
 | |
|   return (
 | |
|     <div>
 | |
|       <UpList path={data.path}></UpList>
 | |
|       <ul class="border-2 rounded-md">
 | |
|         <li class="p-1 flex gap-2">
 | |
|           <button class="flex" onClick={sortDir}>
 | |
|             <img src={asset("/icon/sort-down.svg")} /> Sort Directory
 | |
|           </button>
 | |
|           <button class="flex" onClick={sortAlpha}>
 | |
|             <img src={asset("/icon/sort-alpha-down.svg")} /> Sort Alphabet
 | |
|           </button>
 | |
|         </li>
 | |
|         <ListItem
 | |
|           key=".."
 | |
|           href={`/dir/${encodePath(join(data.path, ".."))}/?pretty`}
 | |
|           icon="/icon/back.svg"
 | |
|         >
 | |
|           ...
 | |
|         </ListItem>
 | |
|         {files.map((file) => (
 | |
|           <ListItem
 | |
|             key={file.name}
 | |
|             href={`/dir/${
 | |
|               encodePath(join(data.path, file.name))
 | |
|             }${(file.isDirectory ? "/" : "")}?pretty`}
 | |
|             icon={file.isDirectory
 | |
|               ? "/icon/folder.svg"
 | |
|               : extToIcon(extname(file.name))}
 | |
|           >
 | |
|             {file.name}
 | |
|           </ListItem>
 | |
|         ))}
 | |
|       </ul>
 | |
|     </div>
 | |
|   );
 | |
|   function sortDir() {
 | |
|     // sort by directory first then by index
 | |
|     const sorted_files = files.map((x, i) => ([x, i] as [EntryInfo, number]))
 | |
|       .sort(
 | |
|         ([a, ai], [b, bi]) => {
 | |
|           if (a.isDirectory && !b.isDirectory) {
 | |
|             return -1;
 | |
|           } else if (!a.isDirectory && b.isDirectory) {
 | |
|             return 1;
 | |
|           } else {
 | |
|             return ai - bi;
 | |
|           }
 | |
|         },
 | |
|       );
 | |
|     setFiles(sorted_files.map(([x, _]) => x));
 | |
|   }
 | |
|   function sortAlpha() {
 | |
|     // sort by alphabet first then by index
 | |
|     const sorted_files = files.map((x, i) => ([x, i] as [EntryInfo, number]))
 | |
|       .sort(
 | |
|         ([a, ai], [b, bi]) => {
 | |
|           const ret = natsortCompare(a.name, b.name);
 | |
|           if (ret === 0) {
 | |
|             return ai - bi;
 | |
|           } else {
 | |
|             return ret;
 | |
|           }
 | |
|         },
 | |
|       );
 | |
|     setFiles(sorted_files.map(([x, _]) => x));
 | |
|   }
 | |
| }
 | |
| 
 | |
| export default function DirListIsland(props: DirListProps){
 | |
|   return <DirList {...props}/>
 | |
| }
 |