You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
140 lines
3.5 KiB
JavaScript
140 lines
3.5 KiB
JavaScript
import React, { useState, useEffect } from 'react';
|
|
import ResizeHandles from './ResizeHandles';
|
|
import './elements.css';
|
|
|
|
const LINE_CHARS = {
|
|
single: '─',
|
|
double: '═'
|
|
};
|
|
|
|
function HorizontalLine({
|
|
element,
|
|
isSelected,
|
|
isSingleSelection,
|
|
onSelect,
|
|
onDelete,
|
|
onUpdate,
|
|
onDragStart,
|
|
onDrag,
|
|
charWidth,
|
|
charHeight,
|
|
crossroadMap = {},
|
|
toolMode
|
|
}) {
|
|
const [isDragging, setIsDragging] = useState(false);
|
|
const [dragData, setDragData] = useState(null);
|
|
|
|
const lineChar = LINE_CHARS[element.lineStyle || 'single'];
|
|
|
|
useEffect(() => {
|
|
const handleKeyPress = (e) => {
|
|
if (isSelected && e.key === 'Delete') {
|
|
onDelete();
|
|
}
|
|
};
|
|
|
|
document.addEventListener('keydown', handleKeyPress);
|
|
return () => document.removeEventListener('keydown', handleKeyPress);
|
|
}, [isSelected, onDelete]);
|
|
|
|
const handleClick = (e) => {
|
|
// Only handle selection in select mode, let other modes bubble to canvas
|
|
if (toolMode === 'select') {
|
|
e.stopPropagation();
|
|
onSelect(e);
|
|
}
|
|
};
|
|
|
|
const handleMouseDown = (e) => {
|
|
// Only handle dragging in select mode
|
|
if (toolMode !== 'select') return;
|
|
|
|
e.stopPropagation();
|
|
onSelect(e);
|
|
|
|
setIsDragging(true);
|
|
const data = onDragStart(element.id, e.clientX, e.clientY);
|
|
setDragData({ ...data, startMouseX: e.clientX, startMouseY: e.clientY });
|
|
};
|
|
|
|
const handleResize = (updates) => {
|
|
onUpdate(updates);
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (!isDragging) return;
|
|
|
|
// Add class to body to disable transitions globally during drag
|
|
document.body.classList.add('dragging-active');
|
|
|
|
const handleMouseMove = (e) => {
|
|
if (dragData) {
|
|
const deltaX = e.clientX - dragData.startMouseX;
|
|
const deltaY = e.clientY - dragData.startMouseY;
|
|
onDrag(dragData, deltaX, deltaY);
|
|
}
|
|
};
|
|
|
|
const handleMouseUp = () => {
|
|
setIsDragging(false);
|
|
setDragData(null);
|
|
document.body.classList.remove('dragging-active');
|
|
};
|
|
|
|
document.addEventListener('mousemove', handleMouseMove);
|
|
document.addEventListener('mouseup', handleMouseUp);
|
|
|
|
return () => {
|
|
document.removeEventListener('mousemove', handleMouseMove);
|
|
document.removeEventListener('mouseup', handleMouseUp);
|
|
document.body.classList.remove('dragging-active');
|
|
};
|
|
}, [isDragging, dragData, onDrag]);
|
|
|
|
// Render line with possible crossroad overrides
|
|
const renderLine = () => {
|
|
let line = '';
|
|
for (let i = 0; i < element.length; i++) {
|
|
const gridX = element.x + i;
|
|
const gridY = element.y;
|
|
const crossroadKey = `${gridX},${gridY}`;
|
|
|
|
// Use crossroad character if it exists, otherwise use line character
|
|
if (crossroadMap[crossroadKey]) {
|
|
line += crossroadMap[crossroadKey];
|
|
} else {
|
|
line += lineChar;
|
|
}
|
|
}
|
|
return line;
|
|
};
|
|
|
|
const style = {
|
|
left: `${element.x * charWidth}px`,
|
|
top: `${element.y * charHeight}px`,
|
|
width: `${element.length * charWidth}px`,
|
|
height: `${charHeight}px`
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className={`line-element horizontal-line ${isSelected ? 'selected' : ''} ${isDragging ? 'dragging' : ''}`}
|
|
style={style}
|
|
onClick={handleClick}
|
|
onMouseDown={handleMouseDown}
|
|
>
|
|
{renderLine()}
|
|
{isSingleSelection && (
|
|
<ResizeHandles
|
|
element={element}
|
|
onResize={handleResize}
|
|
charWidth={charWidth}
|
|
charHeight={charHeight}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default HorizontalLine;
|