Minor UI, fix dragging to select

This commit is contained in:
Zimeng Xiong
2025-11-21 22:52:32 -08:00
parent 6a57e668dd
commit aae0059a6f
3 changed files with 48 additions and 22 deletions
+6 -6
View File
@@ -36,22 +36,22 @@ export const ConfirmModal: React.FC<ConfirmModalProps> = ({
/>
{/* Modal */}
<div className="relative w-full max-w-md bg-white rounded-2xl border-2 border-black shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] p-6 animate-in fade-in zoom-in-95 duration-200">
<div className="relative w-full max-w-md bg-white dark:bg-neutral-900 rounded-2xl border-2 border-black dark:border-neutral-700 shadow-[4px_4px_0px_0px_rgba(0,0,0,1)] dark:shadow-[2px_2px_0px_0px_rgba(255,255,255,0.08)] p-6 animate-in fade-in zoom-in-95 duration-200">
<button
onClick={onCancel}
className="absolute right-4 top-4 text-neutral-400 hover:text-neutral-900 transition-colors"
className="absolute right-4 top-4 text-neutral-400 hover:text-neutral-900 dark:hover:text-white transition-colors"
>
<X size={20} />
</button>
<div className="flex flex-col items-center text-center gap-4">
<div className="w-12 h-12 rounded-full bg-rose-100 flex items-center justify-center text-rose-600 border-2 border-rose-200">
<div className="w-12 h-12 rounded-full bg-rose-100 dark:bg-rose-900/30 flex items-center justify-center text-rose-600 dark:text-rose-300 border-2 border-rose-200 dark:border-rose-900/30">
<AlertTriangle size={24} strokeWidth={2.5} />
</div>
<div className="space-y-2">
<h3 className="text-xl font-bold text-neutral-900 tracking-tight">{title}</h3>
<p className="text-sm font-medium text-neutral-500 leading-relaxed">
<h3 className="text-xl font-bold text-neutral-900 dark:text-neutral-100 tracking-tight">{title}</h3>
<p className="text-sm font-medium text-neutral-500 dark:text-neutral-400 leading-relaxed">
{message}
</p>
</div>
@@ -61,7 +61,7 @@ export const ConfirmModal: React.FC<ConfirmModalProps> = ({
{showCancel && (
<button
onClick={onCancel}
className="flex-1 px-4 py-2.5 bg-emerald-50 text-emerald-700 font-bold rounded-xl border-2 border-emerald-200 hover:bg-emerald-100 hover:border-emerald-300 hover:-translate-y-0.5 transition-all duration-200"
className="flex-1 px-4 py-2.5 bg-emerald-50 dark:bg-neutral-800 text-emerald-700 dark:text-emerald-200 font-bold rounded-xl border-2 border-emerald-200 dark:border-neutral-700 hover:bg-emerald-100 dark:hover:bg-neutral-700 hover:border-emerald-300 dark:hover:border-neutral-600 hover:-translate-y-0.5 transition-all duration-200"
>
{cancelText}
</button>
+1 -1
View File
@@ -88,7 +88,7 @@ const SidebarItem: React.FC<SidebarItemProps> = ({
onDrop?.(e, id);
}}
className={clsx(
"w-full flex items-center gap-3 px-3 py-2.5 text-sm font-bold rounded-lg transition-all duration-200 border-2 group cursor-pointer outline-none focus:ring-2 focus:ring-indigo-500",
"w-full flex items-center gap-3 px-3 py-2.5 text-sm font-bold rounded-lg transition-all duration-200 border-2 group cursor-pointer outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:focus-visible:ring-2 dark:focus-visible:ring-neutral-500",
isActive || isDragOver
? "bg-indigo-50 dark:bg-neutral-800 text-indigo-900 dark:text-neutral-200 border-black dark:border-neutral-700 shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] dark:shadow-[2px_2px_0px_0px_rgba(255,255,255,0.2)] -translate-y-0.5"
: "text-slate-600 dark:text-neutral-400 border-transparent hover:bg-slate-50 dark:hover:bg-neutral-800 hover:border-black dark:hover:border-neutral-700 hover:shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] dark:hover:shadow-[2px_2px_0px_0px_rgba(255,255,255,0.2)] hover:-translate-y-0.5"
+40 -14
View File
@@ -11,6 +11,32 @@ import clsx from 'clsx';
import { ConfirmModal } from '../components/ConfirmModal';
import { importDrawings, importLibrary } from '../utils/importUtils';
type Point = { x: number; y: number };
type SelectionBounds = {
left: number;
top: number;
right: number;
bottom: number;
width: number;
height: number;
};
const getSelectionBounds = (start: Point, current: Point): SelectionBounds => {
const left = Math.min(start.x, current.x);
const right = Math.max(start.x, current.x);
const top = Math.min(start.y, current.y);
const bottom = Math.max(start.y, current.y);
return {
left,
top,
right,
bottom,
width: right - left,
height: bottom - top,
};
};
const DragOverlayPortal: React.FC<{ children: React.ReactNode }> = ({ children }) => {
return createPortal(children, document.body);
};
@@ -55,8 +81,8 @@ export const Dashboard: React.FC = () => {
// Drag Selection State
const [isDragSelecting, setIsDragSelecting] = useState(false);
const [dragStart, setDragStart] = useState<{ x: number; y: number } | null>(null);
const [dragCurrent, setDragCurrent] = useState<{ x: number; y: number } | null>(null);
const [dragStart, setDragStart] = useState<Point | null>(null);
const [dragCurrent, setDragCurrent] = useState<Point | null>(null);
const [potentialDragId, setPotentialDragId] = useState<string | null>(null);
const containerRef = useRef<HTMLDivElement>(null);
@@ -123,6 +149,11 @@ export const Dashboard: React.FC = () => {
}
}, []);
const selectionBounds = React.useMemo<SelectionBounds | null>(() => {
if (!dragStart || !dragCurrent) return null;
return getSelectionBounds(dragStart, dragCurrent);
}, [dragStart, dragCurrent]);
useEffect(() => {
if (!isDragSelecting) return;
@@ -138,14 +169,9 @@ export const Dashboard: React.FC = () => {
return;
}
// Calculate selection rect
const left = Math.min(dragStart.x, dragCurrent.x);
const top = Math.min(dragStart.y, dragCurrent.y);
const width = Math.abs(dragCurrent.x - dragStart.x);
const height = Math.abs(dragCurrent.y - dragStart.y);
const selectionRect = { left, top, right: left + width, bottom: top + height };
const selectionRect = getSelectionBounds(dragStart, dragCurrent);
if (width > 5 || height > 5) {
if (selectionRect.width > 5 || selectionRect.height > 5) {
const newSelectedIds = new Set(selectedIds);
drawings.forEach(drawing => {
const card = document.getElementById(`drawing-card-${drawing.id}`);
@@ -636,15 +662,15 @@ export const Dashboard: React.FC = () => {
</div>
{/* Drag Selection Overlay */}
{isDragSelecting && dragStart && dragCurrent && (
{isDragSelecting && selectionBounds && (
<DragOverlayPortal>
<div
className="fixed z-50 pointer-events-none border-2 border-black dark:border-neutral-500 bg-neutral-500/20 shadow-[2px_2px_0px_0px_rgba(0,0,0,1)] dark:shadow-[2px_2px_0px_0px_rgba(255,255,255,0.2)]"
style={{
left: Math.min(dragStart.x, dragCurrent.x),
top: Math.min(dragStart.y, dragCurrent.y),
width: Math.abs(dragCurrent.x - dragStart.x),
height: Math.abs(dragCurrent.y - dragStart.y),
left: selectionBounds.left,
top: selectionBounds.top,
width: selectionBounds.width,
height: selectionBounds.height,
}}
/>
</DragOverlayPortal>