Minor UI, fix dragging to select
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user