/**
* VisualEditor - Hostinger-Style Website Builder
*/
import React, { useState, useRef, useCallback } from 'react';
import { Canvas } from './Canvas';
import { StylePanel } from './StylePanel';
import { useVisualEditor } from '../hooks/useVisualEditor';
// Section templates for drag and drop
const SECTION_TEMPLATES = {
hero: `
Build Your Dream Website
Create stunning websites in minutes with our intuitive drag-and-drop builder. No coding required.
`,
features: `
Why Choose Us
Everything you need to build a professional website
⚡
Lightning Fast
Optimized for speed with automatic performance enhancements.
🎨
Beautiful Design
Professionally designed templates you can customize.
🔒
Secure Hosting
Enterprise-grade security with free SSL certificates.
`,
testimonials: `
What Our Users Say
"This builder transformed how we create websites. What used to take weeks now takes hours. The drag-and-drop interface is incredibly intuitive."
SJ
Sarah Johnson
Marketing Director
"Finally, a website builder that doesn't require a computer science degree. I built our company site in a weekend and it looks amazing."
MR
Mike Reynolds
Small Business Owner
`,
cta: `
Ready to Get Started?
Join thousands of happy customers building amazing websites.
`,
footer: ``,
gallery: ``,
pricing: `
Simple Pricing
Choose the plan that's right for you
Starter
$9/mo
- ✓ 1 Website
- ✓ 10GB Storage
- ✓ Free SSL
POPULAR
Professional
$29/mo
- ✓ 10 Websites
- ✓ 100GB Storage
- ✓ Priority Support
Enterprise
$99/mo
- ✓ Unlimited Sites
- ✓ 1TB Storage
- ✓ Dedicated Support
`
};
// Element templates
const ELEMENT_TEMPLATES = {
heading: '
Your Heading Here
',
paragraph: 'Add your text here. Click to edit this paragraph and make it your own.
',
button: '',
image: '
',
divider: '
',
spacer: '',
video: 'Video Placeholder
',
form: ``
};
type DeviceMode = 'desktop' | 'tablet' | 'mobile';
type LeftPanelTab = 'add' | 'pages' | 'theme';
// SVG Icons
const Icons = {
desktop: ,
tablet: ,
mobile: ,
undo: ,
redo: ,
plus: ,
layers: ,
pages: ,
theme: ,
preview: ,
chevronDown: ,
chevronRight: ,
drag: ,
text: ,
image: ,
button: ,
video: ,
form: ,
divider: ,
spacer:
};
export const VisualEditor: React.FC = () => {
const {
selectedElement,
showSpacing,
showDimensions,
selectElement,
deselectElement,
applyStyle,
canUndo,
canRedo,
undo,
redo
} = useVisualEditor();
const [deviceMode, setDeviceMode] = useState('desktop');
const [leftTab, setLeftTab] = useState('add');
const [expandedSections, setExpandedSections] = useState>(new Set(['sections', 'elements']));
const [zoom, setZoom] = useState(100);
const [pageName, setPageName] = useState('Home');
const [draggedItem, setDraggedItem] = useState(null);
const canvasRef = useRef<{ addElement: (html: string) => void }>(null);
const toggleSection = (section: string) => {
setExpandedSections(prev => {
const next = new Set(prev);
if (next.has(section)) next.delete(section);
else next.add(section);
return next;
});
};
const handleDragStart = (e: React.DragEvent, type: string, template: string) => {
setDraggedItem(type);
e.dataTransfer.setData('text/html', template);
e.dataTransfer.effectAllowed = 'copy';
};
const handleDragEnd = () => {
setDraggedItem(null);
};
// Get canvas width based on device mode
const getCanvasWidth = () => {
switch (deviceMode) {
case 'tablet': return 768;
case 'mobile': return 375;
default: return '100%';
}
};
return (
{/* ===== TOP HEADER ===== */}
{(['desktop', 'tablet', 'mobile'] as DeviceMode[]).map(mode => (
))}
{zoom}%
{/* ===== MAIN CONTENT ===== */}
{/* ===== LEFT PANEL ===== */}
{/* ===== CANVAS AREA ===== */}
{/* ===== RIGHT PANEL (Style Panel) ===== */}
);
};
// ===== STYLES =====
const styles: { [key: string]: React.CSSProperties } = {
container: {
display: 'flex',
flexDirection: 'column',
height: '100vh',
width: '100vw',
overflow: 'hidden',
background: '#0c0c0c',
fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, sans-serif"
},
// Header
header: {
height: '56px',
background: '#161616',
borderBottom: '1px solid #2a2a2a',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '0 16px',
flexShrink: 0
},
headerLeft: {
display: 'flex',
alignItems: 'center',
gap: '16px'
},
logo: {
display: 'flex',
alignItems: 'center',
gap: '10px',
color: '#fff',
fontWeight: '600',
fontSize: '15px'
},
logoIcon: {
width: '28px',
height: '28px',
background: 'linear-gradient(135deg, #6366f1, #8b5cf6)',
borderRadius: '8px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontWeight: '700',
fontSize: '14px'
},
dividerV: {
width: '1px',
height: '24px',
background: '#333'
},
pageNameInput: {
background: '#222',
border: '1px solid #333',
borderRadius: '6px',
padding: '8px 12px',
color: '#fff',
fontSize: '13px',
width: '140px',
outline: 'none'
},
headerCenter: {
display: 'flex',
alignItems: 'center',
gap: '16px'
},
deviceToggle: {
display: 'flex',
background: '#222',
borderRadius: '8px',
padding: '4px'
},
deviceBtn: {
background: 'none',
border: 'none',
color: '#666',
padding: '8px 12px',
borderRadius: '6px',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
transition: 'all 0.15s'
},
deviceBtnActive: {
background: '#6366f1',
color: '#fff'
},
zoomControl: {
display: 'flex',
alignItems: 'center',
gap: '4px',
background: '#222',
borderRadius: '6px',
padding: '4px'
},
zoomBtn: {
background: 'none',
border: 'none',
color: '#888',
width: '28px',
height: '28px',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '16px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
},
zoomValue: {
color: '#888',
fontSize: '12px',
minWidth: '40px',
textAlign: 'center'
},
headerRight: {
display: 'flex',
alignItems: 'center',
gap: '8px'
},
headerBtn: {
background: '#222',
border: '1px solid #333',
color: '#aaa',
padding: '8px 12px',
borderRadius: '6px',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
gap: '6px',
fontSize: '13px',
transition: 'all 0.15s'
},
publishBtn: {
background: 'linear-gradient(135deg, #6366f1, #8b5cf6)',
border: 'none',
color: '#fff',
padding: '10px 20px',
borderRadius: '6px',
cursor: 'pointer',
fontWeight: '600',
fontSize: '13px'
},
// Main
main: {
display: 'flex',
flex: 1,
overflow: 'hidden'
},
// Left Panel
leftPanel: {
width: '280px',
background: '#161616',
borderRight: '1px solid #2a2a2a',
display: 'flex',
flexDirection: 'column',
flexShrink: 0
},
leftTabs: {
display: 'flex',
borderBottom: '1px solid #2a2a2a'
},
leftTab: {
flex: 1,
background: 'none',
border: 'none',
color: '#666',
padding: '14px 8px',
cursor: 'pointer',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: '4px',
fontSize: '11px',
transition: 'all 0.15s'
},
leftTabActive: {
color: '#fff',
background: '#1e1e1e'
},
leftContent: {
flex: 1,
overflowY: 'auto',
padding: '12px'
},
// Accordion
accordion: {
marginBottom: '8px'
},
accordionHeader: {
width: '100%',
background: '#1e1e1e',
border: 'none',
borderRadius: '8px',
padding: '12px 14px',
color: '#fff',
fontSize: '13px',
fontWeight: '500',
cursor: 'pointer',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center'
},
accordionBody: {
padding: '12px 0'
},
// Section Grid
sectionGrid: {
display: 'grid',
gridTemplateColumns: 'repeat(2, 1fr)',
gap: '8px'
},
sectionItem: {
background: '#1e1e1e',
borderRadius: '8px',
padding: '16px 8px',
textAlign: 'center',
cursor: 'grab',
border: '1px solid transparent',
transition: 'all 0.15s'
},
sectionItemDragging: {
opacity: 0.5,
border: '1px solid #6366f1'
},
sectionThumb: {
fontSize: '24px',
marginBottom: '8px'
},
sectionLabel: {
color: '#888',
fontSize: '11px'
},
// Element Item
elementItem: {
display: 'flex',
alignItems: 'center',
gap: '12px',
padding: '12px',
background: '#1e1e1e',
borderRadius: '8px',
marginBottom: '6px',
cursor: 'grab',
color: '#aaa',
fontSize: '13px',
border: '1px solid transparent',
transition: 'all 0.15s'
},
elementItemDragging: {
opacity: 0.5,
border: '1px solid #6366f1'
},
elementIcon: {
color: '#666'
},
dragHandle: {
marginLeft: 'auto',
color: '#444'
},
// Pages
pagesList: {
display: 'flex',
flexDirection: 'column',
gap: '4px'
},
pageItem: {
display: 'flex',
alignItems: 'center',
gap: '10px',
padding: '12px',
background: '#1e1e1e',
borderRadius: '8px',
color: '#aaa',
fontSize: '13px',
cursor: 'pointer'
},
addPageBtn: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '8px',
padding: '12px',
background: 'none',
border: '1px dashed #333',
borderRadius: '8px',
color: '#666',
fontSize: '13px',
cursor: 'pointer',
marginTop: '8px'
},
// Theme
themePanel: {
display: 'flex',
flexDirection: 'column',
gap: '20px'
},
themeSection: {},
themeSectionTitle: {
color: '#888',
fontSize: '11px',
fontWeight: '600',
textTransform: 'uppercase',
letterSpacing: '0.5px',
marginBottom: '12px'
},
colorGrid: {
display: 'grid',
gridTemplateColumns: 'repeat(4, 1fr)',
gap: '8px'
},
colorSwatch: {
aspectRatio: '1',
borderRadius: '8px',
cursor: 'pointer',
border: '2px solid transparent'
},
themeSelect: {
width: '100%',
background: '#1e1e1e',
border: '1px solid #333',
borderRadius: '6px',
padding: '10px 12px',
color: '#fff',
fontSize: '13px',
cursor: 'pointer'
},
// Canvas
canvasWrapper: {
flex: 1,
background: '#0c0c0c',
display: 'flex',
justifyContent: 'center',
alignItems: 'flex-start',
overflow: 'auto',
padding: '24px'
},
canvasContainer: {
background: '#fff',
borderRadius: '8px',
overflow: 'hidden',
boxShadow: '0 0 0 1px rgba(255,255,255,0.1), 0 30px 100px rgba(0,0,0,0.5)',
minHeight: '100%'
}
};
export default VisualEditor;