feat(web): persist file tree expansion and add column visibility callbacks
Save expanded file tree paths to localStorage so they survive page reloads, and surface an onColumnVisibilityChanged callback from DataTable/ObjectTable.
This commit is contained in:
parent
1c21b039fc
commit
3533aa3358
@ -60,6 +60,7 @@ export type DataTableProps<TData, TValue> = {
|
||||
enableColumnReordering?: boolean;
|
||||
onColumnReorder?: (newOrder: string[]) => void;
|
||||
initialColumnVisibility?: VisibilityState;
|
||||
onColumnVisibilityChanged?: (visibility: VisibilityState) => void;
|
||||
// pagination
|
||||
pageSize?: number;
|
||||
// actions
|
||||
@ -173,6 +174,7 @@ export function DataTable<TData, TValue>({
|
||||
enableColumnReordering = false,
|
||||
onColumnReorder,
|
||||
initialColumnVisibility,
|
||||
onColumnVisibilityChanged,
|
||||
pageSize: defaultPageSize = 100,
|
||||
onRefresh,
|
||||
onAdd,
|
||||
@ -345,7 +347,11 @@ export function DataTable<TData, TValue>({
|
||||
onSortingChange: setSorting,
|
||||
onGlobalFilterChange: setGlobalFilter,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
onColumnVisibilityChange: (updater) => {
|
||||
const next = typeof updater === "function" ? updater(columnVisibility) : updater;
|
||||
setColumnVisibility(next);
|
||||
onColumnVisibilityChanged?.(next);
|
||||
},
|
||||
onRowSelectionChange: (updater) => {
|
||||
if (onRowSelectionChange) {
|
||||
onRowSelectionChange(updater);
|
||||
|
||||
@ -716,8 +716,27 @@ function flattenVisible(tree: TreeNode[], expanded: Set<string>): TreeNode[] {
|
||||
|
||||
// --- Main Exported Component ---
|
||||
|
||||
const STORAGE_KEY = "denchclaw-tree-expanded";
|
||||
|
||||
function loadExpandedPaths(): Set<string> {
|
||||
try {
|
||||
const raw = window.localStorage.getItem(STORAGE_KEY);
|
||||
if (raw) {
|
||||
const arr = JSON.parse(raw) as string[];
|
||||
if (Array.isArray(arr)) return new Set(arr);
|
||||
}
|
||||
} catch { /* ignore corrupt data */ }
|
||||
return new Set();
|
||||
}
|
||||
|
||||
function saveExpandedPaths(paths: Set<string>) {
|
||||
try {
|
||||
window.localStorage.setItem(STORAGE_KEY, JSON.stringify([...paths]));
|
||||
} catch { /* storage full or unavailable */ }
|
||||
}
|
||||
|
||||
export function FileManagerTree({ tree, activePath, onSelect, onRefresh, compact, parentDir, onNavigateUp, browseDir: _browseDir, workspaceRoot, onExternalDrop }: FileManagerTreeProps) {
|
||||
const [expandedPaths, setExpandedPaths] = useState<Set<string>>(() => new Set());
|
||||
const [expandedPaths, setExpandedPaths] = useState<Set<string>>(() => loadExpandedPaths());
|
||||
const [selectedPath, setSelectedPath] = useState<string | null>(null);
|
||||
const [renamingPath, setRenamingPath] = useState<string | null>(null);
|
||||
const [dragOverPath, setDragOverPath] = useState<string | null>(null);
|
||||
@ -771,21 +790,10 @@ export function FileManagerTree({ tree, activePath, onSelect, onRefresh, compact
|
||||
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Auto-expand first level on mount.
|
||||
// Keep ~skills and ~memories collapsed by default; always expand ~chats.
|
||||
const collapsedByDefault = new Set(["~skills", "~memories"]);
|
||||
// Persist expanded paths to localStorage whenever they change
|
||||
useEffect(() => {
|
||||
if (tree.length > 0 && expandedPaths.size === 0) {
|
||||
const initial = new Set<string>();
|
||||
for (const node of tree) {
|
||||
if (collapsedByDefault.has(node.path)) {continue;}
|
||||
if (node.children && node.children.length > 0) {
|
||||
initial.add(node.path);
|
||||
}
|
||||
}
|
||||
setExpandedPaths(initial);
|
||||
}
|
||||
}, [tree]);
|
||||
saveExpandedPaths(expandedPaths);
|
||||
}, [expandedPaths]);
|
||||
|
||||
const handleToggleExpand = useCallback((path: string) => {
|
||||
setExpandedPaths((prev) => {
|
||||
|
||||
@ -50,6 +50,7 @@ type ObjectTableProps = {
|
||||
onRefresh?: () => void;
|
||||
/** Column visibility state keyed by field ID. */
|
||||
columnVisibility?: Record<string, boolean>;
|
||||
onColumnVisibilityChanged?: (visibility: Record<string, boolean>) => void;
|
||||
/** Server-side pagination props. */
|
||||
serverPagination?: ServerPaginationProps;
|
||||
/** Server-side search callback. */
|
||||
@ -440,6 +441,7 @@ export function ObjectTable({
|
||||
onEntryClick,
|
||||
onRefresh,
|
||||
columnVisibility,
|
||||
onColumnVisibilityChanged,
|
||||
serverPagination,
|
||||
onServerSearch,
|
||||
}: ObjectTableProps) {
|
||||
@ -711,6 +713,7 @@ export function ObjectTable({
|
||||
rowActions={getRowActions}
|
||||
stickyFirstColumn
|
||||
initialColumnVisibility={columnVisibility}
|
||||
onColumnVisibilityChanged={onColumnVisibilityChanged}
|
||||
serverPagination={serverPagination}
|
||||
onServerSearch={onServerSearch}
|
||||
/>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user