@mytec: resize
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import { useEffect, useState, useCallback, useRef } from 'react';
|
||||
import type { Site } from '@/types/index.ts';
|
||||
import type { Preset } from '@/services/api.ts';
|
||||
import { api } from '@/services/api.ts';
|
||||
@@ -117,6 +117,46 @@ export default function App() {
|
||||
const [showShortcuts, setShowShortcuts] = useState(false);
|
||||
const [kbDeleteTarget, setKbDeleteTarget] = useState<{ id: string; name: string } | null>(null);
|
||||
|
||||
// Resizable sidebar
|
||||
const PANEL_MIN = 300;
|
||||
const PANEL_MAX = 600;
|
||||
const PANEL_DEFAULT = 380;
|
||||
const [panelWidth, setPanelWidth] = useState(() => {
|
||||
const saved = localStorage.getItem('rfcp-panel-width');
|
||||
const n = saved ? Number(saved) : PANEL_DEFAULT;
|
||||
return n >= PANEL_MIN && n <= PANEL_MAX ? n : PANEL_DEFAULT;
|
||||
});
|
||||
const isDragging = useRef(false);
|
||||
|
||||
const handleDragStart = useCallback((e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
isDragging.current = true;
|
||||
document.body.style.cursor = 'col-resize';
|
||||
document.body.style.userSelect = 'none';
|
||||
|
||||
const onMove = (ev: MouseEvent) => {
|
||||
if (!isDragging.current) return;
|
||||
const newWidth = window.innerWidth - ev.clientX;
|
||||
const clamped = Math.max(PANEL_MIN, Math.min(PANEL_MAX, newWidth));
|
||||
setPanelWidth(clamped);
|
||||
};
|
||||
|
||||
const onUp = () => {
|
||||
isDragging.current = false;
|
||||
document.body.style.cursor = '';
|
||||
document.body.style.userSelect = '';
|
||||
document.removeEventListener('mousemove', onMove);
|
||||
document.removeEventListener('mouseup', onUp);
|
||||
setPanelWidth((w) => {
|
||||
localStorage.setItem('rfcp-panel-width', String(w));
|
||||
return w;
|
||||
});
|
||||
};
|
||||
|
||||
document.addEventListener('mousemove', onMove);
|
||||
document.addEventListener('mouseup', onUp);
|
||||
}, []);
|
||||
|
||||
// Load sites from IndexedDB on mount
|
||||
useEffect(() => {
|
||||
loadSites();
|
||||
@@ -587,9 +627,16 @@ export default function App() {
|
||||
<div
|
||||
className={`${
|
||||
panelCollapsed ? 'hidden' : 'flex'
|
||||
} flex-col w-full sm:w-80 lg:w-96 bg-gray-50 dark:bg-dark-bg border-l border-gray-200 dark:border-dark-border
|
||||
} flex-col bg-gray-50 dark:bg-dark-bg border-l border-gray-200 dark:border-dark-border
|
||||
overflow-y-auto absolute sm:relative inset-0 sm:inset-auto z-[1001]`}
|
||||
style={{ width: window.innerWidth >= 640 ? panelWidth : undefined }}
|
||||
>
|
||||
{/* Resize drag handle (desktop only) */}
|
||||
<div
|
||||
onMouseDown={handleDragStart}
|
||||
className="hidden sm:block absolute left-0 top-0 bottom-0 w-1 cursor-col-resize z-10
|
||||
hover:bg-blue-400/50 active:bg-blue-500/60 transition-colors"
|
||||
/>
|
||||
{/* Mobile drag handle + close */}
|
||||
<div className="sm:hidden flex flex-col items-center pt-2 pb-1">
|
||||
<div className="w-12 h-1 bg-gray-300 dark:bg-dark-border rounded-full mb-2" />
|
||||
|
||||
Reference in New Issue
Block a user