@mindfiredigital/page-builder
Version:
1,475 lines (1,474 loc) • 105 kB
JavaScript
'use strict';
class e {
constructor(e, t) {
(this.canvas = e), (this.sidebar = t);
}
enable() {
this.sidebar.querySelectorAll('.draggable').forEach(e => {
e.addEventListener('dragstart', t => {
var n;
const s = t;
console.log(`Dragging component: ${e.id}`),
null === (n = s.dataTransfer) ||
void 0 === n ||
n.setData('component-type', e.id);
});
});
}
}
class t {
constructor(e = 'Sample Text') {
this.text = e;
}
create() {
const e = document.createElement('div');
return (
(e.innerText = this.text),
(e.contentEditable = 'true'),
e.classList.add('text-component'),
e
);
}
setText(e) {
this.text = e;
}
}
class n {
create(e = null) {
const t = document.createElement('div');
t.classList.add('image-component');
const s = `image-container-${Date.now()}-${Math.random().toString(36).substring(2, 10)}`;
(t.id = s),
(t.style.width = '300px'),
(t.style.height = '300px'),
(t.style.position = 'relative'),
(t.style.backgroundColor = e ? 'transparent' : '#f0f0f0'),
(t.style.display = 'flex'),
(t.style.border = 'none'),
(t.style.alignItems = 'center'),
(t.style.justifyContent = 'center');
const o = document.createElement('div');
(o.style.color = '#666666'),
(o.style.border = 'none'),
(o.style.display = e ? 'none' : 'block');
const i = document.createElement('input');
(i.type = 'file'),
(i.accept = 'image/*'),
(i.style.display = 'none'),
i.addEventListener('change', e => n.handleFileChange(e, t, o));
const l = document.createElement('button');
l.classList.add('upload-btn'),
(l.innerHTML = '🖊️'),
(l.style.position = 'absolute'),
(l.style.padding = '8px'),
(l.style.background = 'transparent'),
(l.style.border = 'none'),
(l.style.cursor = 'pointer'),
(l.style.opacity = '0'),
(l.style.transition = 'opacity 0.2s'),
(l.style.left = '50%'),
(l.style.top = '50%'),
(l.style.transform = 'translate(-50%, -50%)'),
(l.style.fontSize = '24px'),
l.addEventListener('click', () => i.click());
const r = document.createElement('img'),
a = `${s}-img`;
return (
(r.id = a),
(r.style.width = '100%'),
(r.style.height = '100%'),
(r.style.objectFit = 'contain'),
(r.style.border = 'none'),
(r.style.display = 'none'),
e && ((r.src = e), (r.style.display = 'block')),
t.addEventListener('mouseenter', () => {
l.style.opacity = '1';
}),
t.addEventListener('mouseleave', () => {
l.style.opacity = '0';
}),
t.appendChild(o),
t.appendChild(i),
t.appendChild(l),
t.appendChild(r),
t
);
}
static handleFileChange(e, t, n) {
const s = e.target,
o = s.files ? s.files[0] : null;
if (o) {
const e = new FileReader();
(e.onload = function () {
const s = e.result,
o = t.querySelector('img');
o &&
((o.src = s),
(o.style.display = 'block'),
(n.style.display = 'none'),
(t.style.backgroundColor = 'transparent'));
}),
e.readAsDataURL(o);
}
}
static restoreImageUpload(e, t) {
const n = e.querySelector('div:not(.upload-btn)'),
s = e.querySelector('input[type="file"]'),
o = e.querySelector('.upload-btn'),
i = e.querySelector('img');
s.addEventListener('change', t => this.handleFileChange(t, e, n)),
o.addEventListener('click', () => s.click()),
t
? ((i.src = t),
(i.style.display = 'block'),
(n.style.display = 'none'),
(e.style.backgroundColor = 'transparent'))
: ((i.style.display = 'none'),
(n.style.display = 'block'),
(e.style.backgroundColor = '#f0f0f0')),
e.addEventListener('mouseenter', () => {
o.style.opacity = '1';
}),
e.addEventListener('mouseleave', () => {
o.style.opacity = '0';
});
}
}
class s {
constructor(e) {
this.captureStateHandler = e;
}
create(e = null) {
const t = document.createElement('div');
t.classList.add('video-component');
const n = document.createElement('input');
(n.type = 'file'),
(n.accept = 'video/*'),
(n.style.display = 'none'),
n.addEventListener('change', e => {
this.handleFileChange(e, t), this.captureStateHandler();
});
const s = document.createElement('div');
s.classList.add('upload-text'), (s.innerText = e ? '' : 'Upload Video');
const o = document.createElement('video');
(o.controls = !0),
(o.style.width = '100%'),
(o.style.height = '100%'),
(o.style.display = e ? 'block' : 'none'),
e && (o.src = e);
const i = document.createElement('button');
return (
(i.innerHTML = '🖊️'),
i.classList.add('pencil-button'),
i.addEventListener('click', () => n.click()),
t.appendChild(s),
t.appendChild(n),
t.appendChild(o),
t.appendChild(i),
t
);
}
handleFileChange(e, t) {
const n = e.target,
s = n.files ? n.files[0] : null;
if (s && s.type.startsWith('video/')) {
const e = new FileReader();
(e.onload = () => {
const n = t.querySelector('video'),
s = t.querySelector('.upload-text');
(n.src = e.result),
(n.style.display = 'block'),
(s.style.display = 'none');
}),
e.readAsDataURL(s);
} else alert('Please upload a valid video file.');
}
}
class o {
create(e = 'Click Me') {
const t = document.createElement('button');
return (
(t.innerText = e),
t.classList.add('button-component'),
(t.style.padding = '10px 20px'),
(t.style.fontSize = '14px'),
(t.style.borderRadius = '4px'),
(t.style.cursor = 'pointer'),
t
);
}
}
class i {
create(e = 1, t = 'Header') {
const n = document.createElement(`h${e}`);
return (n.innerText = t), n.classList.add('header-component'), n;
}
}
class l {
constructor() {
(this.MINIMUM_SIZE = 20),
(this.originalWidth = 0),
(this.originalHeight = 0),
(this.originalX = 0),
(this.originalY = 0),
(this.originalMouseX = 0),
(this.originalMouseY = 0),
(this.currentResizer = null),
(this.resize = e => {
if (!this.currentResizer) return;
const t = e.pageX - this.originalMouseX,
n = e.pageY - this.originalMouseY;
if (this.currentResizer.classList.contains('bottom-right')) {
const e = this.originalWidth + t,
s = this.originalHeight + n;
e > this.MINIMUM_SIZE && (this.element.style.width = `${e}px`),
s > this.MINIMUM_SIZE && (this.element.style.height = `${s}px`);
} else if (this.currentResizer.classList.contains('bottom-left')) {
const e = this.originalHeight + n,
s = this.originalWidth - t;
e > this.MINIMUM_SIZE && (this.element.style.height = `${e}px`),
s > this.MINIMUM_SIZE &&
((this.element.style.width = `${s}px`),
(this.element.style.left = `${this.originalX + t}px`));
} else if (this.currentResizer.classList.contains('top-right')) {
const e = this.originalWidth + t,
s = this.originalHeight - n;
e > this.MINIMUM_SIZE && (this.element.style.width = `${e}px`),
s > this.MINIMUM_SIZE &&
((this.element.style.height = `${s}px`),
(this.element.style.top = `${this.originalY + n}px`));
} else if (this.currentResizer.classList.contains('top-left')) {
const e = this.originalWidth - t,
s = this.originalHeight - n;
e > this.MINIMUM_SIZE &&
((this.element.style.width = `${e}px`),
(this.element.style.left = `${this.originalX + t}px`)),
s > this.MINIMUM_SIZE &&
((this.element.style.height = `${s}px`),
(this.element.style.top = `${this.originalY + n}px`));
}
}),
(this.stopResize = () => {
window.removeEventListener('mousemove', this.resize),
window.removeEventListener('mouseup', this.stopResize),
(this.currentResizer = null),
b.historyManager.captureState();
}),
(this.element = document.createElement('div')),
this.element.classList.add('container-component'),
this.element.setAttribute('draggable', 'true'),
(this.resizers = document.createElement('div')),
this.resizers.classList.add('resizers'),
this.element.appendChild(this.resizers),
this.addResizeHandles(),
this.addStyles(),
this.initializeEventListeners();
}
addResizeHandles() {
[
{ class: 'top-left', cursor: 'nwse-resize' },
{ class: 'top-right', cursor: 'nesw-resize' },
{ class: 'bottom-left', cursor: 'nesw-resize' },
{ class: 'bottom-right', cursor: 'nwse-resize' },
].forEach(e => {
const t = document.createElement('div');
t.classList.add('resizer', e.class),
t.addEventListener('mousedown', e => this.initResize(e, t)),
this.resizers.appendChild(t);
});
}
initResize(e, t) {
e.preventDefault(),
(this.currentResizer = t),
(this.originalWidth = parseFloat(
getComputedStyle(this.element).getPropertyValue('width')
)),
(this.originalHeight = parseFloat(
getComputedStyle(this.element).getPropertyValue('height')
)),
(this.originalX = this.element.getBoundingClientRect().left),
(this.originalY = this.element.getBoundingClientRect().top),
(this.originalMouseX = e.pageX),
(this.originalMouseY = e.pageY),
window.addEventListener('mousemove', this.resize),
window.addEventListener('mouseup', this.stopResize);
}
initializeEventListeners() {
this.element.addEventListener('dragstart', this.onDragStart.bind(this)),
this.element.addEventListener('drop', this.onDrop.bind(this)),
this.element.addEventListener('dragover', e => e.preventDefault()),
this.element.addEventListener('mouseover', this.onMouseOver.bind(this)),
this.element.addEventListener('mouseleave', this.onMouseLeave.bind(this));
}
onDragStart(e) {
e.stopPropagation();
}
makeDraggable(e) {
let t = !1,
n = 0,
s = 0,
o = 0,
i = 0;
const l = l => {
if (!t) return;
const r = l.clientX - n,
a = l.clientY - s;
e.style.transform = `translate(${o + r}px, ${i + a}px)`;
},
r = () => {
(t = !1),
window.removeEventListener('mousemove', l),
window.removeEventListener('mouseup', r);
};
e.addEventListener('mousedown', a => {
a.preventDefault(),
a.stopPropagation(),
(t = !0),
(n = a.clientX),
(s = a.clientY);
const d = e.getBoundingClientRect();
(o = d.left),
(i = d.top),
window.addEventListener('mousemove', l),
window.addEventListener('mouseup', r);
});
}
onDrop(e) {
var t;
e.preventDefault(), e.stopPropagation();
const n =
null === (t = e.dataTransfer) || void 0 === t
? void 0
: t.getData('component-type');
if (!n) return;
const s = b.createComponent(n);
if (!s) return;
const o = this.element.classList[2],
i = b.generateUniqueClass(n, !0, o);
s.classList.add(i);
const l = document.createElement('span');
(l.className = 'component-label'),
(l.textContent = i),
(s.id = i),
(l.style.display = 'none'),
s.appendChild(l),
s.addEventListener('mouseenter', e => this.showLabel(e, s)),
s.addEventListener('mouseleave', e => this.hideLabel(e, s)),
this.element.appendChild(s),
this.makeDraggable(s),
b.historyManager.captureState();
}
showLabel(e, t) {
e.stopPropagation();
const n = t.querySelector('.component-label');
n && (n.style.display = 'block');
}
hideLabel(e, t) {
e.stopPropagation();
const n = t.querySelector('.component-label');
n && (n.style.display = 'none');
}
onMouseOver(e) {
e.stopPropagation();
document.querySelectorAll('.container-highlight').forEach(e => {
e.classList.remove('container-highlight');
}),
e.target === this.element &&
this.element.classList.add('container-highlight');
}
onMouseLeave(e) {
e.target === this.element &&
this.element.classList.remove('container-highlight');
}
addStyles() {
const e = document.createElement('style');
(e.textContent =
'\n .container-component {\n position: relative !important;\n display: flex;\n min-width: 100px;\n min-height: 100px;\n cursor: grab;\n border: 1px solid #ddd;\n }\n\n .resizer {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: white;\n border: 2px solid #4286f4;\n position: absolute;\n }\n\n .resizer.top-left {\n left: -5px;\n top: -5px;\n cursor: nwse-resize;\n }\n\n .resizer.top-right {\n right: -5px;\n top: -5px;\n cursor: nesw-resize;\n }\n\n .resizer.bottom-left {\n left: -5px;\n bottom: -5px;\n cursor: nesw-resize;\n }\n\n .resizer.bottom-right {\n right: -5px;\n bottom: -5px;\n cursor: nwse-resize;\n }\n '),
document.head.appendChild(e);
}
create() {
return this.element;
}
static restoreResizer(e) {
const t = e.querySelector('.resizers');
t && t.remove();
const n = document.createElement('div');
n.classList.add('resizers');
const s = new l();
(s.element = e), (s.resizers = n), s.addResizeHandles(), e.appendChild(n);
}
static restoreContainer(e) {
l.restoreResizer(e);
const t = new l();
t.element = e;
e.querySelectorAll('.editable-component').forEach(e => {
var s;
if (
(b.controlsManager.addControlButtons(e),
b.addDraggableListeners(e),
e.addEventListener('mouseenter', n => t.showLabel(n, e)),
e.addEventListener('mouseleave', n => t.hideLabel(n, e)),
e.classList.contains('image-component'))
) {
const t =
(null === (s = e.querySelector('img')) || void 0 === s
? void 0
: s.getAttribute('src')) || '';
n.restoreImageUpload(e, t);
}
e.classList.contains('container-component') && this.restoreContainer(e);
});
}
}
class r {
constructor(e, t = `${e}Col-component`) {
(this.columnCount = e),
(this.element = document.createElement('div')),
this.element.classList.add(t),
this.element.setAttribute('draggable', 'true');
for (let t = 1; t <= e; t++) {
const e = this.createColumn(`column-${t}`);
this.element.appendChild(e);
}
this.addStyles(t), this.initializeEventListeners();
}
createColumn(e) {
const t = document.createElement('div');
return (
t.classList.add('column', e),
t.setAttribute('draggable', 'true'),
(t.style.width = 100 / this.columnCount + '%'),
t
);
}
initializeEventListeners() {
this.element.addEventListener('dragover', e => e.preventDefault()),
this.element.addEventListener('drop', this.onDrop.bind(this));
}
onDrop(e) {
var t;
e.preventDefault(), e.stopPropagation();
const n =
null === (t = e.dataTransfer) || void 0 === t
? void 0
: t.getData('component-type');
if (!n) return;
const s = b.createComponent(n);
if (!s) return;
const o = e.target;
if (o && o.classList.contains('column')) {
o.appendChild(s);
const e = `${this.element.id}-${`c${Array.from(o.parentElement.children).indexOf(o)}`}`;
(o.id = e), o.classList.add(e);
let t = o.querySelector('.column-label');
t ||
((t = document.createElement('span')),
(t.className = 'column-label'),
o.appendChild(t)),
(t.textContent = e);
const i = b.generateUniqueClass(n, !0, e);
s.classList.add(i), (s.id = i);
let l = s.querySelector('.component-label');
l ||
((l = document.createElement('span')),
(l.className = 'component-label'),
s.appendChild(l)),
(l.textContent = i),
b.historyManager.captureState();
}
}
addStyles(e) {
const t = document.createElement('style');
(t.textContent = `\n .${e} {\n display: flex;\n width: 97%;\n min-width: 100px;\n min-height: 100px;\n }\n .column {\n flex-grow: 1;\n min-width: 50px;\n border: 1px dashed #ddd;\n padding: 10px;\n position: relative;\n }\n .column:hover {\n outline: 1px solid #3498db;\n background: #f5f5f5;\n }\n `),
document.head.appendChild(t);
}
create() {
return this.element;
}
static restoreColumn(e) {
e.querySelectorAll('.editable-component').forEach(e => {
var t;
if (
(b.controlsManager.addControlButtons(e),
b.addDraggableListeners(e),
e.classList.contains('image-component'))
) {
const s =
(null === (t = e.querySelector('img')) || void 0 === t
? void 0
: t.getAttribute('src')) || '';
n.restoreImageUpload(e, s);
}
});
}
}
class a extends r {
constructor() {
super(2, 'twoCol-component');
}
}
class d extends r {
constructor() {
super(3, 'threeCol-component');
}
}
class c {
create(e, t, n = !1) {
const s = document.createElement('div');
s.classList.add('table-component');
const o = document.createElement('table');
(o.style.width = '100%'), (o.style.borderCollapse = 'collapse');
for (let n = 0; n < e; n++) {
const e = document.createElement('tr');
for (let s = 0; s < t; s++) {
const t = document.createElement('td');
(t.textContent = `R${n + 1}C${s + 1}`),
(t.style.border = '1px solid #000'),
(t.style.padding = '8px'),
e.appendChild(t);
}
o.appendChild(e);
}
if ((s.appendChild(o), !n)) {
const e = document.createElement('div');
e.classList.add('button-container'),
(e.style.marginTop = '10px'),
(e.style.display = 'flex'),
(e.style.gap = '10px');
const t = document.createElement('button');
(t.textContent = 'Add Row'),
t.addEventListener('click', () => this.addRow(o)),
e.appendChild(t);
const n = document.createElement('button');
(n.textContent = 'Add Column'),
n.addEventListener('click', () => this.addColumn(o)),
e.appendChild(n),
s.appendChild(e);
}
return s;
}
addRow(e) {
var t;
const n = e.rows.length,
s =
(null === (t = e.rows[0]) || void 0 === t ? void 0 : t.cells.length) ||
0,
o = document.createElement('tr');
for (let e = 0; e < s; e++) {
const t = document.createElement('td');
(t.textContent = `R${n + 1}C${e + 1}`),
(t.style.border = '1px solid #000'),
(t.style.padding = '8px'),
o.appendChild(t);
}
e.appendChild(o);
}
addColumn(e) {
const t = e.rows.length;
for (let n = 0; n < t; n++) {
const t = document.createElement('td');
(t.textContent = `R${n + 1}C${e.rows[n].cells.length + 1}`),
(t.style.border = '1px solid #000'),
(t.style.padding = '8px'),
e.rows[n].appendChild(t);
}
}
static restore(e) {
const t = new c(),
n = e.querySelector('table');
if (!n) return void console.error('No table found in container');
const s = e.querySelector('.button-container');
if (!s) return void console.error('No button container found');
s.querySelectorAll('button').forEach(e => {
var s;
const o = e.cloneNode(!0);
null === (s = e.parentNode) || void 0 === s || s.replaceChild(o, e),
'Add Row' === o.textContent
? o.addEventListener('click', () => t.addRow(n))
: 'Add Column' === o.textContent &&
o.addEventListener('click', () => t.addColumn(n));
});
}
}
class p {
constructor() {
(this.link = null), (this.isEditing = !1);
}
create(e = '#', t = 'Click Here') {
const n = document.createElement('div');
n.classList.add('link-component'),
(this.link = document.createElement('a')),
(this.link.href = e),
(this.link.innerText = t),
this.link.classList.add('link-component-label');
const s = document.createElement('button');
(s.innerHTML = '🖊️'), s.classList.add('edit-link');
const o = document.createElement('div');
o.classList.add('edit-link-form');
const i = document.createElement('input');
(i.type = 'url'), (i.value = e), (i.placeholder = 'Enter URL');
const l = document.createElement('input');
l.type = 'checkbox';
const r = document.createElement('label');
(r.innerHTML = 'Open in new tab'), r.appendChild(l);
const a = document.createElement('button');
return (
(a.innerHTML = 'Save'),
o.appendChild(i),
o.appendChild(r),
o.appendChild(a),
s.addEventListener('click', e => {
e.preventDefault(),
(this.isEditing = !0),
this.link && (this.link.style.display = 'none'),
(s.style.display = 'none'),
(o.style.display = 'flex');
}),
a.addEventListener('click', e => {
e.preventDefault(),
e.stopPropagation(),
(this.isEditing = !1),
this.link &&
((this.link.href = i.value),
(this.link.style.display = 'inline'),
(this.link.target = l.checked ? '_blank' : '_self')),
(s.style.display = 'inline-flex'),
(o.style.display = 'none');
}),
n.appendChild(this.link),
n.appendChild(s),
n.appendChild(o),
n
);
}
getLinkData() {
var e, t, n;
return {
href: (null === (e = this.link) || void 0 === e ? void 0 : e.href) || '#',
label:
(null === (t = this.link) || void 0 === t ? void 0 : t.innerText) ||
'Click Here',
target:
(null === (n = this.link) || void 0 === n ? void 0 : n.target) ||
'_self',
};
}
updateLink(e, t, n = '_self') {
this.link &&
((this.link.href = e), (this.link.innerText = t), (this.link.target = n));
}
isInEditMode() {
return this.isEditing;
}
static restore(e) {
var t, n;
const s = e.querySelector('.link-component-label'),
o = e.querySelector('.edit-link'),
i = e.querySelector('.edit-link-form'),
l = i.querySelector('button'),
r = i.querySelector('input[type="url"]'),
a = i.querySelector('input[type="checkbox"]');
if (!(s && o && i && l && r && a))
return void console.error('Required elements not found');
(s.style.display = 'inline'),
(o.style.display = 'inline-flex'),
(i.style.display = 'none');
const d = o.cloneNode(!0),
c = l.cloneNode(!0);
null === (t = o.parentNode) || void 0 === t || t.replaceChild(d, o),
null === (n = l.parentNode) || void 0 === n || n.replaceChild(c, l),
d.addEventListener('click', e => {
e.preventDefault(),
(s.style.display = 'none'),
(d.style.display = 'none'),
(i.style.display = 'flex');
}),
c.addEventListener('click', e => {
e.preventDefault(),
e.stopPropagation(),
(s.href = r.value),
(s.style.display = 'inline'),
(s.target = a.checked ? '_blank' : '_self'),
(d.style.display = 'inline-flex'),
(i.style.display = 'none');
});
}
}
class h {
create() {
const e = e => {
let t,
n,
s,
o,
i = !1,
l = !1,
r = 0,
a = 0;
(e.style.position = 'relative'),
(e.style.cursor = 'move'),
e.addEventListener('mousedown', s => {
l ||
((i = !0),
(t = s.clientX),
(n = s.clientY),
(r = parseFloat(e.getAttribute('data-x') || '0')),
(a = parseFloat(e.getAttribute('data-y') || '0')),
document.addEventListener('mousemove', d),
document.addEventListener('mouseup', c));
});
const d = s => {
if (i) {
const o = s.clientX - t,
i = s.clientY - n,
l = r + o,
d = a + i;
(e.style.transform = `translate(${l}px, ${d}px)`),
e.setAttribute('data-x', l.toString()),
e.setAttribute('data-y', d.toString());
}
},
c = () => {
(i = !1),
document.removeEventListener('mousemove', d),
document.removeEventListener('mouseup', c);
};
if (e.classList.contains('container')) {
const i = document.createElement('div');
Object.assign(i.style, {
width: '10px',
height: '10px',
background: 'blue',
position: 'absolute',
right: '0',
bottom: '0',
cursor: 'se-resize',
}),
e.appendChild(i),
i.addEventListener('mousedown', i => {
i.stopPropagation(),
(l = !0),
(s = e.offsetWidth),
(o = e.offsetHeight),
(t = i.clientX),
(n = i.clientY),
document.addEventListener('mousemove', r),
document.addEventListener('mouseup', a);
});
const r = i => {
if (l) {
const l = s + (i.clientX - t),
r = o + (i.clientY - n);
(e.style.width = `${l}px`), (e.style.height = `${r}px`);
}
},
a = () => {
(l = !1),
document.removeEventListener('mousemove', r),
document.removeEventListener('mouseup', a);
};
}
},
n = new l().create();
n.classList.add('container'),
Object.assign(n.style, {
width: '100%',
maxWidth: 'none',
margin: '0 auto',
padding: '20px',
fontFamily: "'Roboto', sans-serif",
}),
e(n);
const s = new l().create();
s.classList.add('container'),
Object.assign(s.style, {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: '40px',
width: '100%',
}),
e(s);
const i = new t('MyBrand').create();
Object.assign(i.style, {
fontSize: '24px',
fontWeight: 'bold',
color: '#333',
});
const r = new l().create();
r.classList.add('container'),
Object.assign(r.style, { display: 'flex', gap: '20px' }),
e(r),
['Home', 'Features', 'Contact'].forEach(e => {
const n = new t(e).create();
Object.assign(n.style, {
cursor: 'pointer',
color: '#555',
textDecoration: 'none',
}),
r.appendChild(n);
}),
s.appendChild(i),
s.appendChild(r);
const a = new l().create();
a.classList.add('container'),
Object.assign(a.style, {
textAlign: 'center',
padding: '60px 20px',
backgroundColor: '#f9f9f9',
borderRadius: '10px',
marginBottom: '40px',
}),
e(a);
const d = new t('Welcome to My Landing Page').create();
Object.assign(d.style, {
textAlign: 'center',
padding: '60px 20px',
backgroundColor: '#f9f9f9',
borderRadius: '10px',
marginBottom: '40px',
width: '100%',
});
const c = new t(
'Discover amazing features and build better products with us.'
).create();
Object.assign(c.style, {
fontSize: '18px',
color: '#666',
marginBottom: '30px',
});
const p = new o().create();
Object.assign(p.style, {
padding: '12px 24px',
fontSize: '16px',
color: '#fff',
backgroundColor: '#007bff',
border: 'none',
borderRadius: '5px',
cursor: 'pointer',
transition: 'background-color 0.3s',
}),
p.addEventListener('mouseenter', () => {
p.style.backgroundColor = '#0056b3';
}),
p.addEventListener('mouseleave', () => {
p.style.backgroundColor = '#007bff';
}),
a.appendChild(d),
a.appendChild(c),
a.appendChild(p);
const h = new l().create();
h.classList.add('container'),
Object.assign(h.style, {
textAlign: 'center',
padding: '20px',
marginTop: '40px',
borderTop: '1px solid #ddd',
}),
e(h);
const u = new t('© 2025 MyBrand. All rights reserved.').create();
return (
Object.assign(u.style, { fontSize: '14px', color: '#999' }),
h.appendChild(u),
n.appendChild(s),
n.appendChild(a),
n.appendChild(h),
n
);
}
}
class u {
constructor(e) {
(this.undoStack = []), (this.redoStack = []), (this.canvas = e);
}
captureState() {
const e = b.getState();
if (e.length > 0) {
const t = this.undoStack[this.undoStack.length - 1];
JSON.stringify(e) !== JSON.stringify(t) &&
(this.undoStack.push(e),
this.undoStack.length > 20 && this.undoStack.shift(),
(this.redoStack = []));
} else console.warn('No valid state to capture.');
}
undo() {
if (this.undoStack.length > 1) {
const e = this.undoStack.pop();
this.redoStack.push(e);
const t = this.undoStack[this.undoStack.length - 1];
b.restoreState(t);
} else if (1 === this.undoStack.length) {
const e = this.undoStack.pop();
this.redoStack.push(e);
const t = b.jsonStorage.load();
t ? b.restoreState(t) : b.restoreState([]);
} else console.warn('No more actions to undo.');
}
redo() {
if (this.redoStack.length > 0) {
const e = this.redoStack.pop();
this.undoStack.push(e), b.restoreState(e);
} else console.warn('No more actions to redo.');
}
}
class m {
save(e) {
localStorage.setItem('pageLayout', JSON.stringify(e));
}
load() {
const e = localStorage.getItem('pageLayout');
return e ? JSON.parse(e) : null;
}
remove() {
localStorage.removeItem('pageLayout');
}
}
class g {
constructor(e) {
(this.icons = {
delete:
'https://res.cloudinary.com/dodvwsaqj/image/upload/v1737366522/delete-2-svgrepo-com_fwkzn7.svg',
}),
(this.canvas = e);
}
addControlButtons(e) {
let t = e.querySelector('img'),
n = e.querySelector('.component-controls');
n ||
((n = document.createElement('div')),
(n.className = 'component-controls'),
t ? e.appendChild(n) : e.prepend(n));
const s = this.createDeleteIcon(e);
n.appendChild(s);
}
createDeleteIcon(e) {
let t = e.querySelector('.delete-icon');
return (
t ||
((t = document.createElement('img')),
(t.src = this.icons.delete),
(t.alt = 'Delete'),
t.classList.add('delete-icon'),
e.appendChild(t)),
(t.onclick = t => {
t.stopPropagation(), this.handleDelete(e);
}),
t
);
}
handleDelete(e) {
this.canvas.historyManager.captureState(), e.remove();
const t = this.canvas.getComponents().filter(t => t !== e);
this.canvas.setComponents(t), this.canvas.historyManager.captureState();
}
}
function y(e) {
const t = document.getElementById('notification');
t &&
((t.innerHTML = e),
t.classList.add('visible'),
t.classList.remove('hidden'),
setTimeout(() => {
t.classList.remove('visible'), t.classList.add('hidden');
}, 2e3));
}
class v {
constructor(e) {
(this.canvas = e),
(this.styleElement = document.createElement('style')),
document.head.appendChild(this.styleElement);
}
generateHTML() {
const e = document.getElementById('canvas');
if (!e)
return console.warn('Canvas element not found!'), this.getBaseHTML();
const t = e.cloneNode(!0);
return this.cleanupElements(t), this.getBaseHTML(t.innerHTML);
}
getBaseHTML(e = 'children') {
return `<!DOCTYPE html>\n<html>\n<head>\n <meta charset="UTF-8" />\n <meta name="viewport" content="width=device-width, initial-scale=1.0" />\n <title>Page Builder</title>\n <style>\n ${this.generateCSS()}\n </style>\n </head>\n <body>\n <div id="page" class="home">\n ${e}\n </div>\n </body>\n </html>`;
}
cleanupElements(e) {
const t = ['contenteditable', 'draggable', 'style'],
n = [
'component-controls',
'delete-icon',
'component-label',
'column-label',
'editable-component',
'resizers',
'resizer',
'upload-btn',
'component-resizer',
'drop-preview',
'edit-link-form',
'edit-link',
];
Array.from(e.children).forEach(s => {
const o = s;
t.forEach(e => {
o.removeAttribute(e);
}),
n.forEach(e => {
o.classList.remove(e);
});
e.querySelectorAll('input').forEach(e => e.remove());
o
.querySelectorAll(
'.component-controls, .delete-icon, .component-label,.column-label, .resizers, .resizer, .drop-preview, .upload-btn, component-resizer,.edit-link, .edit-link-form'
)
.forEach(e => e.remove()),
o.children.length > 0 && this.cleanupElements(o);
});
}
generateCSS() {
const e = document.getElementById('canvas');
if (!e) return '';
const t = e
? window.getComputedStyle(e).getPropertyValue('background-color')
: 'rgb(255, 255, 255)',
n = [];
n.push(
`\n body, html {\n margin: 0;\n padding: 0;\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n }\n .home {\n display: flex;\n justify-content: center;\n align-items: center;\n width: 100%;\n min-height:100vh;\n background-color: ${t};\n margin: 0;\n }\n `
);
const s = e.querySelectorAll('*'),
o = [
'position',
'top',
'left',
'right',
'bottom',
'width',
'height',
'min-width',
'min-height',
'max-width',
'max-height',
'margin',
'padding',
'background-color',
'background-image',
'border',
'border-radius',
'transform',
'opacity',
'z-index',
'display',
'flex-direction',
'justify-content',
'align-items',
'flex-wrap',
'font-size',
'font-weight',
'color',
'text-align',
'line-height',
'font-family',
],
i = [
'component-controls',
'delete-icon',
'component-label',
'resizers',
'resizer',
'upload-btn',
'edit-link-form',
'edit-link',
];
return (
s.forEach(e => {
if (i.some(t => e.classList.contains(t))) return;
const t = window.getComputedStyle(e),
s = [];
o.forEach(e => {
const n = t.getPropertyValue(e);
if (n && 'none' !== n && '' !== n) {
if ('resize' === e) return;
s.push(`${e}: ${n};`);
}
});
const l = this.generateUniqueSelector(e);
s.length > 0 &&
n.push(`\n ${l} {\n ${s.join('\n ')}\n }`);
}),
n.join('\n')
);
}
generateUniqueSelector(e) {
if (e.id) return `#${e.id}`;
if (e.className) return `.${e.className.split(' ').join('.')}`;
const t = e.parentElement;
if (t) {
const n = Array.from(t.children).indexOf(e);
return `${e.tagName.toLowerCase()}:nth-child(${n + 1})`;
}
return e.tagName.toLowerCase();
}
applyCSS(e) {
this.styleElement.textContent = e;
}
}
class w {
constructor(e = '#layers-view', t = '#page') {
this.initializeElements(e, t);
}
initializeElements(e, t) {
(w.layersView = document.querySelector(e)),
w.layersView ||
((w.layersView = document.createElement('div')),
(w.layersView.id = 'layers-view'),
(w.layersView.className = 'layers-view'),
document.body.appendChild(w.layersView),
console.warn(`Layers view element created: ${e}`)),
(w.canvasRoot = document.querySelector(t)),
w.canvasRoot ||
(console.error(`Canvas root element not found: ${t}`),
(w.canvasRoot = document.body));
}
static buildLayerHierarchyFromDOM(e) {
const t = new v(new b()).generateHTML(),
n = new DOMParser().parseFromString(t, 'text/html'),
s = (e, t = 0) => {
var n;
const o = e;
if (!o.id) return null;
const i = {
id: o.id,
isVisible:
'none' !==
(null === (n = o.style) || void 0 === n ? void 0 : n.display),
isLocked: 'true' === o.getAttribute('data-locked'),
depth: t,
children: [],
};
return (
Array.from(e.children).forEach(e => {
const n = s(e, t + 1);
n && i.children.push(n);
}),
i
);
};
return Array.from(n.body.children)
.map(e => s(e))
.filter(e => null !== e);
}
static updateLayersView() {
if (!this.layersView || !this.canvasRoot)
return void console.error('Layers view or canvas root not initialized');
this.layersView.innerHTML = '';
const e = this.buildLayerHierarchyFromDOM(this.canvasRoot),
t = document.createElement('ul');
(t.className = 'layers-list'),
this.renderLayerItems(t, e),
this.layersView.appendChild(t);
}
static renderLayerItems(e, t, n = 0) {
const s = document.createElement('ul');
(s.className = 'layer-list'),
e.appendChild(s),
t.forEach(e => {
const t = document.createElement('li');
(t.className = 'layer-item-container'), s.appendChild(t);
const o = this.createLayerItemElement(e);
if (
((o.style.paddingLeft = 12 * n + 'px'),
t.appendChild(o),
e.children && e.children.length > 0)
) {
const s = document.createElement('span');
(s.className = 'layer-expand-toggle'),
(s.textContent = '▶'),
o.insertBefore(s, o.firstChild);
const i = document.createElement('div');
(i.className = 'child-container'),
(i.style.display = 'none'),
t.appendChild(i),
this.renderLayerItems(i, e.children, n + 1),
s.addEventListener('click', () => {
'block' === i.style.display
? ((i.style.display = 'none'), (s.textContent = '▶'))
: ((i.style.display = 'block'), (s.textContent = '▼'));
});
}
});
}
static createLayerItemElement(e) {
const t = document.createElement('li');
(t.className = 'layer-item'), (t.dataset.layerId = e.id);
const n = document.createElement('span');
(n.className = 'layer-visibility'),
(n.innerHTML = e.isVisible ? '👁️' : '👁️🗨️'),
n.addEventListener('click', () => this.toggleLayerVisibility(e));
const s = document.createElement('span');
(s.className = 'layer-name'),
(s.textContent = `${e.id}`),
s.addEventListener('click', () => this.selectLayer(e));
const o = document.createElement('span');
return (
(o.className = 'layer-lock'),
(o.innerHTML = e.isLocked ? '🔒' : '🔓'),
o.addEventListener('click', () => this.toggleLayerLock(e)),
(t.draggable = !0),
t.addEventListener('dragstart', t => this.handleDragStart(t, e)),
t.addEventListener('dragover', this.handleDragOver),
t.addEventListener('drop', t => this.handleDrop(t, e)),
t.appendChild(n),
t.appendChild(s),
t.appendChild(o),
t
);
}
static toggleLayerVisibility(e) {
const t = document.getElementById(e.id);
t &&
('none' === t.style.display
? ((t.style.display = t.dataset.originalDisplay || ''),
(e.isVisible = !0))
: ((t.dataset.originalDisplay = t.style.display),
(t.style.display = 'none'),
(e.isVisible = !1)),
this.updateLayersView());
}
static toggleLayerLock(e) {
const t = document.getElementById(e.id);
t &&
((e.isLocked = !e.isLocked),
e.isLocked
? (t.setAttribute('data-locked', 'true'),
(t.style.pointerEvents = 'none'))
: (t.removeAttribute('data-locked'), (t.style.pointerEvents = 'auto')),
this.updateLayersView());
}
static selectLayer(e) {
this.switchToCustomizeMode(e.id);
}
static handleDragStart(e, t) {
e.dataTransfer &&
(e.dataTransfer.setData('text/plain', t.id),
(this.draggedItem = e.target));
}
static handleDragOver(e) {
e.preventDefault(), e.stopPropagation();
}
static handleDrop(e, t) {
var n;
if ((e.preventDefault(), e.stopPropagation(), !e.dataTransfer)) return;
const s = parseInt(
(null === (n = e.dataTransfer) || void 0 === n
? void 0
: n.getData('text/plain')) || '-1'
),
o = parseInt(t.id || '-1');
b.reorderComponent(s, o), this.updateLayersView();
}
static switchToCustomizeMode(e) {
const t = document.getElementById('customize-sidebar');
t && ((t.style.display = 'block'), this.loadLayerProperties(e));
}
static loadLayerProperties(e) {
const t = document.getElementById(e);
if (!t) return;
const n = document.getElementById('layer-properties');
n &&
(n.innerHTML = `\n <h3>Layer Properties: ${e}</h3>\n <div>Visibility: ${'none' !== t.style.display ? 'Visible' : 'Hidden'}</div>\n <div>Locked: ${'true' === t.getAttribute('data-locked') ? 'Yes' : 'No'}</div>\n `);
}
}
(w.layersView = null), (w.canvasRoot = null), (w.draggedItem = null);
class C {
static init() {
if (
((this.sidebarElement = document.getElementById('customization')),
(this.controlsContainer = document.getElementById('controls')),
(this.componentNameHeader = document.getElementById('component-name')),
(this.closeButton = document.createElement('button')),
!this.sidebarElement || !this.controlsContainer)
)
return void console.error(
'CustomizationSidebar: Required elements not found.'
);
(this.layersViewController = new w()),
(this.layersModeToggle = document.createElement('div')),
(this.layersModeToggle.className = 'layers-mode-toggle'),
(this.layersModeToggle.innerHTML =
'\n <button id="customize-tab" title="Customize" class="active">⚙️</button>\n <button id="layers-tab" title="Layers"> ☰ </button>\n '),
this.sidebarElement.insertBefore(
this.layersModeToggle,
this.componentNameHeader
),
(this.layersView = document.createElement('div')),
(this.layersView.id = 'layers-view'),
(this.layersView.className = 'layers-view hidden'),
this.controlsContainer.appendChild(this.layersView);
const e = this.layersModeToggle.querySelector('#customize-tab'),
t = this.layersModeToggle.querySelector('#layers-tab');
e.addEventListener('click', () => this.switchToCustomizeMode()),
t.addEventListener('click', () => this.switchToLayersMode()),
this.sidebarElement.appendChild(this.closeButton),
(this.closeButton.textContent = '×'),
this.closeButton.classList.add('close-button'),
this.closeButton.addEventListener('click', () => {
this.hideSidebar();
});
}
static switchToCustomizeMode() {
const e = document.getElementById('customize-tab'),
t = document.getElementById('layers-tab'),
n = document.getElementById('layers-view'),
s = document.getElementById('controls'),
o = document.getElementById('component-name');
e.classList.add('active'),
t.classList.remove('active'),
n.classList.add('hidden'),
s.classList.remove('hidden'),
(s.style.display = 'block'),
(n.style.display = 'none'),
(o.style.display = 'block');
}
static switchToLayersMode() {
const e = document.getElementById('customize-tab'),
t = document.getElementById('layers-tab'),
n = document.getElementById('layers-view'),
s = document.getElementById('controls'),
o = document.getElementById('component-name');
t.classList.add('active'),
e.classList.remove('active'),
(s.style.display = 'none'),
(n.style.display = 'block'),
(o.style.display = 'none'),
w.updateLayersView();
}
static updateLayersView() {
w.updateLayersView();
}
static showSidebar(e) {
const t = document.getElementById('customize-tab'),
n = document.getElementById('layers-tab'),
s = document.getElementById('layers-view'),
o = document.getElementById('controls');
t.classList.add('active'),
n.classList.remove('active'),
s.classList.add('hidden'),
o.classList.remove('hidden');
const i = document.getElementById(e);
if ((console.log(`Showing sidebar for: ${e}`), !i))
return void console.error(`Component with ID "${e}" not found.`);
const l = 'canvas' === e.toLowerCase();
(this.sidebarElement.style.display = 'block'),
(this.controlsContainer.innerHTML = ''),
(this.componentNameHeader.textContent = `Component: ${e}`);
const r = getComputedStyle(i);
this.createSelectControl('Display', 'display', r.display || 'block', [
'block',
'inline',
'inline-block',
'flex',
'grid',
'none',
]),
l ||
(this.createControl('Width', 'width', 'number', i.offsetWidth, {
min: 0,
max: 1e3,
unit: 'px',
}),
this.createControl('Height', 'height', 'number', i.offsetHeight, {
min: 0,
max: 1e3,
unit: 'px',
}),
this.createControl(
'Margin',
'margin',
'number',
parseInt(r.margin) || 0,
{ min: 0, max: 1e3, unit: 'px' }
),
this.createControl(
'Padding',
'padding',
'number',
parseInt(r.padding) || 0,
{ min: 0, max: 1e3, unit: 'px' }
)),
this.createControl('Color', 'color', 'color', r.backgroundColor),
this.createSelectControl('Text Alignment', 'alignment', r.textAlign, [
'left',
'center',
'right',
]),
this.createSelectControl('Font Family', 'font-family', r.fontFamily, [
'Arial',
'Verdana',
'Helvetica',
'Times New Roman',
'Georgia',
'Courier New',
'sans-serif',
'serif',
]),
this.createControl(
'Font Size',
'font-size',
'number',
parseInt(r.fontSize) || 16,
{ min: 0, max: 100, unit: 'px' }
),
this.createControl(
'Text Color',
'text-color',
'color',
r.color || '#000000'
),
this.createControl(
'Border Width',
'border-width',
'number',
parseInt(r.borderWidth) || 0,
{ min: 0, max: 20, unit: 'px' }
),
this.createSelectControl(
'Border Style',
'border-style',
r.borderStyle || 'none',
[
'none',
'solid',
'dashed',
'dotted',
'double',
'groove',
'ridge',
'inset',
'outset',
]
),
this.createControl(
'Border Color',
'border-color',
'color',
r.borderColor || '#000000'
);
const a = C.rgbToHex(r.backgroundColor),
d = document.getElementById('color');
d && (d.value = a), this.addListeners(i);
}
static hideSidebar() {
this.sidebarElement && (this.sidebarElement.style.display = 'none');
}
static rgbToHex(e) {
const t = e.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+\.?\d*))?\)$/);
if (!t) return e;
return `#${((1 << 24) | (parseInt(t[1], 10) << 16) | (parseInt(t[2], 10) << 8) | parseInt(t[3], 10)).toString(16).slice(1).toUpperCase()}`;
}
static createControl(e, t, n, s, o = {}) {
const i = document.createElement('div');
i.classList.add('control-wrapper');
if ('number' === n && o.unit) {
const l = o.unit;
i.innerHTML = `\n <label for="${t}">${e}:</label>\n <input type="${n}" id="${t}" value="${s}">\n <select id="${t}-unit">\n <option value="px" ${'px' === l ? 'selected' : ''}>px</option>\n <option value="rem" ${'rem' === l ? 'selected' : ''}>rem</option>\n <option value="vh" ${'vh' === l ? 'selected' : ''}>vh</option>\n <option value="%" ${'%' === l ? 'selected' : ''}>%</option>\n </select>\n `;
} else
i.innerHTML = `\n <label for="${t}">${e}:</label>\n <input type="color" id="${t}" value="${s}">\n <input type="text" id="color-value" style="font-size: 0.8rem; width: 80px; margin-left: 8px;" value="${s}">\n `;
const l = i.querySelector('input'),
r = i.querySelector(`#${t}-unit`);
l &&