feat: add collapsible settings panel and resizable notes list (v0.1.2)

- Settings panel in categories sidebar now collapses/expands with toggle button
- Settings collapsed by default to save space
- Notes list column now resizable with drag handle (240px-600px range)
- Improved UI flexibility and space management
This commit is contained in:
drelich
2026-03-21 08:43:58 +01:00
parent c147890138
commit e3a1d74413
3 changed files with 143 additions and 81 deletions

View File

@@ -58,6 +58,7 @@ export function CategoriesSidebar({
}: CategoriesSidebarProps) {
const [isCreating, setIsCreating] = useState(false);
const [newCategoryName, setNewCategoryName] = useState('');
const [isSettingsCollapsed, setIsSettingsCollapsed] = useState(true);
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
@@ -185,27 +186,40 @@ export function CategoriesSidebar({
</div>
{/* User Info and Settings */}
<div className="border-t border-gray-200 dark:border-gray-700 p-4 bg-white dark:bg-gray-900">
<div className="flex items-center justify-between mb-3">
<div className="border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900">
<div className="flex items-center justify-between p-4 pb-3">
<div className="flex items-center space-x-2 min-w-0">
<div className="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center text-white font-semibold flex-shrink-0">
{username.charAt(0).toUpperCase()}
</div>
<span className="text-sm text-gray-700 dark:text-gray-200 truncate font-medium">{username}</span>
</div>
<button
onClick={onLogout}
className="p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors flex-shrink-0"
title="Logout"
>
<svg className="w-5 h-5 text-gray-600 dark:text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
</svg>
</button>
<div className="flex items-center gap-1">
<button
onClick={() => setIsSettingsCollapsed(!isSettingsCollapsed)}
className="p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors flex-shrink-0"
title={isSettingsCollapsed ? "Show Settings" : "Hide Settings"}
>
<svg className={`w-4 h-4 text-gray-600 dark:text-gray-300 transition-transform ${isSettingsCollapsed ? 'rotate-180' : ''}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</button>
<button
onClick={onLogout}
className="p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors flex-shrink-0"
title="Logout"
>
<svg className="w-5 h-5 text-gray-600 dark:text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
</svg>
</button>
</div>
</div>
{/* Theme Toggle */}
<div className="flex items-center justify-between mb-3">
{!isSettingsCollapsed && (
<div className="px-4 pb-4">
{/* Theme Toggle */}
<div className="flex items-center justify-between mb-3">
<span className="text-xs text-gray-500 dark:text-gray-400">Theme</span>
<div className="flex items-center space-x-1 bg-gray-100 dark:bg-gray-700 rounded-lg p-1">
<button
@@ -250,77 +264,79 @@ export function CategoriesSidebar({
</div>
</div>
{/* Font Settings */}
<div className="mt-4 pt-3 border-t border-gray-200 dark:border-gray-700 space-y-3">
{/* Font Settings */}
<div className="mt-4 pt-3 border-t border-gray-200 dark:border-gray-700 space-y-3">
<span className="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Fonts</span>
{/* Editor Font */}
<div className="bg-gray-50 dark:bg-gray-800 rounded-lg p-3">
<div className="flex items-center gap-2 mb-2">
<svg className="w-4 h-4 text-gray-500 dark:text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" />
</svg>
<span className="text-xs font-medium text-gray-600 dark:text-gray-300">Editor</span>
</div>
<div className="flex gap-2">
<select
value={editorFont}
onChange={(e) => onEditorFontChange(e.target.value)}
className="flex-1 min-w-0 text-sm px-3 py-1.5 bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg text-gray-700 dark:text-gray-200 focus:ring-2 focus:ring-blue-500 focus:border-transparent cursor-pointer"
style={{ fontFamily: editorFont }}
>
{EDITOR_FONTS.map((font) => (
<option key={font.value} value={font.value} style={{ fontFamily: font.value }}>
{font.name}
</option>
))}
</select>
<select
value={editorFontSize}
onChange={(e) => onEditorFontSizeChange(parseInt(e.target.value, 10))}
className="w-16 flex-shrink-0 text-sm px-2 py-1.5 bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg text-gray-700 dark:text-gray-200 focus:ring-2 focus:ring-blue-500 focus:border-transparent cursor-pointer text-center"
>
{[12, 13, 14, 15, 16, 17, 18, 20, 22, 24].map((size) => (
<option key={size} value={size}>{size}</option>
))}
</select>
</div>
</div>
{/* Editor Font */}
<div className="bg-gray-50 dark:bg-gray-800 rounded-lg p-3">
<div className="flex items-center gap-2 mb-2">
<svg className="w-4 h-4 text-gray-500 dark:text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" />
</svg>
<span className="text-xs font-medium text-gray-600 dark:text-gray-300">Editor</span>
</div>
<div className="flex gap-2">
<select
value={editorFont}
onChange={(e) => onEditorFontChange(e.target.value)}
className="flex-1 min-w-0 text-sm px-3 py-1.5 bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg text-gray-700 dark:text-gray-200 focus:ring-2 focus:ring-blue-500 focus:border-transparent cursor-pointer"
style={{ fontFamily: editorFont }}
>
{EDITOR_FONTS.map((font) => (
<option key={font.value} value={font.value} style={{ fontFamily: font.value }}>
{font.name}
</option>
))}
</select>
<select
value={editorFontSize}
onChange={(e) => onEditorFontSizeChange(parseInt(e.target.value, 10))}
className="w-16 flex-shrink-0 text-sm px-2 py-1.5 bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg text-gray-700 dark:text-gray-200 focus:ring-2 focus:ring-blue-500 focus:border-transparent cursor-pointer text-center"
>
{[12, 13, 14, 15, 16, 17, 18, 20, 22, 24].map((size) => (
<option key={size} value={size}>{size}</option>
))}
</select>
</div>
</div>
{/* Preview Font */}
<div className="bg-gray-50 dark:bg-gray-800 rounded-lg p-3">
<div className="flex items-center gap-2 mb-2">
<svg className="w-4 h-4 text-gray-500 dark:text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
<span className="text-xs font-medium text-gray-600 dark:text-gray-300">Preview</span>
</div>
<div className="flex gap-2">
<select
value={previewFont}
onChange={(e) => onPreviewFontChange(e.target.value)}
className="flex-1 min-w-0 text-sm px-3 py-1.5 bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg text-gray-700 dark:text-gray-200 focus:ring-2 focus:ring-blue-500 focus:border-transparent cursor-pointer"
style={{ fontFamily: previewFont }}
>
{PREVIEW_FONTS.map((font) => (
<option key={font.value} value={font.value} style={{ fontFamily: font.value }}>
{font.name}
</option>
))}
</select>
<select
value={previewFontSize}
onChange={(e) => onPreviewFontSizeChange(parseInt(e.target.value, 10))}
className="w-16 flex-shrink-0 text-sm px-2 py-1.5 bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg text-gray-700 dark:text-gray-200 focus:ring-2 focus:ring-blue-500 focus:border-transparent cursor-pointer text-center"
>
{[12, 13, 14, 15, 16, 17, 18, 20, 22, 24].map((size) => (
<option key={size} value={size}>{size}</option>
))}
</select>
{/* Preview Font */}
<div className="bg-gray-50 dark:bg-gray-800 rounded-lg p-3">
<div className="flex items-center gap-2 mb-2">
<svg className="w-4 h-4 text-gray-500 dark:text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
<span className="text-xs font-medium text-gray-600 dark:text-gray-300">Preview</span>
</div>
<div className="flex gap-2">
<select
value={previewFont}
onChange={(e) => onPreviewFontChange(e.target.value)}
className="flex-1 min-w-0 text-sm px-3 py-1.5 bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg text-gray-700 dark:text-gray-200 focus:ring-2 focus:ring-blue-500 focus:border-transparent cursor-pointer"
style={{ fontFamily: previewFont }}
>
{PREVIEW_FONTS.map((font) => (
<option key={font.value} value={font.value} style={{ fontFamily: font.value }}>
{font.name}
</option>
))}
</select>
<select
value={previewFontSize}
onChange={(e) => onPreviewFontSizeChange(parseInt(e.target.value, 10))}
className="w-16 flex-shrink-0 text-sm px-2 py-1.5 bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg text-gray-700 dark:text-gray-200 focus:ring-2 focus:ring-blue-500 focus:border-transparent cursor-pointer text-center"
>
{[12, 13, 14, 15, 16, 17, 18, 20, 22, 24].map((size) => (
<option key={size} value={size}>{size}</option>
))}
</select>
</div>
</div>
</div>
</div>
</div>
)}
</div>
</div>
);