Files
apparel-designer/client/src/components/sidebar/StickersTab.jsx
Khalid A fd11a36d93 Phase 3: Sidebar & Properties Panel
Implemented full editor UI with three-column layout:
- Sidebar with 4 tabs (Upload, Stickers, Text, Templates)
- UploadTab with drag-and-drop file upload, wires to POST /api/upload
- StickersTab with 96 emoji stickers across 6 categories
- TextTab with font picker (20 Google Fonts), size slider, color picker
- TemplatesTab placeholder for future template system
- LayersPanel showing all elements with select/delete
- PropertiesPanel with position, size, rotation controls

Also added:
- Constants for fonts and stickers
- Enhanced CSS with editor-layout, sidebar, properties-panel classes
- Updated App.jsx to integrate all components
2026-04-21 01:27:59 -05:00

112 lines
3.1 KiB
JavaScript

import { useState } from 'react';
import { STICKERS, STICKER_CATEGORIES } from '../../constants/stickers';
export function StickersTab({ onAddSticker }) {
const [activeCategory, setActiveCategory] = useState('all');
const categories = ['all', ...STICKER_CATEGORIES];
const filteredStickers = activeCategory === 'all'
? STICKERS
: STICKERS.filter(s => s.category === activeCategory);
const handleAddSticker = (emoji) => {
// Create a canvas element with the emoji
const canvas = document.createElement('canvas');
const size = 100;
canvas.width = size;
canvas.height = size;
const ctx = canvas.getContext('2d');
ctx.font = `${size * 0.8}px Arial`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(emoji, size / 2, size / 2);
const dataUrl = canvas.toDataURL('image/png');
onAddSticker({
type: 'sticker',
x: 125,
y: 125,
width: 80,
height: 80,
rotation: 0,
src: dataUrl,
emoji,
});
};
return (
<div>
<h3 style={{ margin: '0 0 1rem 0', fontSize: '14px', color: 'var(--text-primary)' }}>
Stickers
</h3>
{/* Category pills */}
<div style={{
display: 'flex',
gap: '6px',
marginBottom: '1rem',
flexWrap: 'wrap',
}}>
{categories.map((cat) => (
<button
key={cat}
onClick={() => setActiveCategory(cat)}
style={{
padding: '6px 12px',
border: `1px solid ${activeCategory === cat ? 'var(--accent)' : 'var(--border)'}`,
borderRadius: 'var(--radius-xl)',
background: activeCategory === cat ? 'var(--accent)' : 'var(--bg-primary)',
color: activeCategory === cat ? '#fff' : 'var(--text-secondary)',
fontSize: '11px',
cursor: 'pointer',
textTransform: 'capitalize',
transition: 'all 0.15s ease',
}}
>
{cat}
</button>
))}
</div>
{/* Sticker grid */}
<div style={{
display: 'grid',
gridTemplateColumns: 'repeat(5, 1fr)',
gap: '8px',
}}>
{filteredStickers.map((sticker, index) => (
<button
key={index}
onClick={() => handleAddSticker(sticker.emoji)}
style={{
aspectRatio: '1',
border: 'none',
borderRadius: 'var(--radius-md)',
background: 'var(--bg-primary)',
fontSize: '28px',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
transition: 'all 0.15s ease',
}}
onMouseEnter={(e) => {
e.target.style.background = 'var(--accent-bg)';
e.target.style.transform = 'scale(1.1)';
}}
onMouseLeave={(e) => {
e.target.style.background = 'var(--bg-primary)';
e.target.style.transform = 'scale(1)';
}}
>
{sticker.emoji}
</button>
))}
</div>
</div>
);
}