diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index b135526c..e98ae834 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -1,8 +1,9 @@ -import { useState, useMemo, memo, type ComponentType } from 'react' +import { useState, useMemo, useRef, useEffect, memo, type ComponentType } from 'react' import type { VaultEntry, SidebarSelection } from '../types' import { cn } from '@/lib/utils' -import { ChevronRight, ChevronDown, GitCommitHorizontal, Plus } from 'lucide-react' +import { ChevronRight, ChevronDown, GitCommitHorizontal, Plus, SlidersHorizontal } from 'lucide-react' import { getTypeColor, getTypeLightColor } from '../utils/typeColors' +import { useSectionVisibility } from '../hooks/useSectionVisibility' import { FileText, Star, @@ -56,6 +57,22 @@ const BUILT_IN_TYPES = new Set(BUILT_IN_SECTION_GROUPS.map((s) => s.type)) export const Sidebar = memo(function Sidebar({ entries, selection, onSelect, onSelectNote, onCreateType, onCreateNewType, modifiedCount = 0, onCommitPush }: SidebarProps) { const [collapsed, setCollapsed] = useState>({}) + const [showCustomize, setShowCustomize] = useState(false) + const customizeRef = useRef(null) + const { toggleSection: toggleVisibility, isSectionVisible } = useSectionVisibility() + + // Close popover on outside click + useEffect(() => { + if (!showCustomize) return + const handleClick = (e: MouseEvent) => { + if (customizeRef.current && !customizeRef.current.contains(e.target as Node)) { + setShowCustomize(false) + } + } + document.addEventListener('mousedown', handleClick) + return () => document.removeEventListener('mousedown', handleClick) + }, [showCustomize]) + const toggleSection = (type: string) => { setCollapsed((prev) => ({ ...prev, [type]: !prev[type] })) } @@ -232,8 +249,76 @@ export const Sidebar = memo(function Sidebar({ entries, selection, onSelect, onS - {/* Section Groups (built-in + custom) */} - {allSectionGroups.map(renderSection)} + {/* Customize sections button + popover */} +
+ + + {showCustomize && ( +
+
+ Show in sidebar +
+ {allSectionGroups.map(({ label, type, Icon }) => ( + + ))} +
+ )} +
+ + {/* Section Groups (built-in + custom), filtered by visibility */} + {allSectionGroups.filter((g) => isSectionVisible(g.type)).map(renderSection)} {/* Commit button — always visible */} diff --git a/src/hooks/useSectionVisibility.ts b/src/hooks/useSectionVisibility.ts new file mode 100644 index 00000000..ee864655 --- /dev/null +++ b/src/hooks/useSectionVisibility.ts @@ -0,0 +1,44 @@ +import { useState, useCallback } from 'react' + +const STORAGE_KEY = 'laputa-hidden-sections' + +function loadHiddenSections(): Set { + try { + const raw = localStorage.getItem(STORAGE_KEY) + if (raw) { + const arr = JSON.parse(raw) + if (Array.isArray(arr)) return new Set(arr) + } + } catch { + // ignore corrupt data + } + return new Set() +} + +function saveHiddenSections(hidden: Set) { + localStorage.setItem(STORAGE_KEY, JSON.stringify([...hidden])) +} + +export function useSectionVisibility() { + const [hiddenSections, setHiddenSections] = useState>(loadHiddenSections) + + const toggleSection = useCallback((type: string) => { + setHiddenSections((prev) => { + const next = new Set(prev) + if (next.has(type)) { + next.delete(type) + } else { + next.add(type) + } + saveHiddenSections(next) + return next + }) + }, []) + + const isSectionVisible = useCallback( + (type: string) => !hiddenSections.has(type), + [hiddenSections], + ) + + return { hiddenSections, toggleSection, isSectionVisible } +}