Files
apparel-designer/src/components/panels/PropertiesPanel.jsx
2026-04-22 06:21:02 -05:00

114 lines
5.9 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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>
);
});