First refactor without AI

This commit is contained in:
Your Name
2025-03-15 18:32:10 -04:00
commit ab5380a399
25 changed files with 1661 additions and 0 deletions
+164
View File
@@ -0,0 +1,164 @@
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<WidgetData>) => void
}
export const Widget: React.FC<WidgetProps> = ({ widget, gridSize, children, settingsContent, updateWidget }) => {
const widgetRef = useRef<HTMLDivElement>(null);
const modalRef = useRef<HTMLDivElement>(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 = (
<>
<label>
Width (grid cells):
<input
type="number"
min="1"
value={widthInput}
onChange={(e) => setWidthInput(parseInt(e.target.value) || 1)}
/>
</label>
<label>
Height (grid cells):
<input
type="number"
min="1"
value={heightInput}
onChange={(e) => setHeightInput(parseInt(e.target.value) || 1)}
/>
</label>
<button onClick={handleSizeChange}>Apply</button>
</>
);
return (
<div
ref={widgetRef}
className="container"
onMouseDown={handleMouseDown}
style={{ cursor: isDraggable && !isSettingsOpen ? 'grab' : 'default' }}
>
<button className="close-button" onClick={() => undefined /*removeWidget(widget.id)*/} onMouseDown={stopPropagation}>×</button>
<button
className="settings-button"
onClick={() => setIsSettingsOpen(!isSettingsOpen)}
onMouseDown={stopPropagation}
>
<CogwheelIcon size={24} />
</button>
{children}
{isSettingsOpen && (
<div className="widget-settings-modal" onMouseDown={stopPropagation}>
<div ref={modalRef} className="widget-modal-content">
<button className="close-button" onClick={() => setIsSettingsOpen(false)} onMouseDown={stopPropagation}>×</button>
{settingsContent}
{genericSettingsContent}
</div>
</div>
)}
</div>
);
};