diff --git a/src/app/CommandK.tsx b/src/app/CommandK.tsx
index a32066b0..af1d9413 100644
--- a/src/app/CommandK.tsx
+++ b/src/app/CommandK.tsx
@@ -1,19 +1,8 @@
import CommandKClient from '@/components/cmdk/CommandKClient';
-import {
- getPhotosMetaCached,
- getUniqueCamerasCached,
- getUniqueFilmSimulationsCached,
- getUniqueLensesCached,
- getUniqueRecipesCached,
- getUniqueTagsCached,
-} from '@/photo/cache';
+import { getPhotosMetaCached } from '@/photo/cache';
import { photoQuantityText } from '@/photo';
-import {
- ADMIN_DEBUG_TOOLS_ENABLED,
- SHOW_FILM_SIMULATIONS,
- SHOW_RECIPES,
-} from './config';
-import { getUniqueFocalLengths } from '@/photo/db/query';
+import { ADMIN_DEBUG_TOOLS_ENABLED } from './config';
+import { getDataForCategories } from '@/category/data';
export default async function CommandK() {
const [
@@ -28,16 +17,7 @@ export default async function CommandK() {
getPhotosMetaCached()
.then(({ count }) => count)
.catch(() => 0),
- getUniqueCamerasCached().catch(() => []),
- getUniqueLensesCached().catch(() => []),
- getUniqueTagsCached().catch(() => []),
- SHOW_RECIPES
- ? getUniqueRecipesCached().catch(() => [])
- : [],
- SHOW_FILM_SIMULATIONS
- ? getUniqueFilmSimulationsCached().catch(() => [])
- : [],
- getUniqueFocalLengths().catch(() => []),
+ ...getDataForCategories(),
]);
return
[
+ getUniqueCameras()
+ .then(sortCategoriesByCount)
+ .catch(() => []),
+ SHOW_LENSES
+ ? getUniqueLenses()
+ .then(sortCategoriesByCount)
+ .catch(() => [])
+ : [],
+ getUniqueTags()
+ .then(sortTagsByCount)
+ .catch(() => []),
+ SHOW_RECIPES
+ ? getUniqueRecipes()
+ .then(sortCategoriesByCount)
+ .catch(() => [])
+ : [],
+ SHOW_FILM_SIMULATIONS
+ ? getUniqueFilmSimulations()
+ .then(sortCategoriesByCount)
+ .catch(() => [])
+ : [],
+ SHOW_FOCAL_LENGTHS
+ ? getUniqueFocalLengths()
+ .then(sortCategoriesByCount)
+ .catch(() => [])
+ : [],
+] as const;
diff --git a/src/category/index.ts b/src/category/index.ts
index 970c97f9..be9f044a 100644
--- a/src/category/index.ts
+++ b/src/category/index.ts
@@ -66,3 +66,41 @@ export const getOrderedCategoriesFromString = (
.map(category => category.trim().toLocaleLowerCase() as CategoryKey)
.filter(category => CATEGORY_KEYS.includes(category))
: DEFAULT_CATEGORY_KEYS;
+
+export const sortCategoryByCount = (
+ a: { count: number },
+ b: { count: number },
+) => b.count - a.count;
+
+export const sortCategoriesByCount = (
+ categories: T[],
+) => categories.sort(sortCategoryByCount);
+
+const convertCategoryKeysToCategoryNames =
+ (categoryKeys: CategoryKeys): (keyof PhotoSetCategories)[] => {
+ return categoryKeys.map(key => {
+ return key === 'films'
+ ? 'simulations'
+ : key === 'focal-lengths'
+ ? 'focalLengths'
+ : key;
+ });
+ };
+
+export const getCategoryItemsCount = (
+ categoryKeys: CategoryKeys,
+ categories: PhotoSetCategories,
+) =>
+ convertCategoryKeysToCategoryNames(categoryKeys).reduce((acc, key) =>
+ acc + (categories[key]?.length ?? 0)
+ , 0);
+
+export const getCategoriesWithItemsCount = (
+ categoryKeys: CategoryKeys,
+ categories: PhotoSetCategories,
+) =>
+ convertCategoryKeysToCategoryNames(categoryKeys).reduce((acc, key) =>
+ (categories[key]?.length ?? 0) > 0
+ ? acc + 1
+ : acc
+ , 0);
diff --git a/src/components/HeaderList.tsx b/src/components/HeaderList.tsx
index b652538a..2becf235 100644
--- a/src/components/HeaderList.tsx
+++ b/src/components/HeaderList.tsx
@@ -1,23 +1,37 @@
+'use client';
+
import { clsx } from 'clsx/lite';
import AnimateItems from './AnimateItems';
-import { ReactNode } from 'react';
+import { ReactNode, useState } from 'react';
+import LoaderButton from './primitives/LoaderButton';
+import { IoChevronDownOutline, IoChevronUpOutline } from 'react-icons/io5';
+import { COLLAPSE_SIDEBAR_CATEGORIES } from '@/app/config';
export default function HeaderList({
title,
className,
icon,
items,
+ maxItems = 5,
}: {
title?: string,
className?: string,
icon?: ReactNode,
- items: ReactNode[]
+ items: ReactNode[],
+ maxItems?: number,
}) {
+ const [isExpanded, setIsExpanded] = useState(false);
+
+ const hasItemsToExpand =
+ COLLAPSE_SIDEBAR_CATEGORIES &&
+ // Don't show expand button if it only reveals 1 item
+ items.length > (maxItems + 1);
+
return (
{icon &&
@@ -38,8 +52,44 @@ export default function HeaderList({
}
{title}
]
- :[] as ReactNode[]
- ).concat(items)}
+ : [] as ReactNode[]
+ )
+ .concat(items.slice(
+ 0,
+ hasItemsToExpand && !isExpanded ? maxItems : items.length,
+ ))
+ .concat(hasItemsToExpand
+ ? [
+