mirror of
https://github.com/Azornes/Comfyui-LayerForge.git
synced 2026-03-21 20:52:12 -03:00
Introduces a new CanvasLayersPanel component for managing layers visually, including selection, renaming, reordering via drag-and-drop, and deletion. Integrates the panel into the main Canvas and CanvasView, synchronizes selection and state changes, and adds logic for duplicating layers and debounced state saving. Moves IndexedDB state saving to a Web Worker for better performance. Also sets default logger level to DEBUG for improved diagnostics.
700 lines
22 KiB
JavaScript
700 lines
22 KiB
JavaScript
import {createModuleLogger} from "./utils/LoggerUtils.js";
|
|
|
|
const log = createModuleLogger('CanvasLayersPanel');
|
|
|
|
export class CanvasLayersPanel {
|
|
constructor(canvas) {
|
|
this.canvas = canvas;
|
|
this.container = null;
|
|
this.layersContainer = null;
|
|
this.draggedElements = [];
|
|
this.dragInsertionLine = null;
|
|
this.isMultiSelecting = false;
|
|
this.lastSelectedIndex = -1;
|
|
|
|
// Binding metod dla event handlerów
|
|
this.handleLayerClick = this.handleLayerClick.bind(this);
|
|
this.handleDragStart = this.handleDragStart.bind(this);
|
|
this.handleDragOver = this.handleDragOver.bind(this);
|
|
this.handleDragEnd = this.handleDragEnd.bind(this);
|
|
this.handleDrop = this.handleDrop.bind(this);
|
|
|
|
log.info('CanvasLayersPanel initialized');
|
|
}
|
|
|
|
/**
|
|
* Tworzy strukturê HTML panelu warstw
|
|
*/
|
|
createPanelStructure() {
|
|
// Główny kontener panelu
|
|
this.container = document.createElement('div');
|
|
this.container.className = 'layers-panel';
|
|
this.container.innerHTML = `
|
|
<div class="layers-panel-header">
|
|
<span class="layers-panel-title">Warstwy</span>
|
|
<div class="layers-panel-controls">
|
|
<button class="layers-btn" id="add-layer-btn" title="Dodaj warstwę">+</button>
|
|
<button class="layers-btn" id="delete-layer-btn" title="Usuń warstwę">🗑</button>
|
|
</div>
|
|
</div>
|
|
<div class="layers-container" id="layers-container">
|
|
<!-- Lista warstw będzie renderowana tutaj -->
|
|
</div>
|
|
`;
|
|
|
|
this.layersContainer = this.container.querySelector('#layers-container');
|
|
|
|
// Dodanie stylów CSS
|
|
this.injectStyles();
|
|
|
|
// Setup event listeners dla przycisków
|
|
this.setupControlButtons();
|
|
|
|
log.debug('Panel structure created');
|
|
return this.container;
|
|
}
|
|
|
|
/**
|
|
* Dodaje style CSS do panelu
|
|
*/
|
|
injectStyles() {
|
|
const styleId = 'layers-panel-styles';
|
|
if (document.getElementById(styleId)) {
|
|
return; // Style już istnieją
|
|
}
|
|
|
|
const style = document.createElement('style');
|
|
style.id = styleId;
|
|
style.textContent = `
|
|
.layers-panel {
|
|
background: #2a2a2a;
|
|
border: 1px solid #3a3a3a;
|
|
border-radius: 4px;
|
|
padding: 8px;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
font-family: Arial, sans-serif;
|
|
font-size: 12px;
|
|
color: #ffffff;
|
|
user-select: none;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.layers-panel-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding-bottom: 8px;
|
|
border-bottom: 1px solid #3a3a3a;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.layers-panel-title {
|
|
font-weight: bold;
|
|
color: #ffffff;
|
|
}
|
|
|
|
.layers-panel-controls {
|
|
display: flex;
|
|
gap: 4px;
|
|
}
|
|
|
|
.layers-btn {
|
|
background: #3a3a3a;
|
|
border: 1px solid #4a4a4a;
|
|
color: #ffffff;
|
|
padding: 4px 8px;
|
|
border-radius: 3px;
|
|
cursor: pointer;
|
|
font-size: 11px;
|
|
}
|
|
|
|
.layers-btn:hover {
|
|
background: #4a4a4a;
|
|
}
|
|
|
|
.layers-btn:active {
|
|
background: #5a5a5a;
|
|
}
|
|
|
|
.layers-container {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
.layer-row {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 6px 4px;
|
|
margin-bottom: 2px;
|
|
border-radius: 3px;
|
|
cursor: pointer;
|
|
transition: background-color 0.15s ease;
|
|
position: relative;
|
|
gap: 6px;
|
|
}
|
|
|
|
.layer-row:hover {
|
|
background: rgba(255, 255, 255, 0.05);
|
|
}
|
|
|
|
.layer-row.selected {
|
|
background: #2d5aa0 !important;
|
|
box-shadow: inset 0 0 0 1px #4a7bc8;
|
|
}
|
|
|
|
.layer-row.dragging {
|
|
opacity: 0.6;
|
|
}
|
|
|
|
|
|
.layer-thumbnail {
|
|
width: 48px;
|
|
height: 48px;
|
|
border: 1px solid #4a4a4a;
|
|
border-radius: 2px;
|
|
background: transparent;
|
|
position: relative;
|
|
flex-shrink: 0;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.layer-thumbnail canvas {
|
|
width: 100%;
|
|
height: 100%;
|
|
display: block;
|
|
}
|
|
|
|
.layer-thumbnail::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background-image:
|
|
linear-gradient(45deg, #555 25%, transparent 25%),
|
|
linear-gradient(-45deg, #555 25%, transparent 25%),
|
|
linear-gradient(45deg, transparent 75%, #555 75%),
|
|
linear-gradient(-45deg, transparent 75%, #555 75%);
|
|
background-size: 8px 8px;
|
|
background-position: 0 0, 0 4px, 4px -4px, -4px 0px;
|
|
z-index: 1;
|
|
}
|
|
|
|
.layer-thumbnail canvas {
|
|
position: relative;
|
|
z-index: 2;
|
|
}
|
|
|
|
.layer-name {
|
|
flex: 1;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
padding: 2px 4px;
|
|
border-radius: 2px;
|
|
color: #ffffff;
|
|
}
|
|
|
|
.layer-name.editing {
|
|
background: #4a4a4a;
|
|
border: 1px solid #6a6a6a;
|
|
outline: none;
|
|
color: #ffffff;
|
|
}
|
|
|
|
.layer-name input {
|
|
background: transparent;
|
|
border: none;
|
|
color: #ffffff;
|
|
font-size: 12px;
|
|
width: 100%;
|
|
outline: none;
|
|
}
|
|
|
|
.drag-insertion-line {
|
|
position: absolute;
|
|
left: 0;
|
|
right: 0;
|
|
height: 2px;
|
|
background: #4a7bc8;
|
|
border-radius: 1px;
|
|
z-index: 1000;
|
|
box-shadow: 0 0 4px rgba(74, 123, 200, 0.6);
|
|
}
|
|
|
|
.layers-container::-webkit-scrollbar {
|
|
width: 6px;
|
|
}
|
|
|
|
.layers-container::-webkit-scrollbar-track {
|
|
background: #2a2a2a;
|
|
}
|
|
|
|
.layers-container::-webkit-scrollbar-thumb {
|
|
background: #4a4a4a;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.layers-container::-webkit-scrollbar-thumb:hover {
|
|
background: #5a5a5a;
|
|
}
|
|
`;
|
|
|
|
document.head.appendChild(style);
|
|
log.debug('Styles injected');
|
|
}
|
|
|
|
/**
|
|
* Konfiguruje event listenery dla przycisków kontrolnych
|
|
*/
|
|
setupControlButtons() {
|
|
const addBtn = this.container.querySelector('#add-layer-btn');
|
|
const deleteBtn = this.container.querySelector('#delete-layer-btn');
|
|
|
|
addBtn?.addEventListener('click', () => {
|
|
log.info('Add layer button clicked');
|
|
// TODO: Implementacja dodawania warstwy
|
|
});
|
|
|
|
deleteBtn?.addEventListener('click', () => {
|
|
log.info('Delete layer button clicked');
|
|
this.deleteSelectedLayers();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Renderuje listę warstw
|
|
*/
|
|
renderLayers() {
|
|
if (!this.layersContainer) {
|
|
log.warn('Layers container not initialized');
|
|
return;
|
|
}
|
|
|
|
// Wyczyść istniejącą zawartość
|
|
this.layersContainer.innerHTML = '';
|
|
|
|
// Usuń linię wstawiania jeśli istnieje
|
|
this.removeDragInsertionLine();
|
|
|
|
// Sortuj warstwy według zIndex (od najwyższej do najniższej)
|
|
const sortedLayers = [...this.canvas.layers].sort((a, b) => b.zIndex - a.zIndex);
|
|
|
|
sortedLayers.forEach((layer, index) => {
|
|
const layerElement = this.createLayerElement(layer, index);
|
|
this.layersContainer.appendChild(layerElement);
|
|
});
|
|
|
|
log.debug(`Rendered ${sortedLayers.length} layers`);
|
|
}
|
|
|
|
/**
|
|
* Tworzy element HTML dla pojedynczej warstwy
|
|
*/
|
|
createLayerElement(layer, index) {
|
|
const layerRow = document.createElement('div');
|
|
layerRow.className = 'layer-row';
|
|
layerRow.draggable = true;
|
|
layerRow.dataset.layerIndex = index;
|
|
|
|
// Sprawdź czy warstwa jest zaznaczona
|
|
const isSelected = this.canvas.selectedLayers.includes(layer);
|
|
if (isSelected) {
|
|
layerRow.classList.add('selected');
|
|
}
|
|
|
|
// Ustawienie domyślnych właściwości jeśli nie istnieją
|
|
if (!layer.name) {
|
|
layer.name = this.ensureUniqueName(`Layer ${layer.zIndex + 1}`, layer);
|
|
} else {
|
|
// Sprawdź unikalność istniejącej nazwy (np. przy duplikowaniu)
|
|
layer.name = this.ensureUniqueName(layer.name, layer);
|
|
}
|
|
|
|
layerRow.innerHTML = `
|
|
<div class="layer-thumbnail" data-layer-index="${index}"></div>
|
|
<span class="layer-name" data-layer-index="${index}">${layer.name}</span>
|
|
`;
|
|
|
|
// Wygeneruj miniaturkę
|
|
this.generateThumbnail(layer, layerRow.querySelector('.layer-thumbnail'));
|
|
|
|
// Event listenery
|
|
this.setupLayerEventListeners(layerRow, layer, index);
|
|
|
|
return layerRow;
|
|
}
|
|
|
|
/**
|
|
* Generuje miniaturkę warstwy
|
|
*/
|
|
generateThumbnail(layer, thumbnailContainer) {
|
|
if (!layer.image) {
|
|
thumbnailContainer.style.background = '#4a4a4a';
|
|
return;
|
|
}
|
|
|
|
const canvas = document.createElement('canvas');
|
|
const ctx = canvas.getContext('2d', { willReadFrequently: true });
|
|
canvas.width = 48;
|
|
canvas.height = 48;
|
|
|
|
// Oblicz skalę zachowując proporcje
|
|
const scale = Math.min(48 / layer.image.width, 48 / layer.image.height);
|
|
const scaledWidth = layer.image.width * scale;
|
|
const scaledHeight = layer.image.height * scale;
|
|
|
|
// Wycentruj obraz
|
|
const x = (48 - scaledWidth) / 2;
|
|
const y = (48 - scaledHeight) / 2;
|
|
|
|
// Narysuj obraz z wyższą jakością
|
|
ctx.imageSmoothingEnabled = true;
|
|
ctx.imageSmoothingQuality = 'high';
|
|
ctx.drawImage(layer.image, x, y, scaledWidth, scaledHeight);
|
|
|
|
thumbnailContainer.appendChild(canvas);
|
|
}
|
|
|
|
/**
|
|
* Konfiguruje event listenery dla elementu warstwy
|
|
*/
|
|
setupLayerEventListeners(layerRow, layer, index) {
|
|
// Click handler - natychmiastowe zaznaczanie
|
|
layerRow.addEventListener('click', (e) => {
|
|
const nameElement = layerRow.querySelector('.layer-name');
|
|
if (nameElement && nameElement.classList.contains('editing')) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
return;
|
|
}
|
|
this.handleLayerClick(e, layer, index);
|
|
});
|
|
|
|
// Double click handler - edycja nazwy
|
|
layerRow.addEventListener('dblclick', (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
const nameElement = layerRow.querySelector('.layer-name');
|
|
this.startEditingLayerName(nameElement, layer);
|
|
});
|
|
|
|
// Drag handlers
|
|
layerRow.addEventListener('dragstart', (e) => this.handleDragStart(e, layer, index));
|
|
layerRow.addEventListener('dragover', this.handleDragOver);
|
|
layerRow.addEventListener('dragend', this.handleDragEnd);
|
|
layerRow.addEventListener('drop', (e) => this.handleDrop(e, index));
|
|
}
|
|
|
|
/**
|
|
* Obsługuje kliknięcie na warstwę, aktualizując stan bez pełnego renderowania.
|
|
*/
|
|
handleLayerClick(e, layer, index) {
|
|
// Zatrzymujemy, bo dblclick też wywołałby click
|
|
e.preventDefault();
|
|
|
|
const isCtrlPressed = e.ctrlKey || e.metaKey;
|
|
const isShiftPressed = e.shiftKey;
|
|
|
|
// Aktualizuj wewnętrzny stan zaznaczenia w obiekcie canvas
|
|
// Ta funkcja NIE powinna już wywoływać onSelectionChanged w panelu.
|
|
this.canvas.updateSelectionLogic(layer, isCtrlPressed, isShiftPressed, index);
|
|
|
|
// Aktualizuj tylko wygląd (klasy CSS), bez niszczenia DOM
|
|
this.updateSelectionAppearance();
|
|
|
|
log.debug(`Layer clicked: ${layer.name}, selection count: ${this.canvas.selectedLayers.length}`);
|
|
}
|
|
|
|
|
|
/**
|
|
* Rozpoczyna edycję nazwy warstwy
|
|
*/
|
|
startEditingLayerName(nameElement, layer) {
|
|
const currentName = layer.name;
|
|
nameElement.classList.add('editing');
|
|
|
|
const input = document.createElement('input');
|
|
input.type = 'text';
|
|
input.value = currentName;
|
|
input.style.width = '100%';
|
|
|
|
nameElement.innerHTML = '';
|
|
nameElement.appendChild(input);
|
|
|
|
input.focus();
|
|
input.select();
|
|
|
|
const finishEditing = () => {
|
|
let newName = input.value.trim() || `Layer ${layer.zIndex + 1}`;
|
|
newName = this.ensureUniqueName(newName, layer);
|
|
layer.name = newName;
|
|
nameElement.classList.remove('editing');
|
|
nameElement.textContent = newName;
|
|
|
|
this.canvas.saveState();
|
|
log.info(`Layer renamed to: ${newName}`);
|
|
};
|
|
|
|
input.addEventListener('blur', finishEditing);
|
|
input.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Enter') {
|
|
finishEditing();
|
|
} else if (e.key === 'Escape') {
|
|
nameElement.classList.remove('editing');
|
|
nameElement.textContent = currentName;
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
/**
|
|
* Zapewnia unikalność nazwy warstwy
|
|
*/
|
|
ensureUniqueName(proposedName, currentLayer) {
|
|
const existingNames = this.canvas.layers
|
|
.filter(layer => layer !== currentLayer)
|
|
.map(layer => layer.name);
|
|
|
|
if (!existingNames.includes(proposedName)) {
|
|
return proposedName;
|
|
}
|
|
|
|
// Sprawdź czy nazwa już ma numerację w nawiasach
|
|
const match = proposedName.match(/^(.+?)\s*\((\d+)\)$/);
|
|
let baseName, startNumber;
|
|
|
|
if (match) {
|
|
baseName = match[1].trim();
|
|
startNumber = parseInt(match[2]) + 1;
|
|
} else {
|
|
baseName = proposedName;
|
|
startNumber = 1;
|
|
}
|
|
|
|
// Znajdź pierwszą dostępną numerację
|
|
let counter = startNumber;
|
|
let uniqueName;
|
|
|
|
do {
|
|
uniqueName = `${baseName} (${counter})`;
|
|
counter++;
|
|
} while (existingNames.includes(uniqueName));
|
|
|
|
return uniqueName;
|
|
}
|
|
|
|
/**
|
|
* Usuwa zaznaczone warstwy
|
|
*/
|
|
deleteSelectedLayers() {
|
|
if (this.canvas.selectedLayers.length === 0) {
|
|
log.debug('No layers selected for deletion');
|
|
return;
|
|
}
|
|
|
|
log.info(`Deleting ${this.canvas.selectedLayers.length} selected layers`);
|
|
this.canvas.removeSelectedLayers();
|
|
this.renderLayers();
|
|
}
|
|
|
|
/**
|
|
* Rozpoczyna przeciąganie warstwy
|
|
*/
|
|
handleDragStart(e, layer, index) {
|
|
// Sprawdź czy jakakolwiek warstwa jest w trybie edycji
|
|
const editingElement = this.layersContainer.querySelector('.layer-name.editing');
|
|
if (editingElement) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
|
|
// Jeśli przeciągana warstwa nie jest zaznaczona, zaznacz ją
|
|
if (!this.canvas.selectedLayers.includes(layer)) {
|
|
this.canvas.updateSelection([layer]);
|
|
this.renderLayers();
|
|
}
|
|
|
|
this.draggedElements = [...this.canvas.selectedLayers];
|
|
e.dataTransfer.effectAllowed = 'move';
|
|
e.dataTransfer.setData('text/plain', ''); // Wymagane przez standard
|
|
|
|
// Dodaj klasę dragging do przeciąganych elementów
|
|
this.layersContainer.querySelectorAll('.layer-row').forEach((row, idx) => {
|
|
const sortedLayers = [...this.canvas.layers].sort((a, b) => b.zIndex - a.zIndex);
|
|
if (this.draggedElements.includes(sortedLayers[idx])) {
|
|
row.classList.add('dragging');
|
|
}
|
|
});
|
|
|
|
log.debug(`Started dragging ${this.draggedElements.length} layers`);
|
|
}
|
|
|
|
/**
|
|
* Obsługuje przeciąganie nad warstwą
|
|
*/
|
|
handleDragOver(e) {
|
|
e.preventDefault();
|
|
e.dataTransfer.dropEffect = 'move';
|
|
|
|
const layerRow = e.currentTarget;
|
|
const rect = layerRow.getBoundingClientRect();
|
|
const midpoint = rect.top + rect.height / 2;
|
|
const isUpperHalf = e.clientY < midpoint;
|
|
|
|
this.showDragInsertionLine(layerRow, isUpperHalf);
|
|
}
|
|
|
|
/**
|
|
* Pokazuje linię wskaźnika wstawiania
|
|
*/
|
|
showDragInsertionLine(targetRow, isUpperHalf) {
|
|
this.removeDragInsertionLine();
|
|
|
|
const line = document.createElement('div');
|
|
line.className = 'drag-insertion-line';
|
|
|
|
if (isUpperHalf) {
|
|
line.style.top = '-1px';
|
|
} else {
|
|
line.style.bottom = '-1px';
|
|
}
|
|
|
|
targetRow.style.position = 'relative';
|
|
targetRow.appendChild(line);
|
|
this.dragInsertionLine = line;
|
|
}
|
|
|
|
/**
|
|
* Usuwa linię wskaźnika wstawiania
|
|
*/
|
|
removeDragInsertionLine() {
|
|
if (this.dragInsertionLine) {
|
|
this.dragInsertionLine.remove();
|
|
this.dragInsertionLine = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Obsługuje upuszczenie warstwy
|
|
*/
|
|
handleDrop(e, targetIndex) {
|
|
e.preventDefault();
|
|
this.removeDragInsertionLine();
|
|
|
|
if (this.draggedElements.length === 0) return;
|
|
|
|
const rect = e.currentTarget.getBoundingClientRect();
|
|
const midpoint = rect.top + rect.height / 2;
|
|
const isUpperHalf = e.clientY < midpoint;
|
|
|
|
// Oblicz docelowy indeks
|
|
let insertIndex = targetIndex;
|
|
if (!isUpperHalf) {
|
|
insertIndex = targetIndex + 1;
|
|
}
|
|
|
|
this.moveLayersToPosition(this.draggedElements, insertIndex);
|
|
|
|
log.info(`Dropped ${this.draggedElements.length} layers at position ${insertIndex}`);
|
|
}
|
|
|
|
/**
|
|
* Kończy przeciąganie
|
|
*/
|
|
handleDragEnd(e) {
|
|
this.removeDragInsertionLine();
|
|
|
|
// Usuń klasę dragging ze wszystkich elementów
|
|
this.layersContainer.querySelectorAll('.layer-row').forEach(row => {
|
|
row.classList.remove('dragging');
|
|
});
|
|
|
|
this.draggedElements = [];
|
|
}
|
|
|
|
/**
|
|
* Przenosi warstwy na nową pozycję
|
|
*/
|
|
moveLayersToPosition(layers, insertIndex) {
|
|
const sortedLayers = [...this.canvas.layers].sort((a, b) => a.zIndex - b.zIndex);
|
|
|
|
// Usuń przeciągane warstwy z listy
|
|
const filteredLayers = sortedLayers.filter(layer => !layers.includes(layer));
|
|
|
|
// Wstaw warstwy w nowej pozycji (odwróć kolejność bo renderujemy od góry)
|
|
const reverseInsertIndex = filteredLayers.length - insertIndex;
|
|
filteredLayers.splice(reverseInsertIndex, 0, ...layers);
|
|
|
|
// Zaktualizuj zIndex dla wszystkich warstw
|
|
filteredLayers.forEach((layer, index) => {
|
|
layer.zIndex = index;
|
|
});
|
|
|
|
this.canvas.layers = filteredLayers;
|
|
this.canvas.render();
|
|
this.renderLayers();
|
|
this.canvas.saveState();
|
|
}
|
|
|
|
/**
|
|
* Aktualizuje panel gdy zmienią się warstwy
|
|
*/
|
|
onLayersChanged() {
|
|
this.renderLayers();
|
|
}
|
|
|
|
/**
|
|
* Aktualizuje wygląd zaznaczenia w panelu bez pełnego renderowania.
|
|
*/
|
|
updateSelectionAppearance() {
|
|
const sortedLayers = [...this.canvas.layers].sort((a, b) => b.zIndex - a.zIndex);
|
|
const layerRows = this.layersContainer.querySelectorAll('.layer-row');
|
|
|
|
layerRows.forEach((row, index) => {
|
|
const layer = sortedLayers[index];
|
|
if (this.canvas.selectedLayers.includes(layer)) {
|
|
row.classList.add('selected');
|
|
} else {
|
|
row.classList.remove('selected');
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Aktualizuje panel gdy zmienią się warstwy (np. dodanie, usunięcie, zmiana kolejności)
|
|
* To jest jedyne miejsce, gdzie powinniśmy w pełni renderować panel.
|
|
*/
|
|
onLayersChanged() {
|
|
this.renderLayers();
|
|
}
|
|
|
|
/**
|
|
* Aktualizuje panel gdy zmieni się zaznaczenie (wywoływane z zewnątrz).
|
|
* Zamiast pełnego renderowania, tylko aktualizujemy wygląd.
|
|
*/
|
|
onSelectionChanged() {
|
|
this.updateSelectionAppearance();
|
|
}
|
|
|
|
/**
|
|
* Niszczy panel i czyści event listenery
|
|
*/
|
|
destroy() {
|
|
if (this.container && this.container.parentNode) {
|
|
this.container.parentNode.removeChild(this.container);
|
|
}
|
|
this.container = null;
|
|
this.layersContainer = null;
|
|
this.draggedElements = [];
|
|
this.removeDragInsertionLine();
|
|
|
|
log.info('CanvasLayersPanel destroyed');
|
|
}
|
|
}
|