First refactor without AI
This commit is contained in:
@@ -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>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user