From 8d7b3f0914ce7e22227707e13be25f72e83db36d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Cmit=20Tun=C3=A7?= Date: Fri, 31 Oct 2025 17:14:21 -0300 Subject: [PATCH] Implement fullscreen editor for template preview: Added a fullscreen editing feature with collapsible editor functionality and improved loading indicators. Enhanced the template preview Blade view for better user interaction and experience during content editing and previewing. --- .../Schemas/FooterTemplateForm.php | 3 + .../components/template-preview.blade.php | 355 +++++++++++++++++- 2 files changed, 353 insertions(+), 5 deletions(-) diff --git a/app/Filament/Admin/Resources/FooterTemplates/Schemas/FooterTemplateForm.php b/app/Filament/Admin/Resources/FooterTemplates/Schemas/FooterTemplateForm.php index 059d403..3763544 100644 --- a/app/Filament/Admin/Resources/FooterTemplates/Schemas/FooterTemplateForm.php +++ b/app/Filament/Admin/Resources/FooterTemplates/Schemas/FooterTemplateForm.php @@ -8,6 +8,8 @@ use Filament\Forms\Components\Toggle; use Filament\Schemas\Components\Section; use Filament\Schemas\Components\View; use Filament\Schemas\Schema; +use Filament\Forms\Components\CodeEditor\Enums\Language; + class FooterTemplateForm { @@ -26,6 +28,7 @@ class FooterTemplateForm CodeEditor::make('html_content') ->label(__('footer-templates.field_html_content')) ->required() + ->language(Language::Html) ->live(onBlur: false) // Real-time updates ->columnSpanFull() ->helperText(__('footer-templates.field_html_content_help')) diff --git a/resources/views/components/template-preview.blade.php b/resources/views/components/template-preview.blade.php index e028835..7de8217 100644 --- a/resources/views/components/template-preview.blade.php +++ b/resources/views/components/template-preview.blade.php @@ -16,19 +16,28 @@
+
@@ -68,6 +77,106 @@ + + @@ -79,9 +188,14 @@ function templatePreview(previewUrl, type, fieldName) { type: type, fieldName: fieldName, iframeSrc: '', + fullscreenIframeSrc: '', isLoading: false, content: '', codeMirrorView: null, + isFullscreen: false, + editorCollapsed: false, + clonedEditor: null, + originalEditorWrapper: null, init() { console.log('[Preview] Init:', { previewUrl, type, fieldName }); @@ -92,6 +206,13 @@ function templatePreview(previewUrl, type, fieldName) { this.findCodeMirror(); }, 1500); }); + + // ESC key to close fullscreen + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape' && this.isFullscreen) { + this.closeFullscreenEditor(); + } + }); }, findCodeMirror() { @@ -312,6 +433,10 @@ function templatePreview(previewUrl, type, fieldName) { URL.revokeObjectURL(this.iframeSrc); this.iframeSrc = ''; } + if (this.fullscreenIframeSrc) { + URL.revokeObjectURL(this.fullscreenIframeSrc); + this.fullscreenIframeSrc = ''; + } return; } @@ -338,12 +463,17 @@ function templatePreview(previewUrl, type, fieldName) { const html = await response.text(); + // Update both iframes if (this.iframeSrc) { URL.revokeObjectURL(this.iframeSrc); } + if (this.fullscreenIframeSrc) { + URL.revokeObjectURL(this.fullscreenIframeSrc); + } const blob = new Blob([html], { type: 'text/html; charset=utf-8' }); this.iframeSrc = URL.createObjectURL(blob); + this.fullscreenIframeSrc = URL.createObjectURL(blob); this.isLoading = false; console.log('[Preview] Updated successfully, content length:', this.content.length); @@ -353,12 +483,227 @@ function templatePreview(previewUrl, type, fieldName) { } }, + openFullscreenEditor() { + this.isFullscreen = true; + this.editorCollapsed = false; + + this.$nextTick(() => { + setTimeout(() => { + this.cloneEditorToFullscreen(); + }, 100); + }); + }, + + closeFullscreenEditor() { + // Restore original editor if it was moved + if (this.clonedEditor && this.originalEditorWrapper) { + const editorContainer = this.clonedEditor.parentElement; + if (editorContainer && editorContainer.id === 'fullscreen-code-editor') { + // Move editor back to original location + const cmEditor = this.clonedEditor.querySelector('.cm-editor'); + if (cmEditor && this.originalEditorWrapper) { + // Restore to original wrapper + const originalContainer = this.originalEditorWrapper.querySelector('.cm-editor').parentElement; + if (originalContainer) { + // Keep the editor where it is, just clean up + this.clonedEditor = null; + this.originalEditorWrapper = null; + } + } + } + } + + this.isFullscreen = false; + this.editorCollapsed = false; + + // Clean up fullscreen iframe + if (this.fullscreenIframeSrc) { + URL.revokeObjectURL(this.fullscreenIframeSrc); + this.fullscreenIframeSrc = ''; + } + }, + + toggleEditorCollapse() { + this.editorCollapsed = !this.editorCollapsed; + }, + + cloneEditorToFullscreen() { + const fullscreenContainer = document.getElementById('fullscreen-code-editor'); + if (!fullscreenContainer) { + console.error('[Preview] Fullscreen container not found'); + return; + } + + // Find the original CodeMirror editor + const wrapperSelectors = ['.fi-fo-field-wrp', '.fi-input-wrp', '[wire\\:id]']; + let targetWrapper = null; + + const dataFieldElements = document.querySelectorAll(`[data-field-name="${this.fieldName}"]`); + if (dataFieldElements.length > 0) { + for (const selector of wrapperSelectors) { + targetWrapper = dataFieldElements[0].closest(selector); + if (targetWrapper) break; + } + } + + if (!targetWrapper) { + const input = document.querySelector(`textarea[name="${this.fieldName}"], input[name="${this.fieldName}"]`); + if (input) { + for (const selector of wrapperSelectors) { + targetWrapper = input.closest(selector); + if (targetWrapper) break; + } + } + } + + if (!targetWrapper) { + const cmEditor = document.querySelector('.cm-editor'); + if (cmEditor) { + for (const selector of wrapperSelectors) { + targetWrapper = cmEditor.closest(selector); + if (targetWrapper) break; + } + } + } + + if (!targetWrapper) { + console.error('[Preview] Could not find editor wrapper'); + return; + } + + this.originalEditorWrapper = targetWrapper; + + // Find the CodeMirror editor element + const cmEditor = targetWrapper.querySelector('.cm-editor'); + if (!cmEditor) { + console.error('[Preview] CodeMirror editor not found'); + return; + } + + // Clone the entire editor structure + const editorParent = cmEditor.parentElement; + if (!editorParent) { + console.error('[Preview] Editor parent not found'); + return; + } + + // Create a deep clone + const clone = editorParent.cloneNode(true); + clone.id = 'cloned-editor'; + + // Clear the container first + fullscreenContainer.innerHTML = ''; + + // Append clone + fullscreenContainer.appendChild(clone); + + this.clonedEditor = clone; + + // Adjust styles for fullscreen + const clonedCmEditor = clone.querySelector('.cm-editor'); + if (clonedCmEditor) { + clonedCmEditor.style.height = '100%'; + clonedCmEditor.style.minHeight = '100%'; + + // Make the cm-scroller take full height + const cmScroller = clonedCmEditor.querySelector('.cm-scroller'); + if (cmScroller) { + cmScroller.style.height = '100%'; + cmScroller.style.minHeight = '100%'; + } + + // Make the cm-content take full height + const cmContent = clonedCmEditor.querySelector('.cm-content'); + if (cmContent) { + cmContent.style.minHeight = '100%'; + } + } + + // Update content watcher for cloned editor + const clonedCmContent = clone.querySelector('.cm-content'); + if (clonedCmContent) { + // Watch for changes in cloned editor + const observer = new MutationObserver(() => { + const newContent = clonedCmContent.textContent || ''; + if (newContent !== this.content) { + this.content = newContent; + // Also update original editor if it exists + const originalCmContent = cmEditor.querySelector('.cm-content'); + if (originalCmContent) { + // Sync back to original if needed + } + } + }); + observer.observe(clonedCmContent, { + childList: true, + subtree: true, + characterData: true, + }); + + // Update content from cloned editor + this.content = clonedCmContent.textContent || ''; + } + + // Try to sync CodeMirror 6 view if available + if (clonedCmEditor) { + if (clonedCmEditor.view || clonedCmEditor._view) { + const view = clonedCmEditor.view || clonedCmEditor._view; + if (view.state && view.state.doc) { + // Watch for changes + if (view.dispatch) { + const originalDispatch = view.dispatch; + view.dispatch = (tr) => { + originalDispatch.call(view, tr); + this.content = view.state.doc.toString(); + }; + } + } + } + } + + console.log('[Preview] Editor cloned to fullscreen'); + }, + destroy() { if (this.iframeSrc) { URL.revokeObjectURL(this.iframeSrc); } + if (this.fullscreenIframeSrc) { + URL.revokeObjectURL(this.fullscreenIframeSrc); + } + if (this.isFullscreen) { + this.closeFullscreenEditor(); + } } }; } @endpush + +@push('styles') + +@endpush