UNPKG

@mindfiredigital/page-builder

Version:
1,475 lines (1,474 loc) 105 kB
'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 &&