feat: add resize functionality to KnowledgeDirectoryPanel with localization support
This commit is contained in:
@@ -12,7 +12,7 @@ import {
|
|||||||
Trash2Icon,
|
Trash2Icon,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState, type PointerEvent } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
import { OptionCombobox } from "@/components/option-combobox";
|
import { OptionCombobox } from "@/components/option-combobox";
|
||||||
@@ -61,6 +61,11 @@ type DirectoryDialogState = {
|
|||||||
|
|
||||||
type DirectoryOption = { value: string; label: string };
|
type DirectoryOption = { value: string; label: string };
|
||||||
|
|
||||||
|
const DIRECTORY_PANEL_WIDTH_STORAGE_KEY = "knowledge-directory-panel-width";
|
||||||
|
const DIRECTORY_PANEL_MIN_WIDTH = 180;
|
||||||
|
const DIRECTORY_PANEL_MAX_WIDTH = 360;
|
||||||
|
const DIRECTORY_PANEL_DEFAULT_WIDTH = 224;
|
||||||
|
|
||||||
function rootDirectoryOptions(items: KnowledgeDirectory[]): DirectoryOption[] {
|
function rootDirectoryOptions(items: KnowledgeDirectory[]): DirectoryOption[] {
|
||||||
return items.map((item) => ({ value: String(item.id), label: item.name }));
|
return items.map((item) => ({ value: String(item.id), label: item.name }));
|
||||||
}
|
}
|
||||||
@@ -84,6 +89,16 @@ export function KnowledgeDirectoryPanel({
|
|||||||
onChanged,
|
onChanged,
|
||||||
}: KnowledgeDirectoryPanelProps) {
|
}: KnowledgeDirectoryPanelProps) {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
|
const [panelWidth, setPanelWidth] = useState(() => {
|
||||||
|
if (typeof window === "undefined") {
|
||||||
|
return DIRECTORY_PANEL_DEFAULT_WIDTH;
|
||||||
|
}
|
||||||
|
const saved = Number(localStorage.getItem(DIRECTORY_PANEL_WIDTH_STORAGE_KEY));
|
||||||
|
if (!Number.isFinite(saved)) {
|
||||||
|
return DIRECTORY_PANEL_DEFAULT_WIDTH;
|
||||||
|
}
|
||||||
|
return Math.min(DIRECTORY_PANEL_MAX_WIDTH, Math.max(DIRECTORY_PANEL_MIN_WIDTH, saved));
|
||||||
|
});
|
||||||
const [directories, setDirectories] = useState<KnowledgeDirectory[]>([]);
|
const [directories, setDirectories] = useState<KnowledgeDirectory[]>([]);
|
||||||
const [expandedIds, setExpandedIds] = useState<Set<number>>(new Set());
|
const [expandedIds, setExpandedIds] = useState<Set<number>>(new Set());
|
||||||
const [contextMenuDirectoryId, setContextMenuDirectoryId] = useState<number | null>(null);
|
const [contextMenuDirectoryId, setContextMenuDirectoryId] = useState<number | null>(null);
|
||||||
@@ -121,6 +136,35 @@ export function KnowledgeDirectoryPanel({
|
|||||||
void loadDirectories();
|
void loadDirectories();
|
||||||
}, [loadDirectories]);
|
}, [loadDirectories]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
localStorage.setItem(DIRECTORY_PANEL_WIDTH_STORAGE_KEY, String(panelWidth));
|
||||||
|
}, [panelWidth]);
|
||||||
|
|
||||||
|
function handleResizePointerDown(event: PointerEvent<HTMLDivElement>) {
|
||||||
|
event.preventDefault();
|
||||||
|
const startX = event.clientX;
|
||||||
|
const startWidth = panelWidth;
|
||||||
|
|
||||||
|
function handlePointerMove(moveEvent: globalThis.PointerEvent) {
|
||||||
|
const nextWidth = startWidth + moveEvent.clientX - startX;
|
||||||
|
setPanelWidth(
|
||||||
|
Math.min(DIRECTORY_PANEL_MAX_WIDTH, Math.max(DIRECTORY_PANEL_MIN_WIDTH, nextWidth)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePointerUp() {
|
||||||
|
window.removeEventListener("pointermove", handlePointerMove);
|
||||||
|
window.removeEventListener("pointerup", handlePointerUp);
|
||||||
|
document.body.style.cursor = "";
|
||||||
|
document.body.style.userSelect = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
document.body.style.cursor = "col-resize";
|
||||||
|
document.body.style.userSelect = "none";
|
||||||
|
window.addEventListener("pointermove", handlePointerMove);
|
||||||
|
window.addEventListener("pointerup", handlePointerUp);
|
||||||
|
}
|
||||||
|
|
||||||
function toggleDirectory(id: number) {
|
function toggleDirectory(id: number) {
|
||||||
setExpandedIds((current) => {
|
setExpandedIds((current) => {
|
||||||
const next = new Set(current);
|
const next = new Set(current);
|
||||||
@@ -202,7 +246,10 @@ export function KnowledgeDirectoryPanel({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex h-full min-h-0 w-56 shrink-0 flex-col border-r bg-muted/20">
|
<div
|
||||||
|
className="relative flex h-full min-h-0 shrink-0 flex-col border-r bg-muted/20"
|
||||||
|
style={{ width: panelWidth }}
|
||||||
|
>
|
||||||
<div className="flex h-[49px] items-center justify-between border-b bg-background px-3">
|
<div className="flex h-[49px] items-center justify-between border-b bg-background px-3">
|
||||||
<div className="text-sm font-medium">{t("knowledge.directory")}</div>
|
<div className="text-sm font-medium">{t("knowledge.directory")}</div>
|
||||||
<Button
|
<Button
|
||||||
@@ -252,6 +299,13 @@ export function KnowledgeDirectoryPanel({
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
<div
|
||||||
|
className="absolute top-0 right-[-3px] z-10 h-full w-1.5 cursor-col-resize transition-colors hover:bg-primary/30"
|
||||||
|
onPointerDown={handleResizePointerDown}
|
||||||
|
role="separator"
|
||||||
|
aria-orientation="vertical"
|
||||||
|
aria-label={t("knowledge.resizeDirectoryPanel")}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<ProjectDialog
|
<ProjectDialog
|
||||||
open={dialog.open}
|
open={dialog.open}
|
||||||
|
|||||||
@@ -1814,6 +1814,7 @@
|
|||||||
"batchDeleteFailed": "Could not delete selected items.",
|
"batchDeleteFailed": "Could not delete selected items.",
|
||||||
"targetDirectory": "Target directory",
|
"targetDirectory": "Target directory",
|
||||||
"directory": "Directory",
|
"directory": "Directory",
|
||||||
|
"resizeDirectoryPanel": "Resize directory panel",
|
||||||
"allContent": "All content",
|
"allContent": "All content",
|
||||||
"rootContent": "Root content",
|
"rootContent": "Root content",
|
||||||
"rootDirectory": "Root directory",
|
"rootDirectory": "Root directory",
|
||||||
|
|||||||
@@ -1814,6 +1814,7 @@
|
|||||||
"batchDeleteFailed": "批量删除失败",
|
"batchDeleteFailed": "批量删除失败",
|
||||||
"targetDirectory": "目标目录",
|
"targetDirectory": "目标目录",
|
||||||
"directory": "目录",
|
"directory": "目录",
|
||||||
|
"resizeDirectoryPanel": "调整目录面板宽度",
|
||||||
"allContent": "全部内容",
|
"allContent": "全部内容",
|
||||||
"rootContent": "根目录内容",
|
"rootContent": "根目录内容",
|
||||||
"rootDirectory": "一级目录",
|
"rootDirectory": "一级目录",
|
||||||
|
|||||||
Reference in New Issue
Block a user