114 lines
5.9 KiB
JavaScript
114 lines
5.9 KiB
JavaScript
import { memo } from 'react';
|
||
import { BackgroundRemovalButton } from '../sidebar/BackgroundRemovalButton';
|
||
|
||
export const PropertiesPanel = memo(function PropertiesPanel({ element, onUpdate, onDelete, onEditPhoto }) {
|
||
if (!element) {
|
||
return (
|
||
<div className="properties-panel">
|
||
<div style={{ padding: '1rem', borderBottom: '1px solid var(--border)' }}>
|
||
<h3 style={{ margin: 0, fontSize: '14px', fontWeight: '600', color: 'var(--text-primary)' }}>Properties</h3>
|
||
</div>
|
||
<div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '1rem', color: 'var(--text-muted)', fontSize: '12px', textAlign: 'center' }}>
|
||
Select an element to edit its properties
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
const handlePositionChange = (axis, value) => onUpdate({ [axis]: parseFloat(value) || 0 });
|
||
const handleSizeChange = (axis, value) => onUpdate({ [axis]: Math.max(20, parseFloat(value) || 20) });
|
||
const handleRotationChange = (value) => onUpdate({ rotation: Math.max(-180, Math.min(180, parseFloat(value) || 0)) });
|
||
|
||
const inputStyle = { width: '100%', padding: '0.5rem', border: '1px solid var(--border)', borderRadius: 'var(--radius-sm)', fontSize: '13px' };
|
||
const labelStyle = { display: 'block', fontSize: '11px', fontWeight: '600', color: 'var(--text-secondary)', marginBottom: '0.5rem', textTransform: 'uppercase' };
|
||
|
||
return (
|
||
<div className="properties-panel">
|
||
<div style={{ padding: '1rem', borderBottom: '1px solid var(--border)' }}>
|
||
<h3 style={{ margin: 0, fontSize: '14px', fontWeight: '600', color: 'var(--text-primary)' }}>Properties</h3>
|
||
</div>
|
||
|
||
<div style={{ flex: 1, overflow: 'auto', padding: '1rem' }}>
|
||
{/* Element type badge */}
|
||
<div style={{ display: 'inline-block', padding: '4px 8px', background: 'var(--accent-bg)', borderRadius: 'var(--radius-sm)', fontSize: '11px', fontWeight: '600', color: 'var(--accent)', textTransform: 'uppercase', marginBottom: '1rem' }}>
|
||
{element.type}
|
||
</div>
|
||
|
||
{/* Position */}
|
||
<div style={{ marginBottom: '1rem' }}>
|
||
<label style={labelStyle}>Position</label>
|
||
<div style={{ display: 'flex', gap: '0.5rem' }}>
|
||
<div style={{ flex: 1 }}>
|
||
<label style={{ fontSize: '10px', color: 'var(--text-muted)' }}>X</label>
|
||
<input type="number" value={Math.round(element.x)} onChange={(e) => handlePositionChange('x', e.target.value)} style={inputStyle} />
|
||
</div>
|
||
<div style={{ flex: 1 }}>
|
||
<label style={{ fontSize: '10px', color: 'var(--text-muted)' }}>Y</label>
|
||
<input type="number" value={Math.round(element.y)} onChange={(e) => handlePositionChange('y', e.target.value)} style={inputStyle} />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Size (for images and stickers) */}
|
||
{(element.type === 'image' || element.type === 'sticker') && (
|
||
<div style={{ marginBottom: '1rem' }}>
|
||
<label style={labelStyle}>Size</label>
|
||
<div style={{ display: 'flex', gap: '0.5rem' }}>
|
||
<div style={{ flex: 1 }}>
|
||
<label style={{ fontSize: '10px', color: 'var(--text-muted)' }}>W</label>
|
||
<input type="number" value={Math.round(element.width)} onChange={(e) => handleSizeChange('width', e.target.value)} style={inputStyle} />
|
||
</div>
|
||
<div style={{ flex: 1 }}>
|
||
<label style={{ fontSize: '10px', color: 'var(--text-muted)' }}>H</label>
|
||
<input type="number" value={Math.round(element.height)} onChange={(e) => handleSizeChange('height', e.target.value)} style={inputStyle} />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Edit Photo button */}
|
||
{element.type === 'image' && onEditPhoto && (
|
||
<div style={{ marginBottom: '1rem' }}>
|
||
<button onClick={() => onEditPhoto(element)} style={{ width: '100%', padding: '0.75rem', border: '1px solid var(--accent)', borderRadius: 'var(--radius-md)', background: 'var(--accent-bg)', color: 'var(--accent)', fontSize: '13px', fontWeight: '600', cursor: 'pointer' }}>
|
||
✏️ Edit Photo
|
||
</button>
|
||
</div>
|
||
)}
|
||
|
||
{/* Text-specific controls */}
|
||
{element.type === 'text' && (
|
||
<>
|
||
<div style={{ marginBottom: '1rem' }}>
|
||
<label style={labelStyle}>Font Size: {Math.round(element.fontSize)}px</label>
|
||
<input type="range" min="12" max="120" value={element.fontSize} onChange={(e) => onUpdate({ fontSize: parseInt(e.target.value, 10) })} style={{ width: '100%' }} />
|
||
</div>
|
||
<div style={{ marginBottom: '1rem' }}>
|
||
<label style={labelStyle}>Color</label>
|
||
<input type="color" value={element.fill} onChange={(e) => onUpdate({ fill: e.target.value })} style={{ width: '100%', height: '36px', border: '1px solid var(--border)', borderRadius: 'var(--radius-sm)', cursor: 'pointer', padding: '2px' }} />
|
||
</div>
|
||
</>
|
||
)}
|
||
|
||
{/* Rotation */}
|
||
<div style={{ marginBottom: '1rem' }}>
|
||
<label style={labelStyle}>Rotation: {Math.round(element.rotation)}°</label>
|
||
<input type="range" min="-180" max="180" value={element.rotation} onChange={(e) => handleRotationChange(e.target.value)} style={{ width: '100%' }} />
|
||
</div>
|
||
|
||
{/* Background Removal (for images) */}
|
||
{element.type === 'image' && (
|
||
<BackgroundRemovalButton
|
||
selectedElement={element}
|
||
onUpdate={(_id, attrs) => onUpdate(attrs)}
|
||
/>
|
||
)}
|
||
|
||
{/* Delete */}
|
||
<button onClick={() => onDelete(element.id)} style={{ width: '100%', padding: '0.75rem', border: 'none', borderRadius: 'var(--radius-md)', background: 'var(--error)', color: '#fff', fontSize: '13px', fontWeight: '600', cursor: 'pointer', marginTop: '1rem' }}>
|
||
Delete Element
|
||
</button>
|
||
</div>
|
||
</div>
|
||
);
|
||
});
|