@mindfiredigital/page-builder
Version:
355 lines (354 loc) • 11.9 kB
JavaScript
import { Canvas } from './canvas/Canvas.js';
import { Sidebar } from './sidebar/ConfigSidebar.js';
import { CustomizationSidebar } from './sidebar/CustomizationSidebar.js';
import { createSidebar } from './sidebar/CreateSidebar.js';
import { createNavbar } from './navbar/CreateNavbar.js';
import { HTMLGenerator } from './services/HTMLGenerator.js';
import { JSONStorage } from './services/JSONStorage.js';
import {
showDialogBox,
showNotification,
syntaxHighlightCSS,
syntaxHighlightHTML,
} from './utils/utilityFunctions.js';
import { createZipFile } from './utils/zipGenerator.js';
import { ShortcutManager } from './services/ShortcutManager.js';
import { PreviewPanel } from './canvas/PreviewPanel.js';
import './styles/main.css';
import { svgs } from './icons/svgs.js';
export class PageBuilder {
constructor(dynamicComponents = { Basic: [], Extra: [], Custom: {} }) {
this.dynamicComponents = dynamicComponents;
this.canvas = new Canvas();
this.sidebar = new Sidebar(this.canvas);
this.htmlGenerator = new HTMLGenerator(this.canvas);
this.jsonStorage = new JSONStorage();
this.previewPanel = new PreviewPanel();
this.initializeEventListeners();
}
initializeEventListeners() {
// document.addEventListener('DOMContentLoaded', () => {
this.canvas = new Canvas();
this.sidebar = new Sidebar(this.canvas);
this.htmlGenerator = new HTMLGenerator(this.canvas);
this.jsonStorage = new JSONStorage();
this.previewPanel = new PreviewPanel();
this.setupInitialComponents();
this.setupSaveButton();
this.setupResetButton();
this.setupExportHTMLButton();
this.setupViewButton();
this.setupPreviewModeButtons();
this.setupUndoRedoButtons();
// });
}
setupInitialComponents() {
createSidebar(this.dynamicComponents);
Canvas.init();
this.sidebar.init();
ShortcutManager.init();
CustomizationSidebar.init();
// Only create header if it doesn't exist
if (!PageBuilder.headerInitialized) {
const existingHeader = document.getElementById('page-builder-header');
if (!existingHeader) {
const appElement = document.getElementById('app');
if (appElement && appElement.parentNode) {
const header = document.createElement('header');
header.id = 'page-builder-header';
header.appendChild(createNavbar());
appElement.parentNode.insertBefore(header, appElement);
PageBuilder.headerInitialized = true;
} else {
console.error('Error: #app not found in the DOM');
}
} else {
PageBuilder.headerInitialized = true;
}
}
}
setupSaveButton() {
const saveButton = document.getElementById('save-btn');
if (saveButton) {
saveButton.addEventListener('click', () => {
const layoutJSON = Canvas.getState();
this.jsonStorage.save(layoutJSON);
showNotification('Saving progress...');
});
}
}
setupResetButton() {
const resetButton = document.getElementById('reset-btn');
if (resetButton) {
resetButton.addEventListener('click', () => {
showDialogBox(
'Are you sure you want to reset the layout?',
() => {
this.jsonStorage.remove();
Canvas.clearCanvas();
showNotification('The saved layout has been successfully reset.');
},
() => {
console.log('Layout reset canceled.');
}
);
});
}
}
setupExportHTMLButton() {
const exportButton = document.getElementById('export-html-btn');
if (exportButton) {
exportButton.addEventListener('click', () => {
const htmlGenerator = new HTMLGenerator(new Canvas());
const html = htmlGenerator.generateHTML();
const css = htmlGenerator.generateCSS();
const highlightedHTML = syntaxHighlightHTML(html);
const highlightedCSS = syntaxHighlightCSS(css);
const modal = this.createExportModal(
highlightedHTML,
highlightedCSS,
html,
css
);
document.body.appendChild(modal);
modal.classList.add('show');
});
}
}
createExportModal(highlightedHTML, highlightedCSS, html, css) {
const modal = document.createElement('div');
modal.id = 'export-dialog';
modal.classList.add('modal');
const modalContent = document.createElement('div');
modalContent.classList.add('modal-content');
const closeButton = this.createCloseButton(modal);
modalContent.appendChild(closeButton);
const htmlSection = this.createCodeSection('HTML', highlightedHTML);
const cssSection = this.createCodeSection('CSS', highlightedCSS);
const exportButton = this.createExportToZipButton(html, css);
modalContent.appendChild(htmlSection);
modalContent.appendChild(cssSection);
modalContent.appendChild(exportButton);
const exportButtonWrapper = document.createElement('div');
exportButtonWrapper.classList.add('button-wrapper');
exportButtonWrapper.appendChild(modalContent);
modal.appendChild(exportButtonWrapper);
this.setupModalEventListeners(modal);
return modal;
}
createCloseButton(modal) {
const closeButton = document.createElement('button');
closeButton.textContent = '×';
closeButton.classList.add('close-btn');
closeButton.addEventListener('click', () => this.closeModal(modal));
return closeButton;
}
createCodeSection(title, highlightedContent) {
const section = document.createElement('div');
section.classList.add('modal-section');
const titleElement = document.createElement('h2');
titleElement.textContent = title;
const codeBlock = document.createElement('div');
codeBlock.classList.add('code-block');
codeBlock.setAttribute('contenteditable', 'true');
codeBlock.innerHTML = highlightedContent;
section.appendChild(titleElement);
section.appendChild(codeBlock);
return section;
}
createExportToZipButton(html, css) {
const exportButton = document.createElement('button');
exportButton.textContent = 'Export to ZIP';
exportButton.classList.add('export-btn');
exportButton.addEventListener('click', () => {
const zipFile = createZipFile([
{ name: 'index.html', content: html },
{ name: 'styles.css', content: css },
]);
const link = document.createElement('a');
link.href = URL.createObjectURL(zipFile);
link.download = 'exported-files.zip';
link.click();
URL.revokeObjectURL(link.href);
});
return exportButton;
}
setupModalEventListeners(modal) {
modal.addEventListener('click', event => {
if (event.target === modal) {
this.closeModal(modal);
}
});
document.addEventListener('keydown', event => {
if (event.key === 'Escape') {
this.closeModal(modal);
}
});
}
closeModal(modal) {
modal.classList.remove('show');
modal.classList.add('hide');
setTimeout(() => modal.remove(), 300);
}
setupViewButton() {
const viewButton = document.getElementById('view-btn');
if (viewButton) {
viewButton.addEventListener('click', () => {
const html = this.htmlGenerator.generateHTML();
const fullScreenModal = this.createFullScreenPreviewModal(html);
document.body.appendChild(fullScreenModal);
});
}
}
createFullScreenPreviewModal(html) {
const fullScreenModal = document.createElement('div');
fullScreenModal.id = 'preview-modal';
fullScreenModal.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: #f5f5f5;
z-index: 1000;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
padding: 10px;
`;
const iframe = document.createElement('iframe');
iframe.id = 'preview-iframe';
iframe.style.cssText = `
width: 97%;
height: 90%;
border: none;
background: #fff;
margin-right: 20px;
`;
iframe.srcdoc = html;
fullScreenModal.appendChild(iframe);
const closeButton = this.createPreviewCloseButton(fullScreenModal);
fullScreenModal.appendChild(closeButton);
const responsivenessContainer = this.createResponsivenessControls(iframe);
fullScreenModal.insertBefore(responsivenessContainer, iframe);
return fullScreenModal;
}
createPreviewCloseButton(fullScreenModal) {
const closeButton = document.createElement('button');
closeButton.id = 'close-modal-btn';
closeButton.textContent = '✕';
closeButton.style.cssText = `
position: absolute;
top: 10px;
right: 20px;
font-size: 20px;
border: none;
background: none;
cursor: pointer;
`;
const closeModal = () => {
setTimeout(() => fullScreenModal.remove(), 300);
document.removeEventListener('keydown', escKeyListener);
};
closeButton.addEventListener('click', closeModal);
const escKeyListener = event => {
if (event.key === 'Escape') {
closeModal();
}
};
document.addEventListener('keydown', escKeyListener);
return closeButton;
}
createResponsivenessControls(iframe) {
const responsivenessContainer = document.createElement('div');
responsivenessContainer.style.cssText = `
display: flex;
gap: 10px;
margin-bottom: 10px;
`;
const sizes = [
{
icon: svgs.mobile,
title: 'Desktop',
width: '375px',
height: '90%',
},
{
icon: svgs.tablet,
title: 'Tablet',
width: '768px',
height: '90%',
},
{
icon: svgs.desktop,
title: 'Mobile',
width: '97%',
height: '90%',
},
];
sizes.forEach(size => {
const button = document.createElement('button');
button.style.cssText = `
padding: 5px;
border: none;
background: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
`;
button.title = size.title;
const iconContainer = document.createElement('div');
iconContainer.innerHTML = size.icon;
const svgElement = iconContainer.querySelector('svg');
if (svgElement) {
svgElement.style.width = '24px';
svgElement.style.height = '24px';
svgElement.classList.add('component-icon');
}
button.appendChild(iconContainer);
button.addEventListener('click', () => {
iframe.style.width = size.width;
iframe.style.height = size.height;
});
responsivenessContainer.appendChild(button);
});
return responsivenessContainer;
}
setupPreviewModeButtons() {
const desktopButton = document.getElementById('preview-desktop');
const tabletButton = document.getElementById('preview-tablet');
const mobileButton = document.getElementById('preview-mobile');
if (desktopButton) {
desktopButton.addEventListener('click', () => {
this.previewPanel.setPreviewMode('desktop');
});
}
if (tabletButton) {
tabletButton.addEventListener('click', () => {
this.previewPanel.setPreviewMode('tablet');
});
}
if (mobileButton) {
mobileButton.addEventListener('click', () => {
this.previewPanel.setPreviewMode('mobile');
});
}
}
setupUndoRedoButtons() {
const undoButton = document.getElementById('undo-btn');
const redoButton = document.getElementById('redo-btn');
if (undoButton) {
undoButton.addEventListener('click', () => {
Canvas.historyManager.undo();
});
}
if (redoButton) {
redoButton.addEventListener('click', () => {
Canvas.historyManager.redo();
});
}
}
}
PageBuilder.headerInitialized = false;