import React, { useRef, useState, useEffect } from 'react'; import { WidgetData } from '../types'; import { CogwheelIcon } from './CogwheelIcon'; import { useAtom, useAtomValue } from 'jotai'; import { draggableState } from '../state/draggable.state'; interface WidgetProps { widget: WidgetData; gridSize: { width: number; height: number }; children: React.ReactNode; settingsContent?: React.ReactNode; updateWidget: (id: string, data: Partial) => void } export const Widget: React.FC = ({ widget, gridSize, children, settingsContent, updateWidget }) => { const widgetRef = useRef(null); const modalRef = useRef(null); const [isDragging, setIsDragging] = useState(false); const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 }); const [isSettingsOpen, setIsSettingsOpen] = useState(false); const [widthInput, setWidthInput] = useState(widget.gridWidth); const [heightInput, setHeightInput] = useState(widget.gridHeight); const isDraggable = useAtomValue(draggableState) const snapToGrid = (pos: number, grid: number) => Math.round(pos / grid); const handleMouseDown = (e: React.MouseEvent) => { if(!isDraggable || isSettingsOpen){ return } if (widgetRef.current && e.target === widgetRef.current) { setIsDragging(true); const rect = widgetRef.current.getBoundingClientRect(); setDragOffset({ x: e.clientX - rect.left, y: e.clientY - rect.top }); } }; const handleMouseMove = (e: MouseEvent) => { if (isDragging && widgetRef.current) { e.preventDefault(); const newGridX = snapToGrid(e.clientX - dragOffset.x, gridSize.width); const newGridY = snapToGrid(e.clientY - dragOffset.y, gridSize.height); widgetRef.current.style.left = `${newGridX * gridSize.width}px`; widgetRef.current.style.top = `${newGridY * gridSize.height}px`; } }; const handleMouseUp = () => { if (isDragging && widgetRef.current) { setIsDragging(false); const rect = widgetRef.current.getBoundingClientRect(); const newGridX = snapToGrid(rect.left, gridSize.width); const newGridY = snapToGrid(rect.top, gridSize.height); updateWidget(widget.id, { gridX: newGridX, gridY: newGridY }); } }; const handleClickOutside = (e: MouseEvent) => { if (modalRef.current && !modalRef.current.contains(e.target as Node)) { setIsSettingsOpen(false); } }; const stopPropagation = (e: React.MouseEvent) => { e.stopPropagation(); }; const handleSizeChange = () => { const newWidth = Math.max(1, widthInput); const newHeight = Math.max(1, heightInput); updateWidget(widget.id, { gridWidth: newWidth, gridHeight: newHeight }); setWidthInput(newWidth); setHeightInput(newHeight); setIsSettingsOpen(false); }; useEffect(() => { if (isDragging) { window.addEventListener('mousemove', handleMouseMove); window.addEventListener('mouseup', handleMouseUp); document.body.style.userSelect = 'none'; } return () => { window.removeEventListener('mousemove', handleMouseMove); window.removeEventListener('mouseup', handleMouseUp); document.body.style.userSelect = ''; }; }, [isDragging, dragOffset, gridSize]); useEffect(() => { if (isSettingsOpen) { document.addEventListener('mousedown', handleClickOutside); } return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, [isSettingsOpen]); useEffect(() => { if (widgetRef.current && !isDragging) { widgetRef.current.style.left = `${widget.gridX * gridSize.width}px`; widgetRef.current.style.top = `${widget.gridY * gridSize.height}px`; widgetRef.current.style.width = `${(widget.gridWidth) * gridSize.width}px`; widgetRef.current.style.height = `${(widget.gridHeight) * gridSize.height}px`; } }, [widget.gridX, widget.gridY, widget.gridWidth, widget.gridHeight, gridSize, isDragging]); const genericSettingsContent = ( <> ); return (
{children} {isSettingsOpen && (
{settingsContent} {genericSettingsContent}
)}
); };