aboutsummaryrefslogtreecommitdiffstats
path: root/apps/web/components/dashboard/ViewOptions.tsx
diff options
context:
space:
mode:
authorMohamed Bassem <me@mbassem.com>2025-07-26 12:58:01 +0000
committerMohamed Bassem <me@mbassem.com>2025-07-26 12:58:01 +0000
commit154efe17421ca96d433fcc1f820ad460e1675bdc (patch)
tree4336090648fe7196818bcc371104d3b603a68c0e /apps/web/components/dashboard/ViewOptions.tsx
parent8b4fb49cc066eef602d9d089e7b71d183231a8fd (diff)
downloadkarakeep-154efe17421ca96d433fcc1f820ad460e1675bdc.tar.zst
feat: Configurable number of grid columns. Fixes #1713
Diffstat (limited to 'apps/web/components/dashboard/ViewOptions.tsx')
-rw-r--r--apps/web/components/dashboard/ViewOptions.tsx115
1 files changed, 115 insertions, 0 deletions
diff --git a/apps/web/components/dashboard/ViewOptions.tsx b/apps/web/components/dashboard/ViewOptions.tsx
new file mode 100644
index 00000000..6367421f
--- /dev/null
+++ b/apps/web/components/dashboard/ViewOptions.tsx
@@ -0,0 +1,115 @@
+"use client";
+
+import React from "react";
+import { ButtonWithTooltip } from "@/components/ui/button";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu";
+import { Slider } from "@/components/ui/slider";
+import {
+ useBookmarkLayout,
+ useGridColumns,
+} from "@/lib/userLocalSettings/bookmarksLayout";
+import {
+ updateBookmarksLayout,
+ updateGridColumns,
+} from "@/lib/userLocalSettings/userLocalSettings";
+import {
+ Check,
+ LayoutDashboard,
+ LayoutGrid,
+ LayoutList,
+ List,
+ LucideIcon,
+ Settings,
+} from "lucide-react";
+
+type LayoutType = "masonry" | "grid" | "list" | "compact";
+
+const iconMap: Record<LayoutType, LucideIcon> = {
+ masonry: LayoutDashboard,
+ grid: LayoutGrid,
+ list: LayoutList,
+ compact: List,
+};
+
+const layoutNames: Record<LayoutType, string> = {
+ masonry: "Masonry",
+ grid: "Grid",
+ list: "List",
+ compact: "Compact",
+};
+
+export default function ViewOptions() {
+ const layout = useBookmarkLayout();
+ const gridColumns = useGridColumns();
+ const [tempColumns, setTempColumns] = React.useState(gridColumns);
+
+ const showColumnSlider = layout === "grid" || layout === "masonry";
+
+ // Update temp value when actual value changes
+ React.useEffect(() => {
+ setTempColumns(gridColumns);
+ }, [gridColumns]);
+
+ return (
+ <DropdownMenu>
+ <DropdownMenuTrigger asChild>
+ <ButtonWithTooltip
+ tooltip="View Options"
+ delayDuration={100}
+ variant="ghost"
+ >
+ <Settings size={18} />
+ </ButtonWithTooltip>
+ </DropdownMenuTrigger>
+ <DropdownMenuContent className="w-56">
+ <div className="px-2 py-1.5 text-sm font-semibold">Layout</div>
+ {(Object.keys(iconMap) as LayoutType[]).map((key) => (
+ <DropdownMenuItem
+ key={key}
+ className="cursor-pointer justify-between"
+ onClick={async () => await updateBookmarksLayout(key as LayoutType)}
+ >
+ <div className="flex items-center gap-2">
+ {React.createElement(iconMap[key as LayoutType], { size: 18 })}
+ <span>{layoutNames[key]}</span>
+ </div>
+ {layout === key && <Check className="ml-2 size-4" />}
+ </DropdownMenuItem>
+ ))}
+
+ {showColumnSlider && (
+ <>
+ <DropdownMenuSeparator />
+ <div className="px-2 py-3">
+ <div className="mb-2 flex items-center justify-between">
+ <span className="text-sm font-semibold">Columns</span>
+ <span className="text-sm text-muted-foreground">
+ {tempColumns}
+ </span>
+ </div>
+ <Slider
+ value={[tempColumns]}
+ onValueChange={([value]) => setTempColumns(value)}
+ onValueCommit={([value]) => updateGridColumns(value)}
+ min={1}
+ max={6}
+ step={1}
+ className="w-full"
+ />
+ <div className="mt-1 flex justify-between text-xs text-muted-foreground">
+ <span>1</span>
+ <span>6</span>
+ </div>
+ </div>
+ </>
+ )}
+ </DropdownMenuContent>
+ </DropdownMenu>
+ );
+}