- Three-column layout (sidebar/canvas/properties) - Sidebar with tabs: Upload, Stickers, Text - Upload tab with drag-and-drop and click-to-upload - Stickers tab with 6 categories (40+ emojis) - Text tab with font selector, size slider, color picker - Properties panel with position, size, rotation controls - Delete button for selected element - Responsive layout for mobile Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
110 lines
3.4 KiB
JavaScript
110 lines
3.4 KiB
JavaScript
export function PropertiesPanel({ selectedElement, onUpdate, onDelete }) {
|
|
if (!selectedElement) {
|
|
return (
|
|
<div className="properties-panel">
|
|
<h3>Properties</h3>
|
|
<div className="no-selection">
|
|
<p>Select an element to edit its properties</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const handlePositionChange = (axis, value) => {
|
|
onUpdate(selectedElement.id, { [axis]: Number(value) });
|
|
};
|
|
|
|
const handleSizeChange = (dimension, value) => {
|
|
onUpdate(selectedElement.id, { [dimension]: Number(value) });
|
|
};
|
|
|
|
const handleRotationChange = (value) => {
|
|
onUpdate(selectedElement.id, { rotation: Number(value) });
|
|
};
|
|
|
|
const getIcon = () => {
|
|
if (selectedElement.type === 'image') return '🖼️';
|
|
if (selectedElement.type === 'text') return 'T';
|
|
if (selectedElement.type === 'sticker') return '😊';
|
|
return '📦';
|
|
};
|
|
|
|
return (
|
|
<div className="properties-panel">
|
|
<h3>Properties</h3>
|
|
<div className="element-header">
|
|
<span className="element-icon">{getIcon()}</span>
|
|
<span className="element-name">
|
|
{selectedElement.type === 'text'
|
|
? selectedElement.text?.substring(0, 20) || 'Text'
|
|
: `${selectedElement.type}`}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="property-group">
|
|
<label>Position</label>
|
|
<div className="property-row">
|
|
<div className="property-input">
|
|
<span className="property-label">X</span>
|
|
<input
|
|
type="number"
|
|
value={Math.round(selectedElement.x)}
|
|
onChange={(e) => handlePositionChange('x', e.target.value)}
|
|
/>
|
|
</div>
|
|
<div className="property-input">
|
|
<span className="property-label">Y</span>
|
|
<input
|
|
type="number"
|
|
value={Math.round(selectedElement.y)}
|
|
onChange={(e) => handlePositionChange('y', e.target.value)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="property-group">
|
|
<label>Size</label>
|
|
<div className="property-row">
|
|
<div className="property-input">
|
|
<span className="property-label">W</span>
|
|
<input
|
|
type="number"
|
|
value={Math.round(selectedElement.width || selectedElement.fontSize || 0)}
|
|
onChange={(e) =>
|
|
handleSizeChange(selectedElement.text ? 'fontSize' : 'width', e.target.value)
|
|
}
|
|
/>
|
|
</div>
|
|
{selectedElement.type !== 'text' && (
|
|
<div className="property-input">
|
|
<span className="property-label">H</span>
|
|
<input
|
|
type="number"
|
|
value={Math.round(selectedElement.height || 0)}
|
|
onChange={(e) => handleSizeChange('height', e.target.value)}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="property-group">
|
|
<label>Rotation: {Math.round(selectedElement.rotation || 0)}°</label>
|
|
<input
|
|
type="range"
|
|
min="0"
|
|
max="360"
|
|
value={selectedElement.rotation || 0}
|
|
onChange={(e) => handleRotationChange(e.target.value)}
|
|
className="rotation-slider"
|
|
/>
|
|
</div>
|
|
|
|
<button className="delete-btn" onClick={() => onDelete(selectedElement.id)}>
|
|
Delete Element
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|