카테고리 목록 이름 기반 정렬 구현

This commit is contained in:
static
2025-01-23 00:28:30 +09:00
parent b48b9719ca
commit 606609d468
6 changed files with 100 additions and 46 deletions

View File

@@ -27,3 +27,32 @@ export const formatNetworkSpeed = (speed: number) => {
if (speed < 1000 * 1000 * 1000) return `${(speed / 1000 / 1000).toFixed(1)} Mbps`;
return `${(speed / 1000 / 1000 / 1000).toFixed(1)} Gbps`;
};
export enum SortBy {
NAME_ASC,
NAME_DESC,
}
type SortFunc = (a?: string, b?: string) => number;
const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" });
const sortByNameAsc: SortFunc = (a, b) => {
if (a && b) return collator.compare(a, b);
if (a) return -1;
if (b) return 1;
return 0;
};
const sortByNameDesc: SortFunc = (a, b) => -sortByNameAsc(a, b);
export const sortEntries = <T extends { name?: string }>(entries: T[], sortBy: SortBy) => {
let sortFunc: SortFunc;
if (sortBy === SortBy.NAME_ASC) {
sortFunc = sortByNameAsc;
} else {
sortFunc = sortByNameDesc;
}
entries.sort((a, b) => sortFunc(a.name, b.name));
};

View File

@@ -1,8 +1,9 @@
<script lang="ts">
import type { Component } from "svelte";
import { untrack, type Component } from "svelte";
import type { SvelteHTMLElements } from "svelte/elements";
import type { Writable } from "svelte/store";
import { get, type Writable } from "svelte/store";
import type { CategoryInfo } from "$lib/modules/filesystem";
import { SortBy, sortEntries } from "$lib/modules/util";
import Category from "./Category.svelte";
import type { SelectedCategory } from "./service";
@@ -11,16 +12,48 @@
categoryMenuIcon?: Component<SvelteHTMLElements["svg"]>;
onCategoryClick: (category: SelectedCategory) => void;
onCategoryMenuClick?: (category: SelectedCategory) => void;
sortBy?: SortBy;
}
let { categories, categoryMenuIcon, onCategoryClick, onCategoryMenuClick }: Props = $props();
let {
categories,
categoryMenuIcon,
onCategoryClick,
onCategoryMenuClick,
sortBy = SortBy.NAME_ASC,
}: Props = $props();
let categoriesWithName: { name?: string; info: Writable<CategoryInfo | null> }[] = $state([]);
$effect(() => {
categoriesWithName = categories.map((category) => ({
name: get(category)?.name,
info: category,
}));
const sort = () => {
sortEntries(categoriesWithName, sortBy);
};
return untrack(() => {
sort();
const unsubscribes = categoriesWithName.map((category) =>
category.info.subscribe((value) => {
if (category.name === value?.name) return;
category.name = value?.name;
sort();
}),
);
return () => unsubscribes.forEach((unsubscribe) => unsubscribe());
});
});
</script>
{#if categories.length > 0}
{#if categoriesWithName.length > 0}
<div class="space-y-1">
{#each categories as category}
{#each categoriesWithName as { info }}
<Category
info={category}
{info}
menuIcon={categoryMenuIcon}
onclick={onCategoryClick}
onMenuClick={onCategoryMenuClick}

View File

@@ -1,7 +1,9 @@
<script lang="ts">
import type { Writable } from "svelte/store";
import { untrack } from "svelte";
import { get, type Writable } from "svelte/store";
import { CheckBox } from "$lib/components/inputs";
import { getFileInfo, type FileInfo, type CategoryInfo } from "$lib/modules/filesystem";
import { SortBy, sortEntries } from "$lib/modules/util";
import type { SelectedCategory } from "$lib/molecules/Categories";
import SubCategories from "$lib/molecules/SubCategories.svelte";
import { masterKeyStore } from "$lib/stores";
@@ -17,6 +19,7 @@
onSubCategoryClick: (subCategory: SelectedCategory) => void;
onSubCategoryCreateClick: () => void;
onSubCategoryMenuClick: (subCategory: SelectedCategory) => void;
sortBy?: SortBy;
isFileRecursive: boolean;
}
@@ -27,21 +30,42 @@
onSubCategoryClick,
onSubCategoryCreateClick,
onSubCategoryMenuClick,
sortBy = SortBy.NAME_ASC,
isFileRecursive = $bindable(),
}: Props = $props();
let files: { info: Writable<FileInfo | null>; isRecursive: boolean }[] = $state([]);
let files: { name?: string; info: Writable<FileInfo | null>; isRecursive: boolean }[] = $state(
[],
);
$effect(() => {
files =
info.files
?.filter(({ isRecursive }) => isFileRecursive || !isRecursive)
.map(({ id, isRecursive }) => ({
info: getFileInfo(id, $masterKeyStore?.get(1)?.key!),
isRecursive,
})) ?? [];
.map(({ id, isRecursive }) => {
const info = getFileInfo(id, $masterKeyStore?.get(1)?.key!);
return {
name: get(info)?.name,
info,
isRecursive,
};
}) ?? [];
// TODO: Sorting
const sort = () => {
sortEntries(files, sortBy);
};
return untrack(() => {
sort();
const unsubscribes = files.map((file) =>
file.info.subscribe((value) => {
if (file.name === value?.name) return;
file.name = value?.name;
sort();
}),
);
return () => unsubscribes.forEach((unsubscribe) => unsubscribe());
});
});
</script>

View File

@@ -7,6 +7,7 @@
type DirectoryInfo,
type FileInfo,
} from "$lib/modules/filesystem";
import { SortBy, sortEntries } from "$lib/modules/util";
import {
fileUploadStatusStore,
isFileUploading,
@@ -15,7 +16,6 @@
} from "$lib/stores";
import File from "./File.svelte";
import SubDirectory from "./SubDirectory.svelte";
import { SortBy, sortEntries } from "./service";
import UploadingFile from "./UploadingFile.svelte";
import type { SelectedDirectoryEntry } from "../service";

View File

@@ -1,2 +1 @@
export { default } from "./DirectoryEntries.svelte";
export * from "./service";

View File

@@ -1,31 +0,0 @@
export enum SortBy {
NAME_ASC,
NAME_DESC,
}
type SortFunc = (a?: string, b?: string) => number;
const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" });
const sortByNameAsc: SortFunc = (a, b) => {
if (a && b) return collator.compare(a, b);
if (a) return -1;
if (b) return 1;
return 0;
};
const sortByNameDesc: SortFunc = (a, b) => -sortByNameAsc(a, b);
export const sortEntries = <T extends { name?: string }>(
entries: T[],
sortBy: SortBy = SortBy.NAME_ASC,
) => {
let sortFunc: SortFunc;
if (sortBy === SortBy.NAME_ASC) {
sortFunc = sortByNameAsc;
} else {
sortFunc = sortByNameDesc;
}
entries.sort((a, b) => sortFunc(a.name, b.name));
};