@mindfiredigital/page-builder
Version:
280 lines (279 loc) • 10.2 kB
JavaScript
import { Canvas } from '../canvas/Canvas.js'; // Assuming a Canvas management class
import { HTMLGenerator } from '../services/HTMLGenerator.js';
class LayersViewController {
constructor(
layersViewSelector = '#layers-view',
canvasRootSelector = '#page'
) {
// Ensure elements exist before assignment
this.initializeElements(layersViewSelector, canvasRootSelector);
}
/**
* Initialize layers view and canvas root elements
*/
initializeElements(layersViewSelector, canvasRootSelector) {
// Try to find layers view
LayersViewController.layersView =
document.querySelector(layersViewSelector);
if (!LayersViewController.layersView) {
// Create layers view if it doesn't exist
LayersViewController.layersView = document.createElement('div');
LayersViewController.layersView.id = 'layers-view';
LayersViewController.layersView.className = 'layers-view';
// Optional: Add the layers view to the document
// You might want to append this to a specific container
document.body.appendChild(LayersViewController.layersView);
console.warn(`Layers view element created: ${layersViewSelector}`);
}
// Try to find canvas root
LayersViewController.canvasRoot =
document.querySelector(canvasRootSelector);
if (!LayersViewController.canvasRoot) {
console.error(`Canvas root element not found: ${canvasRootSelector}`);
// Fallback to body if no specific root is found
LayersViewController.canvasRoot = document.body;
}
}
/**
* Recursive function to build layer hierarchy from DOM
*/
static buildLayerHierarchyFromDOM(rootElement) {
const htmlGenerator = new HTMLGenerator(new Canvas());
const generatedHTML = htmlGenerator.generateHTML();
// Parse the generated HTML into a DOM tree
const parser = new DOMParser();
const doc = parser.parseFromString(generatedHTML, 'text/html');
// Recursively traverse the DOM to build LayerItem hierarchy
const traverseDom = (element, depth = 0) => {
var _a;
const htmlElement = element; // Assert the element is an HTMLElement
// Skip elements without an `id` attribute
if (!htmlElement.id) {
return null;
}
const layer = {
id: htmlElement.id,
isVisible:
((_a = htmlElement.style) === null || _a === void 0
? void 0
: _a.display) !== 'none', // Check visibility only for HTMLElements
isLocked: htmlElement.getAttribute('data-locked') === 'true',
depth,
children: [],
};
Array.from(element.children).forEach(child => {
const childLayer = traverseDom(child, depth + 1);
if (childLayer) {
layer.children.push(childLayer);
}
});
return layer;
};
const rootElements = Array.from(doc.body.children);
// Build the hierarchy and filter out any `null` layers
return rootElements
.map(element => traverseDom(element))
.filter(layer => layer !== null);
}
/**
* Render the layers view with nested structure
*/
static updateLayersView() {
if (!this.layersView || !this.canvasRoot) {
console.error('Layers view or canvas root not initialized');
return;
}
// Clear existing layers
this.layersView.innerHTML = '';
// Build hierarchy from DOM
const hierarchy = this.buildLayerHierarchyFromDOM(this.canvasRoot);
// Create layers list
const layersList = document.createElement('ul');
layersList.className = 'layers-list';
// Render layer items
this.renderLayerItems(layersList, hierarchy);
// Append to layers view
this.layersView.appendChild(layersList);
}
/**
* Render layer items recursively
*/
static renderLayerItems(parentElement, layers, depth = 0) {
const list = document.createElement('ul');
list.className = 'layer-list';
parentElement.appendChild(list);
layers.forEach(layer => {
// List item to contain everything related to this layer
const listItem = document.createElement('li');
listItem.className = 'layer-item-container';
list.appendChild(listItem);
// The actual layer item element
const layerItem = this.createLayerItemElement(layer);
layerItem.style.paddingLeft = `${depth * 12}px`;
listItem.appendChild(layerItem);
// Handle nested children
if (layer.children && layer.children.length > 0) {
const expandToggle = document.createElement('span');
expandToggle.className = 'layer-expand-toggle';
expandToggle.textContent = '▶';
layerItem.insertBefore(expandToggle, layerItem.firstChild);
// Created a container for children
const childContainer = document.createElement('div');
childContainer.className = 'child-container';
childContainer.style.display = 'none';
listItem.appendChild(childContainer);
// Recursively render children
this.renderLayerItems(childContainer, layer.children, depth + 1);
// Toggle expand/collapse
expandToggle.addEventListener('click', () => {
const isExpanded = childContainer.style.display === 'block';
if (isExpanded) {
childContainer.style.display = 'none';
expandToggle.textContent = '▶';
} else {
childContainer.style.display = 'block';
expandToggle.textContent = '▼';
}
});
}
});
}
/**
* Create a layer item element with advanced interactions
*/
static createLayerItemElement(layer) {
const layerItem = document.createElement('li');
layerItem.className = 'layer-item';
layerItem.dataset.layerId = layer.id;
// Visibility toggle
const visibilityToggle = document.createElement('span');
visibilityToggle.className = 'layer-visibility';
visibilityToggle.innerHTML = layer.isVisible ? '👁️' : '👁️🗨️';
visibilityToggle.addEventListener('click', () =>
this.toggleLayerVisibility(layer)
);
// Layer name with type
const layerName = document.createElement('span');
layerName.className = 'layer-name';
layerName.textContent = `${layer.id}`;
layerName.addEventListener('click', () => this.selectLayer(layer));
// Lock toggle
const lockToggle = document.createElement('span');
lockToggle.className = 'layer-lock';
lockToggle.innerHTML = layer.isLocked ? '🔒' : '🔓';
lockToggle.addEventListener('click', () => this.toggleLayerLock(layer));
// Drag and drop functionality
layerItem.draggable = true;
layerItem.addEventListener('dragstart', e =>
this.handleDragStart(e, layer)
);
layerItem.addEventListener('dragover', this.handleDragOver);
layerItem.addEventListener('drop', e => this.handleDrop(e, layer));
// Append elements
layerItem.appendChild(visibilityToggle);
layerItem.appendChild(layerName);
layerItem.appendChild(lockToggle);
return layerItem;
}
/**
* Toggle layer visibility
*/
static toggleLayerVisibility(layer) {
const component = document.getElementById(layer.id);
if (!component) return;
if (component.style.display === 'none') {
component.style.display = component.dataset.originalDisplay || '';
layer.isVisible = true;
} else {
component.dataset.originalDisplay = component.style.display;
component.style.display = 'none';
layer.isVisible = false;
}
this.updateLayersView();
}
/**
* Toggle layer lock state
*/
static toggleLayerLock(layer) {
const component = document.getElementById(layer.id);
if (!component) return;
layer.isLocked = !layer.isLocked;
if (layer.isLocked) {
component.setAttribute('data-locked', 'true');
component.style.pointerEvents = 'none';
} else {
component.removeAttribute('data-locked');
component.style.pointerEvents = 'auto';
}
this.updateLayersView();
}
/**
* Select and customize a specific layer
*/
static selectLayer(layer) {
// Switch to customize mode for the selected layer
this.switchToCustomizeMode(layer.id);
}
/**
* Drag and drop handling methods
*/
static handleDragStart(e, layer) {
if (e.dataTransfer) {
e.dataTransfer.setData('text/plain', layer.id);
this.draggedItem = e.target;
}
}
static handleDragOver(e) {
e.preventDefault();
e.stopPropagation();
}
static handleDrop(e, targetLayer) {
var _a;
e.preventDefault();
e.stopPropagation();
if (!e.dataTransfer) return;
const fromIndex = parseInt(
((_a = e.dataTransfer) === null || _a === void 0
? void 0
: _a.getData('text/plain')) || '-1'
);
const toIndex = parseInt(targetLayer.id || '-1');
// Implement complex reordering logic
Canvas.reorderComponent(fromIndex, toIndex);
// Refresh the layers view
this.updateLayersView();
}
/**
* Switch to layer customization mode
*/
static switchToCustomizeMode(layerId) {
// Implement layer-specific customization
// This could open a sidebar, highlight the component, etc.
const sidebar = document.getElementById('customize-sidebar');
if (sidebar) {
sidebar.style.display = 'block';
// Load specific layer properties
this.loadLayerProperties(layerId);
}
}
/**
* Load layer-specific properties into the sidebar
*/
static loadLayerProperties(layerId) {
const component = document.getElementById(layerId);
if (!component) return;
// Example: Populate sidebar with component properties
const propertiesContainer = document.getElementById('layer-properties');
if (propertiesContainer) {
propertiesContainer.innerHTML = `
<h3>Layer Properties: ${layerId}</h3>
<div>Visibility: ${component.style.display !== 'none' ? 'Visible' : 'Hidden'}</div>
<div>Locked: ${component.getAttribute('data-locked') === 'true' ? 'Yes' : 'No'}</div>
`;
}
}
}
LayersViewController.layersView = null;
LayersViewController.canvasRoot = null;
LayersViewController.draggedItem = null;
export default LayersViewController;