@mindfiredigital/page-builder
Version:
439 lines (438 loc) • 16.4 kB
JavaScript
import { Canvas } from '../canvas/Canvas.js';
import { debounce } from '../utils/utilityFunctions.js';
import LayersViewController from './LayerViewController.js';
export class CustomizationSidebar {
static init() {
this.sidebarElement = document.getElementById('customization');
this.controlsContainer = document.getElementById('controls');
this.componentNameHeader = document.getElementById('component-name');
this.closeButton = document.createElement('button');
if (!this.sidebarElement || !this.controlsContainer) {
console.error('CustomizationSidebar: Required elements not found.');
return;
}
// Initialize LayersViewController
this.layersViewController = new LayersViewController();
// Create layers mode toggle
this.layersModeToggle = document.createElement('div');
this.layersModeToggle.className = 'layers-mode-toggle';
this.layersModeToggle.innerHTML = `
<button id="customize-tab" title="Customize" class="active">⚙️</button>
<button id="layers-tab" title="Layers"> ☰ </button>
`;
this.sidebarElement.insertBefore(
this.layersModeToggle,
this.componentNameHeader
);
// Create layers view
this.layersView = document.createElement('div');
this.layersView.id = 'layers-view';
this.layersView.className = 'layers-view hidden';
this.controlsContainer.appendChild(this.layersView);
// Add event listeners for tab switching
const customizeTab = this.layersModeToggle.querySelector('#customize-tab');
const layersTab = this.layersModeToggle.querySelector('#layers-tab');
customizeTab.addEventListener('click', () => this.switchToCustomizeMode());
layersTab.addEventListener('click', () => this.switchToLayersMode());
// Add the close button to the sidebar
this.sidebarElement.appendChild(this.closeButton);
this.closeButton.textContent = '×'; // Close button symbol
this.closeButton.classList.add('close-button');
// Add the event listener to hide the sidebar when the close button is clicked
this.closeButton.addEventListener('click', () => {
this.hideSidebar();
});
}
static switchToCustomizeMode() {
const customizeTab = document.getElementById('customize-tab');
const layersTab = document.getElementById('layers-tab');
const layersView = document.getElementById('layers-view');
const controlsContainer = document.getElementById('controls');
const componentName = document.getElementById('component-name');
customizeTab.classList.add('active');
layersTab.classList.remove('active');
layersView.classList.add('hidden');
controlsContainer.classList.remove('hidden');
// Ensure only the control view is visible
controlsContainer.style.display = 'block'; // show the controls
layersView.style.display = 'none';
componentName.style.display = 'block';
}
static switchToLayersMode() {
const customizeTab = document.getElementById('customize-tab');
const layersTab = document.getElementById('layers-tab');
const layersView = document.getElementById('layers-view');
const controlsContainer = document.getElementById('controls');
const componentName = document.getElementById('component-name');
layersTab.classList.add('active');
customizeTab.classList.remove('active');
// Ensure only the layers view is visible
controlsContainer.style.display = 'none'; // Hides the controls
layersView.style.display = 'block';
componentName.style.display = 'none';
// Update the layers view using the new LayersViewController
LayersViewController.updateLayersView();
}
static updateLayersView() {
LayersViewController.updateLayersView();
}
static showSidebar(componentId) {
const customizeTab = document.getElementById('customize-tab');
const layersTab = document.getElementById('layers-tab');
const layersView = document.getElementById('layers-view');
const controlsContainer = document.getElementById('controls');
// Ensure we're in customize mode when showing sidebar
customizeTab.classList.add('active');
layersTab.classList.remove('active');
layersView.classList.add('hidden');
controlsContainer.classList.remove('hidden');
// Existing showSidebar logic follows...
const component = document.getElementById(componentId);
console.log(`Showing sidebar for: ${componentId}`);
if (!component) {
console.error(`Component with ID "${componentId}" not found.`);
return;
}
// Check if the component is a canvas itself
const isCanvas = componentId.toLowerCase() === 'canvas';
this.sidebarElement.style.display = 'block';
this.controlsContainer.innerHTML = '';
// Set the component name in the header
this.componentNameHeader.textContent = `Component: ${componentId}`;
// Dynamically create controls
const styles = getComputedStyle(component);
// Controls Display Control
this.createSelectControl('Display', 'display', styles.display || 'block', [
'block',
'inline',
'inline-block',
'flex',
'grid',
'none',
]);
// Exclude some controls for canvas
if (!isCanvas) {
this.createControl('Width', 'width', 'number', component.offsetWidth, {
min: 0,
max: 1000,
unit: 'px',
});
this.createControl('Height', 'height', 'number', component.offsetHeight, {
min: 0,
max: 1000,
unit: 'px',
});
this.createControl(
'Margin',
'margin',
'number',
parseInt(styles.margin) || 0,
{
min: 0,
max: 1000,
unit: 'px',
}
);
this.createControl(
'Padding',
'padding',
'number',
parseInt(styles.padding) || 0,
{
min: 0,
max: 1000,
unit: 'px',
}
);
}
this.createControl('Color', 'color', 'color', styles.backgroundColor);
this.createSelectControl('Text Alignment', 'alignment', styles.textAlign, [
'left',
'center',
'right',
]);
// Controls for fonts
this.createSelectControl('Font Family', 'font-family', styles.fontFamily, [
'Arial',
'Verdana',
'Helvetica',
'Times New Roman',
'Georgia',
'Courier New',
'sans-serif',
'serif',
]);
this.createControl(
'Font Size',
'font-size',
'number',
parseInt(styles.fontSize) || 16,
{
min: 0,
max: 100,
unit: 'px',
}
);
// Controls for text color editing
this.createControl(
'Text Color',
'text-color',
'color',
styles.color || '#000000'
);
// Controls for border width
this.createControl(
'Border Width',
'border-width',
'number',
parseInt(styles.borderWidth) || 0,
{
min: 0,
max: 20,
unit: 'px',
}
);
// Controls for Border Style
this.createSelectControl(
'Border Style',
'border-style',
styles.borderStyle || 'none',
[
'none',
'solid',
'dashed',
'dotted',
'double',
'groove',
'ridge',
'inset',
'outset',
]
);
// Controls for Border Color
this.createControl(
'Border Color',
'border-color',
'color',
styles.borderColor || '#000000'
);
// Convert the background color to hex format
const colorHex = CustomizationSidebar.rgbToHex(styles.backgroundColor);
// Set the color input to the component's background color in hex format
const colorInput = document.getElementById('color');
if (colorInput) {
colorInput.value = colorHex; // Set to valid hex color value
}
// Add event listeners for live updates
this.addListeners(component);
}
static hideSidebar() {
if (this.sidebarElement) {
this.sidebarElement.style.display = 'none';
}
}
static rgbToHex(rgb) {
const result = rgb.match(
/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+\.?\d*))?\)$/
);
if (!result) return rgb; // If the format is not matched, return the original string
const r = parseInt(result[1], 10);
const g = parseInt(result[2], 10);
const b = parseInt(result[3], 10);
// Ensure it's in the correct hex format
return `#${((1 << 24) | (r << 16) | (g << 8) | b).toString(16).slice(1).toUpperCase()}`;
}
static createControl(label, id, type, value, attributes = {}) {
const wrapper = document.createElement('div');
wrapper.classList.add('control-wrapper');
// Check if the control is a color input or a number input
const isNumber = type === 'number';
// Format value for number inputs and add a unit dropdown
if (isNumber && attributes.unit) {
const unit = attributes.unit;
wrapper.innerHTML = `
<label for="${id}">${label}:</label>
<input type="${type}" id="${id}" value="${value}">
<select id="${id}-unit">
<option value="px" ${unit === 'px' ? 'selected' : ''}>px</option>
<option value="rem" ${unit === 'rem' ? 'selected' : ''}>rem</option>
<option value="vh" ${unit === 'vh' ? 'selected' : ''}>vh</option>
<option value="%" ${unit === '%' ? 'selected' : ''}>%</option>
</select>
`;
} else {
wrapper.innerHTML = `
<label for="${id}">${label}:</label>
<input type="color" id="${id}" value="${value}">
<input type="text" id="color-value" style="font-size: 0.8rem; width: 80px; margin-left: 8px;" value="${value}">
`;
}
const input = wrapper.querySelector('input');
const unitSelect = wrapper.querySelector(`#${id}-unit`);
if (input) {
Object.keys(attributes).forEach(key => {
input.setAttribute(key, attributes[key].toString());
});
}
// If it's a color input, update the hex code display
const colorinput = wrapper.querySelector('input[type="color"]');
const hexInput = wrapper.querySelector('#color-value');
if (colorinput) {
colorinput.addEventListener('input', () => {
if (hexInput) {
hexInput.value = colorinput.value; // Update hex code display
}
});
}
if (hexInput) {
hexInput.addEventListener('input', () => {
if (colorinput) {
colorinput.value = hexInput.value; // Update color input with the new hex code
}
});
}
this.controlsContainer.appendChild(wrapper);
// Update value dynamically when unit changes
if (unitSelect) {
unitSelect.addEventListener('change', () => {
const unit = unitSelect.value;
const currentValue = parseInt(input.value);
input.value = `${currentValue}${unit}`;
});
}
}
static createSelectControl(label, id, currentValue, options) {
const wrapper = document.createElement('div');
wrapper.classList.add('control-wrapper');
const selectOptions = options
.map(
option =>
`<option value="${option}" ${option === currentValue ? 'selected' : ''}>${option}</option>`
)
.join('');
wrapper.innerHTML = `
<label for="${id}">${label}:</label>
<select id="${id}">${selectOptions}</select>
`;
this.controlsContainer.appendChild(wrapper);
}
static addListeners(component) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
const controls = {
width: document.getElementById('width'),
height: document.getElementById('height'),
color: document.getElementById('color'),
margin: document.getElementById('margin'),
padding: document.getElementById('padding'),
alignment: document.getElementById('alignment'),
fontSize: document.getElementById('font-size'),
textColor: document.getElementById('text-color'),
borderWidth: document.getElementById('border-width'),
borderStyle: document.getElementById('border-style'),
borderColor: document.getElementById('border-color'),
display: document.getElementById('display'),
fontFamily: document.getElementById('font-family'),
};
if (!controls) return;
const captureStateDebounced = debounce(() => {
Canvas.historyManager.captureState();
}, 300);
(_a = controls.width) === null || _a === void 0
? void 0
: _a.addEventListener('input', () => {
const unit = document.getElementById('width-unit').value;
component.style.width = `${controls.width.value}${unit}`;
captureStateDebounced();
});
(_b = controls.height) === null || _b === void 0
? void 0
: _b.addEventListener('input', () => {
const unit = document.getElementById('height-unit').value;
component.style.height = `${controls.height.value}${unit}`;
captureStateDebounced();
});
(_c = controls.color) === null || _c === void 0
? void 0
: _c.addEventListener('input', () => {
component.style.backgroundColor = controls.color.value;
const colorValueSpan = document.querySelector('#color-value');
if (colorValueSpan) {
colorValueSpan.textContent = controls.color.value; // Update color hex code display
}
captureStateDebounced();
});
(_d = controls.margin) === null || _d === void 0
? void 0
: _d.addEventListener('input', () => {
const unit = document.getElementById('margin-unit').value;
component.style.margin = `${controls.margin.value}${unit}`;
captureStateDebounced();
});
(_e = controls.padding) === null || _e === void 0
? void 0
: _e.addEventListener('input', () => {
const unit = document.getElementById('padding-unit').value;
component.style.padding = `${controls.padding.value}${unit}`;
captureStateDebounced();
});
(_f = controls.alignment) === null || _f === void 0
? void 0
: _f.addEventListener('change', () => {
component.style.textAlign = controls.alignment.value;
captureStateDebounced();
});
(_g = controls.fontSize) === null || _g === void 0
? void 0
: _g.addEventListener('input', () => {
const unit = document.getElementById('font-size-unit').value;
component.style.fontSize = `${controls.fontSize.value}${unit}`;
captureStateDebounced();
});
//Controls for editing text color
(_h = controls.textColor) === null || _h === void 0
? void 0
: _h.addEventListener('input', () => {
component.style.color = controls.textColor.value;
captureStateDebounced();
});
//Controls for editing border width
(_j = controls.borderWidth) === null || _j === void 0
? void 0
: _j.addEventListener('input', () => {
const unit = document.getElementById('border-width-unit').value;
component.style.borderWidth = `${controls.borderWidth.value}${unit}`;
captureStateDebounced();
});
//Controls for border style
(_k = controls.borderStyle) === null || _k === void 0
? void 0
: _k.addEventListener('change', () => {
component.style.borderStyle = controls.borderStyle.value;
captureStateDebounced();
});
//Controls for border color
(_l = controls.borderColor) === null || _l === void 0
? void 0
: _l.addEventListener('input', () => {
component.style.borderColor = controls.borderColor.value;
captureStateDebounced();
});
//Controls for display edit
(_m = controls.display) === null || _m === void 0
? void 0
: _m.addEventListener('change', () => {
component.style.display = controls.display.value;
captureStateDebounced();
});
//Controls for fonts
(_o = controls.fontFamily) === null || _o === void 0
? void 0
: _o.addEventListener('change', () => {
component.style.fontFamily = controls.fontFamily.value;
captureStateDebounced();
});
}
static getLayersViewController() {
return this.layersViewController;
}
}