diff --git a/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.css b/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.css index 5991ea5..1b6b574 100644 --- a/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.css +++ b/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.css @@ -8423,4 +8423,292 @@ ol li:before { left: -20px; } +/* Image Insertion Modal Styles */ +.image-modal-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.7); + display: flex; + justify-content: center; + align-items: center; + z-index: 10000; + backdrop-filter: blur(5px); +} + +.image-modal { + background: white; + border-radius: 16px; + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); + width: 90%; + max-width: 800px; + max-height: 80vh; + display: flex; + flex-direction: column; + overflow: hidden; +} + +.image-modal-header { + padding: 20px 24px; + border-bottom: 1px solid #e5e7eb; + display: flex; + justify-content: space-between; + align-items: center; + background: #f9fafb; +} + +.image-modal-header h3 { + margin: 0; + font-size: 18px; + font-weight: 600; + color: #111827; +} + +.image-modal-content { + flex: 1; + padding: 24px; + overflow-y: auto; +} + +.image-source-tabs { + display: flex; + gap: 8px; + margin-bottom: 24px; + border-bottom: 1px solid #e5e7eb; +} + +.tab-btn { + padding: 12px 20px; + border: none; + background: transparent; + color: #6b7280; + font-weight: 500; + cursor: pointer; + border-bottom: 2px solid transparent; + transition: all 0.2s ease; +} + +.tab-btn.active { + color: #4f46e5; + border-bottom-color: #4f46e5; + background: #f0f4ff; +} + +.tab-btn:hover { + color: #4f46e5; + background: #f9fafb; +} + +.property-images-section { + display: flex; + flex-direction: column; + gap: 20px; +} + +.image-categories { + display: flex; + gap: 8px; + flex-wrap: wrap; +} + +.category-btn { + padding: 8px 16px; + border: 1px solid #d1d5db; + background: white; + color: #374151; + border-radius: 8px; + font-size: 14px; + cursor: pointer; + transition: all 0.2s ease; +} + +.category-btn.active { + background: #4f46e5; + color: white; + border-color: #4f46e5; +} + +.category-btn:hover { + background: #f3f4f6; + border-color: #9ca3af; +} + +.property-images-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); + gap: 16px; + max-height: 300px; + overflow-y: auto; +} + +.property-image-item { + position: relative; + aspect-ratio: 1; + border-radius: 8px; + overflow: hidden; + cursor: pointer; + border: 2px solid transparent; + transition: all 0.2s ease; +} + +.property-image-item:hover { + border-color: #4f46e5; + transform: scale(1.02); +} + +.property-image-item.selected { + border-color: #4f46e5; + box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.2); +} + +.property-image-item img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.image-overlay { + position: absolute; + bottom: 0; + left: 0; + right: 0; + background: linear-gradient(transparent, rgba(0, 0, 0, 0.7)); + color: white; + padding: 8px; + font-size: 12px; + opacity: 0; + transition: opacity 0.2s ease; +} + +.property-image-item:hover .image-overlay { + opacity: 1; +} + +.local-upload-section { + display: flex; + justify-content: center; + align-items: center; + min-height: 200px; +} + +.upload-area { + border: 2px dashed #d1d5db; + border-radius: 12px; + padding: 40px; + text-align: center; + cursor: pointer; + transition: all 0.2s ease; + background: #f9fafb; + width: 100%; + max-width: 400px; +} + +.upload-area:hover { + border-color: #4f46e5; + background: #f0f4ff; +} + +.upload-area.dragover { + border-color: #4f46e5; + background: #e0e7ff; +} + +.file-input { + display: none; +} + +.upload-content { + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; +} + +.upload-content p { + margin: 0; + font-weight: 500; + color: #374151; +} + +.upload-content small { + color: #6b7280; +} + +.image-modal-actions { + padding: 20px 24px; + border-top: 1px solid #e5e7eb; + display: flex; + justify-content: flex-end; + gap: 12px; + background: #f9fafb; +} + +/* Enhanced Draggable Image Styles */ +.draggable-image-container { + position: absolute; + border: 2px solid transparent; + cursor: move; + z-index: 1000; + min-width: 50px; + min-height: 50px; + user-select: none; +} + +.draggable-image-container.selected { + border-color: #4f46e5; + box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.2); +} + +.draggable-image-container img { + width: 100%; + height: 100%; + object-fit: cover; + display: block; +} + +.resize-handle { + position: absolute; + background: #4f46e5; + border: 2px solid white; + border-radius: 50%; + width: 12px; + height: 12px; + z-index: 1001; +} + +.resize-handle.nw { top: -6px; left: -6px; cursor: nw-resize; } +.resize-handle.ne { top: -6px; right: -6px; cursor: ne-resize; } +.resize-handle.sw { bottom: -6px; left: -6px; cursor: sw-resize; } +.resize-handle.se { bottom: -6px; right: -6px; cursor: se-resize; } +.resize-handle.n { top: -6px; left: 50%; transform: translateX(-50%); cursor: n-resize; } +.resize-handle.s { bottom: -6px; left: 50%; transform: translateX(-50%); cursor: s-resize; } +.resize-handle.w { top: 50%; left: -6px; transform: translateY(-50%); cursor: w-resize; } +.resize-handle.e { top: 50%; right: -6px; transform: translateY(-50%); cursor: e-resize; } + +.delete-handle { + position: absolute; + top: -8px; + right: -8px; + background: #ef4444; + color: white; + border: none; + border-radius: 50%; + width: 20px; + height: 20px; + font-size: 12px; + cursor: pointer; + z-index: 1002; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); +} + +.delete-handle:hover { + background: #dc2626; + transform: scale(1.1); +} + diff --git a/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.html b/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.html index c28df32..1c7746a 100644 --- a/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.html +++ b/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.html @@ -969,7 +969,7 @@ Text - + Image @@ -1230,4 +1230,62 @@ + + + + + + Insert Image + ✕ + + + + + Property Images + + + Upload Image + + + + + + + + {category.label} + + + + + + + + + {image.name} + + + + + + + + + + + + Click to upload image or drag and drop + Supports: JPG, PNG, GIF, WebP + + + + + + Cancel + + Insert Image + + + + \ No newline at end of file diff --git a/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.js b/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.js index d3afbeb..ab3fe0b 100644 --- a/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.js +++ b/force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.js @@ -45,6 +45,7 @@ export default class PropertyTemplateSelector extends LightningElement { // Real property images from Salesforce @track realPropertyImages = []; + @track propertyImages = []; @track imagesByCategory = { 'Interior': [ { url: 'https://images.unsplash.com/photo-1600585154340-be6161a56a0c?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200', title: 'Interior View 1', category: 'Interior' }, @@ -115,6 +116,16 @@ export default class PropertyTemplateSelector extends LightningElement { @track tableRows = 3; @track tableCols = 3; @track includeHeader = true; + + // Image insertion modal properties + @track showImageModal = false; + @track imageSource = 'property'; // 'property' or 'local' + @track selectedImageCategory = 'all'; + @track selectedImageUrl = ''; + @track selectedImageName = ''; + @track uploadedImageData = ''; + @track renderKey = 0; // For forcing re-renders + @track insertButtonDisabled = true; // Explicit button state // Table Drag and Drop Variables @track isDraggingTable = false; @@ -144,6 +155,76 @@ export default class PropertyTemplateSelector extends LightningElement { return this.replacementActiveTab === 'upload'; } + // Image insertion modal getters + get isImageModalOpen() { + return this.showImageModal; + } + + get imageCategories() { + const categories = [ + { label: 'All Images', value: 'all' }, + { label: 'Exterior', value: 'exterior' }, + { label: 'Interior', value: 'interior' }, + { label: 'Kitchen', value: 'kitchen' }, + { label: 'Bedroom', value: 'bedroom' }, + { label: 'Bathroom', value: 'bathroom' }, + { label: 'Living Room', value: 'living' }, + { label: 'Maps', value: 'maps' }, + { label: 'None', value: 'none' } + ]; + return categories; + } + + get filteredPropertyImages() { + console.log('filteredPropertyImages called'); + console.log('propertyImages:', this.propertyImages); + console.log('selectedImageCategory:', this.selectedImageCategory); + + if (!this.propertyImages || this.propertyImages.length === 0) { + console.log('No property images available'); + return []; + } + + if (this.selectedImageCategory === 'all') { + console.log('Returning all images:', this.propertyImages); + return this.propertyImages; + } + + const filtered = this.propertyImages.filter(image => { + const category = image.category ? image.category.toLowerCase() : 'none'; + return category === this.selectedImageCategory; + }); + + console.log('Filtered images:', filtered); + return filtered; + } + + get propertyTabClass() { + return this.imageSource === 'property' ? 'tab-btn active' : 'tab-btn'; + } + + get localTabClass() { + return this.imageSource === 'local' ? 'tab-btn active' : 'tab-btn'; + } + + getCategoryButtonClass(categoryValue) { + return this.selectedImageCategory === categoryValue ? 'category-btn active' : 'category-btn'; + } + + get showPropertyImagesSection() { + return this.imageSource === 'property'; + } + + get showLocalUploadSection() { + return this.imageSource === 'local'; + } + + get isInsertButtonDisabled() { + const disabled = !this.selectedImageUrl || this.selectedImageUrl === ''; + console.log('isInsertButtonDisabled check:', disabled, 'selectedImageUrl:', this.selectedImageUrl, 'renderKey:', this.renderKey); + return disabled; + } + // Computed properties for template selection get isBlankTemplateSelected() { return this.selectedTemplateId === 'blank-template'; @@ -3670,6 +3751,353 @@ export default class PropertyTemplateSelector extends LightningElement { } } + // Show image insertion modal + showImageInsertModal() { + this.showImageModal = true; + this.selectedImageUrl = ''; + this.selectedImageName = ''; + this.uploadedImageData = ''; + this.selectedImageCategory = 'all'; + this.insertButtonDisabled = true; + + // Populate property images from the existing data + this.populatePropertyImages(); + } + + // Populate property images array + populatePropertyImages() { + this.propertyImages = []; + + // Add images from imagesByCategory + Object.keys(this.imagesByCategory).forEach(category => { + this.imagesByCategory[category].forEach(image => { + this.propertyImages.push({ + url: image.url, + name: image.title || image.name || `${category} Image`, + category: category.toLowerCase() + }); + }); + }); + + // Add real property images if available + if (this.realPropertyImages && this.realPropertyImages.length > 0) { + this.realPropertyImages.forEach(image => { + this.propertyImages.push({ + url: image.url || image.Url__c, + name: image.name || image.Name || 'Property Image', + category: (image.category || image.Category__c || 'none').toLowerCase() + }); + }); + } + + console.log('Property images populated:', this.propertyImages); + } + + // Close image insertion modal + closeImageModal() { + this.showImageModal = false; + this.selectedImageUrl = ''; + this.selectedImageName = ''; + this.uploadedImageData = ''; + this.insertButtonDisabled = true; + + // Clear any selections + document.querySelectorAll('.property-image-item').forEach(item => { + item.classList.remove('selected'); + }); + + // Reset upload area + this.resetUploadArea(); + } + + // Set image source (property or local) + setImageSource(event) { + const source = event.target.dataset.source; + this.imageSource = source; + this.selectedImageUrl = ''; + this.selectedImageName = ''; + this.uploadedImageData = ''; + this.insertButtonDisabled = true; + + // Clear any selections + document.querySelectorAll('.property-image-item').forEach(item => { + item.classList.remove('selected'); + }); + + // Reset upload area + this.resetUploadArea(); + } + + // Select image category + selectImageCategory(event) { + const category = event.target.dataset.category; + this.selectedImageCategory = category; + + // Update button states + document.querySelectorAll('.category-btn').forEach(btn => { + btn.classList.remove('active'); + if (btn.dataset.category === category) { + btn.classList.add('active'); + } + }); + } + + // Select property image + selectPropertyImage(event) { + // Get the image URL from the closest element with data-image-url + const imageItem = event.target.closest('[data-image-url]'); + const imageUrl = imageItem ? imageItem.dataset.imageUrl : null; + const imageName = event.target.alt || event.target.textContent || 'Property Image'; + + console.log('Property image selected:', imageUrl, imageName); + + if (!imageUrl) { + console.error('No image URL found in selected item'); + return; + } + + // Remove previous selection + document.querySelectorAll('.property-image-item').forEach(item => { + item.classList.remove('selected'); + }); + + // Add selection to clicked item + const targetItem = event.target.closest('.property-image-item'); + if (targetItem) { + targetItem.classList.add('selected'); + } + + // Force reactivity by creating new objects + this.selectedImageUrl = imageUrl; + this.selectedImageName = imageName; + this.uploadedImageData = ''; + this.insertButtonDisabled = false; + + console.log('After selection - selectedImageUrl:', this.selectedImageUrl); + console.log('Button disabled state:', this.insertButtonDisabled); + + // Log current state for debugging + this.logCurrentState(); + + // Reset upload area if we're on local tab + if (this.imageSource === 'local') { + this.resetUploadArea(); + } + + // Force a re-render by updating a tracked property + this.forceRerender(); + } + + // Reset upload area to default state + resetUploadArea() { + const uploadArea = this.template.querySelector('.upload-area'); + if (uploadArea) { + // Remove existing preview if any + const existingPreview = uploadArea.querySelector('.uploaded-image-preview'); + if (existingPreview) { + existingPreview.remove(); + } + + // Show upload content again + const uploadContent = uploadArea.querySelector('.upload-content'); + if (uploadContent) { + uploadContent.style.display = 'flex'; + } + } + } + + // Trigger file upload + triggerFileUpload() { + const fileInput = this.template.querySelector('.file-input'); + if (fileInput) { + fileInput.click(); + } + } + + // Handle file upload + handleFileUpload(event) { + const file = event.target.files[0]; + if (file) { + console.log('File selected:', file.name); + const reader = new FileReader(); + reader.onload = (e) => { + this.uploadedImageData = e.target.result; + this.selectedImageUrl = e.target.result; + this.selectedImageName = file.name; + this.insertButtonDisabled = false; + + console.log('Image loaded, selectedImageUrl:', this.selectedImageUrl); + console.log('Button disabled state:', this.insertButtonDisabled); + + // Log current state for debugging + this.logCurrentState(); + + // Update the upload area to show selected image + this.updateUploadAreaWithSelectedImage(e.target.result, file.name); + + // Force a re-render by updating a tracked property + this.forceRerender(); + }; + reader.readAsDataURL(file); + } + } + + // Update upload area to show selected image + updateUploadAreaWithSelectedImage(imageUrl, fileName) { + console.log('updateUploadAreaWithSelectedImage called with:', imageUrl, fileName); + const uploadArea = this.template.querySelector('.upload-area'); + console.log('Upload area found:', uploadArea); + if (uploadArea) { + // Remove existing preview if any + const existingPreview = uploadArea.querySelector('.uploaded-image-preview'); + if (existingPreview) { + existingPreview.remove(); + } + + // Create preview container + const previewContainer = document.createElement('div'); + previewContainer.className = 'uploaded-image-preview'; + previewContainer.style.cssText = ` + position: relative; + width: 100%; + max-width: 200px; + margin: 0 auto; + border-radius: 8px; + overflow: hidden; + border: 2px solid #4f46e5; + box-shadow: 0 4px 12px rgba(79, 70, 229, 0.2); + `; + + // Create image element + const img = document.createElement('img'); + img.src = imageUrl; + img.alt = fileName; + img.style.cssText = ` + width: 100%; + height: auto; + display: block; + max-height: 150px; + object-fit: cover; + `; + + // Create file name overlay + const fileNameOverlay = document.createElement('div'); + fileNameOverlay.style.cssText = ` + position: absolute; + bottom: 0; + left: 0; + right: 0; + background: linear-gradient(transparent, rgba(0, 0, 0, 0.7)); + color: white; + padding: 8px; + font-size: 12px; + font-weight: 500; + `; + fileNameOverlay.textContent = fileName; + + previewContainer.appendChild(img); + previewContainer.appendChild(fileNameOverlay); + + // Replace upload content with preview + const uploadContent = uploadArea.querySelector('.upload-content'); + if (uploadContent) { + uploadContent.style.display = 'none'; + } + + uploadArea.appendChild(previewContainer); + + // Add click handler to change image + uploadArea.onclick = () => { + this.triggerFileUpload(); + }; + } + } + + // Handle insert button click with debugging + handleInsertButtonClick() { + console.log('=== INSERT BUTTON CLICKED ==='); + this.logCurrentState(); + console.log('============================='); + this.insertSelectedImage(); + } + + // Insert selected image + insertSelectedImage() { + console.log('insertSelectedImage called'); + console.log('selectedImageUrl:', this.selectedImageUrl); + console.log('insertButtonDisabled:', this.insertButtonDisabled); + + // Check if we have a valid image URL + const imageUrl = this.selectedImageUrl || this.uploadedImageData; + const imageName = this.selectedImageName || 'Uploaded Image'; + + if (this.insertButtonDisabled || !imageUrl) { + console.error('Please select an image first'); + console.error('selectedImageUrl:', this.selectedImageUrl); + console.error('uploadedImageData:', this.uploadedImageData); + console.error('insertButtonDisabled:', this.insertButtonDisabled); + alert('Please select an image first'); + return; + } + + const editor = this.template.querySelector('.enhanced-editor-content'); + if (editor) { + // Save undo state before making changes + this.saveUndoState(); + this.setupEditorClickHandler(); + + // Create draggable image container + const imageContainer = document.createElement('div'); + imageContainer.className = 'draggable-image-container'; + imageContainer.style.left = '50px'; + imageContainer.style.top = '50px'; + imageContainer.style.width = '200px'; + imageContainer.style.height = '150px'; + imageContainer.style.zIndex = '1000'; + imageContainer.style.position = 'absolute'; + imageContainer.style.overflow = 'hidden'; + imageContainer.style.border = '2px solid transparent'; + imageContainer.style.cursor = 'move'; + imageContainer.style.userSelect = 'none'; + + // Create image element + const img = document.createElement('img'); + img.src = imageUrl; + img.alt = imageName; + img.style.width = '100%'; + img.style.height = '100%'; + img.style.objectFit = 'cover'; + img.style.display = 'block'; + + imageContainer.appendChild(img); + + // Add resize handles + this.addResizeHandles(imageContainer); + + // Add delete handle + this.addDeleteHandle(imageContainer); + + // Add drag functionality + this.makeDraggable(imageContainer); + + // Add click to select functionality + imageContainer.addEventListener('click', (e) => { + e.stopPropagation(); + this.selectDraggableElement(imageContainer); + }); + + // Select the image after a short delay + setTimeout(() => { + this.selectDraggableElement(imageContainer); + }, 100); + + editor.appendChild(imageContainer); + + // Close modal + this.closeImageModal(); + } + } + // Insert draggable image element insertDraggableImage() { const input = document.createElement('input'); @@ -6460,6 +6888,240 @@ export default class PropertyTemplateSelector extends LightningElement { } } + // Enhanced image manipulation methods + addResizeHandles(container) { + const handles = ['nw', 'ne', 'sw', 'se', 'n', 's', 'w', 'e']; + + handles.forEach(handle => { + const resizeHandle = document.createElement('div'); + resizeHandle.className = `resize-handle ${handle}`; + resizeHandle.style.position = 'absolute'; + resizeHandle.style.background = '#4f46e5'; + resizeHandle.style.border = '2px solid white'; + resizeHandle.style.borderRadius = '50%'; + resizeHandle.style.width = '12px'; + resizeHandle.style.height = '12px'; + resizeHandle.style.zIndex = '1001'; + + // Position handles + switch(handle) { + case 'nw': resizeHandle.style.top = '-6px'; resizeHandle.style.left = '-6px'; resizeHandle.style.cursor = 'nw-resize'; break; + case 'ne': resizeHandle.style.top = '-6px'; resizeHandle.style.right = '-6px'; resizeHandle.style.cursor = 'ne-resize'; break; + case 'sw': resizeHandle.style.bottom = '-6px'; resizeHandle.style.left = '-6px'; resizeHandle.style.cursor = 'sw-resize'; break; + case 'se': resizeHandle.style.bottom = '-6px'; resizeHandle.style.right = '-6px'; resizeHandle.style.cursor = 'se-resize'; break; + case 'n': resizeHandle.style.top = '-6px'; resizeHandle.style.left = '50%'; resizeHandle.style.transform = 'translateX(-50%)'; resizeHandle.style.cursor = 'n-resize'; break; + case 's': resizeHandle.style.bottom = '-6px'; resizeHandle.style.left = '50%'; resizeHandle.style.transform = 'translateX(-50%)'; resizeHandle.style.cursor = 's-resize'; break; + case 'w': resizeHandle.style.top = '50%'; resizeHandle.style.left = '-6px'; resizeHandle.style.transform = 'translateY(-50%)'; resizeHandle.style.cursor = 'w-resize'; break; + case 'e': resizeHandle.style.top = '50%'; resizeHandle.style.right = '-6px'; resizeHandle.style.transform = 'translateY(-50%)'; resizeHandle.style.cursor = 'e-resize'; break; + } + + // Add resize functionality + this.addResizeFunctionality(resizeHandle, container, handle); + + container.appendChild(resizeHandle); + }); + } + + // Add delete handle to image + addDeleteHandle(container) { + const deleteHandle = document.createElement('button'); + deleteHandle.className = 'delete-handle'; + deleteHandle.innerHTML = '×'; + deleteHandle.style.position = 'absolute'; + deleteHandle.style.top = '-8px'; + deleteHandle.style.right = '-8px'; + deleteHandle.style.background = '#ef4444'; + deleteHandle.style.color = 'white'; + deleteHandle.style.border = 'none'; + deleteHandle.style.borderRadius = '50%'; + deleteHandle.style.width = '20px'; + deleteHandle.style.height = '20px'; + deleteHandle.style.fontSize = '12px'; + deleteHandle.style.cursor = 'pointer'; + deleteHandle.style.zIndex = '1002'; + deleteHandle.style.display = 'flex'; + deleteHandle.style.alignItems = 'center'; + deleteHandle.style.justifyContent = 'center'; + deleteHandle.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.2)'; + + deleteHandle.addEventListener('click', (e) => { + e.stopPropagation(); + this.saveUndoState(); + container.remove(); + }); + + deleteHandle.addEventListener('mouseenter', () => { + deleteHandle.style.background = '#dc2626'; + deleteHandle.style.transform = 'scale(1.1)'; + }); + + deleteHandle.addEventListener('mouseleave', () => { + deleteHandle.style.background = '#ef4444'; + deleteHandle.style.transform = 'scale(1)'; + }); + + container.appendChild(deleteHandle); + } + + // Add resize functionality to handle + addResizeFunctionality(handle, container, direction) { + let isResizing = false; + let startX, startY, startWidth, startHeight, startLeft, startTop; + + handle.addEventListener('mousedown', (e) => { + e.stopPropagation(); + isResizing = true; + + startX = e.clientX; + startY = e.clientY; + startWidth = parseInt(window.getComputedStyle(container).width, 10); + startHeight = parseInt(window.getComputedStyle(container).height, 10); + startLeft = parseInt(window.getComputedStyle(container).left, 10); + startTop = parseInt(window.getComputedStyle(container).top, 10); + + document.addEventListener('mousemove', handleResize); + document.addEventListener('mouseup', stopResize); + }); + + const handleResize = (e) => { + if (!isResizing) return; + + const deltaX = e.clientX - startX; + const deltaY = e.clientY - startY; + + let newWidth = startWidth; + let newHeight = startHeight; + let newLeft = startLeft; + let newTop = startTop; + + switch(direction) { + case 'se': + newWidth = Math.max(50, startWidth + deltaX); + newHeight = Math.max(50, startHeight + deltaY); + break; + case 'sw': + newWidth = Math.max(50, startWidth - deltaX); + newHeight = Math.max(50, startHeight + deltaY); + newLeft = startLeft + (startWidth - newWidth); + break; + case 'ne': + newWidth = Math.max(50, startWidth + deltaX); + newHeight = Math.max(50, startHeight - deltaY); + newTop = startTop + (startHeight - newHeight); + break; + case 'nw': + newWidth = Math.max(50, startWidth - deltaX); + newHeight = Math.max(50, startHeight - deltaY); + newLeft = startLeft + (startWidth - newWidth); + newTop = startTop + (startHeight - newHeight); + break; + case 'e': + newWidth = Math.max(50, startWidth + deltaX); + break; + case 'w': + newWidth = Math.max(50, startWidth - deltaX); + newLeft = startLeft + (startWidth - newWidth); + break; + case 's': + newHeight = Math.max(50, startHeight + deltaY); + break; + case 'n': + newHeight = Math.max(50, startHeight - deltaY); + newTop = startTop + (startHeight - newHeight); + break; + } + + container.style.width = newWidth + 'px'; + container.style.height = newHeight + 'px'; + container.style.left = newLeft + 'px'; + container.style.top = newTop + 'px'; + }; + + const stopResize = () => { + isResizing = false; + document.removeEventListener('mousemove', handleResize); + document.removeEventListener('mouseup', stopResize); + }; + } + + // Make element draggable (enhanced version) + makeDraggable(element) { + let isDragging = false; + let startX, startY, startLeft, startTop; + + element.addEventListener('mousedown', (e) => { + // Don't start drag if clicking on resize handles or delete button + if (e.target.classList.contains('resize-handle') || e.target.classList.contains('delete-handle')) { + return; + } + + isDragging = true; + startX = e.clientX; + startY = e.clientY; + startLeft = parseInt(window.getComputedStyle(element).left, 10); + startTop = parseInt(window.getComputedStyle(element).top, 10); + + element.style.cursor = 'grabbing'; + document.addEventListener('mousemove', handleDrag); + document.addEventListener('mouseup', stopDrag); + }); + + const handleDrag = (e) => { + if (!isDragging) return; + + const deltaX = e.clientX - startX; + const deltaY = e.clientY - startY; + + element.style.left = (startLeft + deltaX) + 'px'; + element.style.top = (startTop + deltaY) + 'px'; + }; + + const stopDrag = () => { + isDragging = false; + element.style.cursor = 'move'; + document.removeEventListener('mousemove', handleDrag); + document.removeEventListener('mouseup', stopDrag); + }; + } + + // Select draggable element + selectDraggableElement(element) { + // Remove selection from all elements + document.querySelectorAll('.draggable-image-container').forEach(el => { + el.classList.remove('selected'); + }); + + // Add selection to clicked element + element.classList.add('selected'); + } + + // Force re-render by updating a tracked property + forceRerender() { + // Update a dummy property to force reactivity + this.renderKey = this.renderKey ? this.renderKey + 1 : 1; + } + + // Debug method to log current state + logCurrentState() { + console.log('=== CURRENT STATE ==='); + console.log('selectedImageUrl:', this.selectedImageUrl); + console.log('selectedImageName:', this.selectedImageName); + console.log('uploadedImageData:', this.uploadedImageData); + console.log('insertButtonDisabled:', this.insertButtonDisabled); + console.log('imageSource:', this.imageSource); + console.log('propertyImages length:', this.propertyImages ? this.propertyImages.length : 0); + console.log('===================='); + } + + // Test method to manually set an image (for debugging) + testSetImage() { + console.log('Testing manual image set'); + this.selectedImageUrl = 'https://images.unsplash.com/photo-1600585154340-be6161a56a0c?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=1200'; + this.selectedImageName = 'Test Image'; + this.insertButtonDisabled = false; + this.logCurrentState(); + } + connectedCallback() { this.loadSavedTemplates(); }
Click to upload image or drag and drop