| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  | import { html, type TemplateResult, cssManager } from '@design.estate/dees-element'; | 
					
						
							| 
									
										
										
										
											2025-06-24 08:19:53 +00:00
										 |  |  | import { DeesModal } from '../dees-modal.js'; | 
					
						
							|  |  |  | import { type IBlock } from './wysiwyg.types.js'; | 
					
						
							| 
									
										
										
										
											2025-06-24 10:45:06 +00:00
										 |  |  | import { WysiwygShortcuts } from './wysiwyg.shortcuts.js'; | 
					
						
							| 
									
										
										
										
											2025-06-24 13:41:12 +00:00
										 |  |  | import { PROGRAMMING_LANGUAGES } from './wysiwyg.constants.js'; | 
					
						
							| 
									
										
										
										
											2025-06-24 08:19:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | export class WysiwygModalManager { | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Shows language selection modal for code blocks | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   static async showLanguageSelectionModal(): Promise<string | null> { | 
					
						
							|  |  |  |     return new Promise((resolve) => { | 
					
						
							|  |  |  |       let selectedLanguage: string | null = null; | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       DeesModal.createAndShow({ | 
					
						
							|  |  |  |         heading: 'Select Programming Language', | 
					
						
							|  |  |  |         content: html`
 | 
					
						
							|  |  |  |           <style> | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |             .language-container { | 
					
						
							|  |  |  |               padding: 16px; | 
					
						
							|  |  |  |               max-height: 400px; | 
					
						
							|  |  |  |               overflow-y: auto; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2025-06-24 08:19:53 +00:00
										 |  |  |             .language-grid { | 
					
						
							|  |  |  |               display: grid; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |               grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); | 
					
						
							| 
									
										
										
										
											2025-06-24 08:19:53 +00:00
										 |  |  |               gap: 8px; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             .language-button { | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |               padding: 12px 8px; | 
					
						
							|  |  |  |               background: transparent; | 
					
						
							|  |  |  |               border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#374151')}; | 
					
						
							|  |  |  |               border-radius: 6px; | 
					
						
							| 
									
										
										
										
											2025-06-24 08:19:53 +00:00
										 |  |  |               cursor: pointer; | 
					
						
							|  |  |  |               text-align: center; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |               font-size: 13px; | 
					
						
							|  |  |  |               font-weight: 500; | 
					
						
							|  |  |  |               transition: all 0.15s ease; | 
					
						
							|  |  |  |               color: ${cssManager.bdTheme('#374151', '#e5e7eb')}; | 
					
						
							| 
									
										
										
										
											2025-06-24 08:19:53 +00:00
										 |  |  |             } | 
					
						
							|  |  |  |             .language-button:hover { | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |               background: ${cssManager.bdTheme('#f9fafb', '#1f2937')}; | 
					
						
							|  |  |  |               border-color: ${cssManager.bdTheme('#d1d5db', '#4b5563')}; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             .language-button.selected { | 
					
						
							|  |  |  |               background: ${cssManager.bdTheme('#f3f4f6', '#374151')}; | 
					
						
							|  |  |  |               border-color: ${cssManager.bdTheme('#9ca3af', '#6b7280')}; | 
					
						
							|  |  |  |               color: ${cssManager.bdTheme('#111827', '#f9fafb')}; | 
					
						
							| 
									
										
										
										
											2025-06-24 08:19:53 +00:00
										 |  |  |             } | 
					
						
							|  |  |  |           </style> | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |           <div class="language-container"> | 
					
						
							|  |  |  |             <div class="language-grid"> | 
					
						
							|  |  |  |               ${this.getLanguages().map(lang => html`
 | 
					
						
							|  |  |  |                 <div  | 
					
						
							|  |  |  |                   class="language-button ${selectedLanguage === lang.toLowerCase() ? 'selected' : ''}"  | 
					
						
							|  |  |  |                   @click="${() => { | 
					
						
							|  |  |  |                     selectedLanguage = lang.toLowerCase(); | 
					
						
							|  |  |  |                     // Close modal by finding it in DOM
 | 
					
						
							|  |  |  |                     const modal = document.querySelector('dees-modal'); | 
					
						
							|  |  |  |                     if (modal && typeof (modal as any).destroy === 'function') { | 
					
						
							|  |  |  |                       (modal as any).destroy(); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     resolve(selectedLanguage); | 
					
						
							|  |  |  |                   }}"> | 
					
						
							|  |  |  |                   ${lang} | 
					
						
							|  |  |  |                 </div> | 
					
						
							|  |  |  |               `)}
 | 
					
						
							|  |  |  |             </div> | 
					
						
							| 
									
										
										
										
											2025-06-24 08:19:53 +00:00
										 |  |  |           </div> | 
					
						
							|  |  |  |         `,
 | 
					
						
							|  |  |  |         menuOptions: [ | 
					
						
							|  |  |  |           { | 
					
						
							|  |  |  |             name: 'Cancel', | 
					
						
							|  |  |  |             action: async (modal) => { | 
					
						
							|  |  |  |               modal.destroy(); | 
					
						
							|  |  |  |               resolve(null); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         ] | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Shows block settings modal | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   static async showBlockSettingsModal( | 
					
						
							|  |  |  |     block: IBlock,  | 
					
						
							|  |  |  |     onUpdate: (block: IBlock) => void | 
					
						
							|  |  |  |   ): Promise<void> { | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2025-06-24 10:45:06 +00:00
										 |  |  |     const content = html`
 | 
					
						
							|  |  |  |       <style> | 
					
						
							|  |  |  |         .settings-container { | 
					
						
							|  |  |  |           padding: 16px; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         .settings-section { | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |           margin-bottom: 24px; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         .settings-section:last-child { | 
					
						
							|  |  |  |           margin-bottom: 0; | 
					
						
							| 
									
										
										
										
											2025-06-24 10:45:06 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |         .settings-label { | 
					
						
							|  |  |  |           font-weight: 500; | 
					
						
							|  |  |  |           margin-bottom: 8px; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |           color: ${cssManager.bdTheme('#6b7280', '#9ca3af')}; | 
					
						
							|  |  |  |           font-size: 12px; | 
					
						
							|  |  |  |           text-transform: uppercase; | 
					
						
							|  |  |  |           letter-spacing: 0.05em; | 
					
						
							| 
									
										
										
										
											2025-06-24 10:45:06 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |         .block-type-grid { | 
					
						
							|  |  |  |           display: grid; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |           grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); | 
					
						
							| 
									
										
										
										
											2025-06-24 10:45:06 +00:00
										 |  |  |           gap: 8px; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         .block-type-button { | 
					
						
							|  |  |  |           padding: 12px; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |           background: transparent; | 
					
						
							|  |  |  |           border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#374151')}; | 
					
						
							|  |  |  |           border-radius: 6px; | 
					
						
							| 
									
										
										
										
											2025-06-24 10:45:06 +00:00
										 |  |  |           cursor: pointer; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |           text-align: left; | 
					
						
							|  |  |  |           transition: all 0.15s ease; | 
					
						
							| 
									
										
										
										
											2025-06-24 10:45:06 +00:00
										 |  |  |           display: flex; | 
					
						
							|  |  |  |           align-items: center; | 
					
						
							|  |  |  |           gap: 8px; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |           font-size: 13px; | 
					
						
							|  |  |  |           color: ${cssManager.bdTheme('#374151', '#e5e7eb')}; | 
					
						
							| 
									
										
										
										
											2025-06-24 10:45:06 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |         .block-type-button:hover { | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |           background: ${cssManager.bdTheme('#f9fafb', '#1f2937')}; | 
					
						
							|  |  |  |           border-color: ${cssManager.bdTheme('#d1d5db', '#4b5563')}; | 
					
						
							| 
									
										
										
										
											2025-06-24 10:45:06 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |         .block-type-button.selected { | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |           background: ${cssManager.bdTheme('#f3f4f6', '#374151')}; | 
					
						
							|  |  |  |           border-color: ${cssManager.bdTheme('#9ca3af', '#6b7280')}; | 
					
						
							|  |  |  |           color: ${cssManager.bdTheme('#111827', '#f9fafb')}; | 
					
						
							| 
									
										
										
										
											2025-06-24 10:45:06 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |         .block-type-icon { | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |           font-weight: 500; | 
					
						
							| 
									
										
										
										
											2025-06-24 10:45:06 +00:00
										 |  |  |           font-size: 16px; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |           width: 20px; | 
					
						
							|  |  |  |           text-align: center; | 
					
						
							|  |  |  |           flex-shrink: 0; | 
					
						
							|  |  |  |           opacity: 0.7; | 
					
						
							| 
									
										
										
										
											2025-06-24 10:45:06 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |       </style> | 
					
						
							|  |  |  |       <div class="settings-container"> | 
					
						
							|  |  |  |         ${this.getBlockTypeSelector(block, onUpdate)} | 
					
						
							|  |  |  |         ${block.type === 'code' ? this.getCodeBlockSettings(block, onUpdate) : ''} | 
					
						
							|  |  |  |       </div> | 
					
						
							|  |  |  |     `;
 | 
					
						
							| 
									
										
										
										
											2025-06-24 08:19:53 +00:00
										 |  |  |      | 
					
						
							|  |  |  |     DeesModal.createAndShow({ | 
					
						
							|  |  |  |       heading: 'Block Settings', | 
					
						
							|  |  |  |       content, | 
					
						
							|  |  |  |       menuOptions: [ | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |           name: 'Done', | 
					
						
							| 
									
										
										
										
											2025-06-24 08:19:53 +00:00
										 |  |  |           action: async (modal) => { | 
					
						
							|  |  |  |             modal.destroy(); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       ] | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Gets code block settings content | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   private static getCodeBlockSettings( | 
					
						
							|  |  |  |     block: IBlock,  | 
					
						
							|  |  |  |     onUpdate: (block: IBlock) => void | 
					
						
							|  |  |  |   ): TemplateResult { | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |     const currentLanguage = block.metadata?.language || 'javascript'; | 
					
						
							| 
									
										
										
										
											2025-06-24 08:19:53 +00:00
										 |  |  |      | 
					
						
							|  |  |  |     return html`
 | 
					
						
							|  |  |  |       <style> | 
					
						
							|  |  |  |         .language-grid { | 
					
						
							|  |  |  |           display: grid; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |           grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); | 
					
						
							|  |  |  |           gap: 6px; | 
					
						
							| 
									
										
										
										
											2025-06-24 08:19:53 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |         .language-button { | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |           padding: 8px 4px; | 
					
						
							|  |  |  |           background: transparent; | 
					
						
							|  |  |  |           border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#374151')}; | 
					
						
							| 
									
										
										
										
											2025-06-24 08:19:53 +00:00
										 |  |  |           border-radius: 4px; | 
					
						
							|  |  |  |           cursor: pointer; | 
					
						
							|  |  |  |           text-align: center; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |           transition: all 0.15s ease; | 
					
						
							|  |  |  |           font-size: 12px; | 
					
						
							|  |  |  |           color: ${cssManager.bdTheme('#374151', '#e5e7eb')}; | 
					
						
							| 
									
										
										
										
											2025-06-24 08:19:53 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |         .language-button:hover { | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |           background: ${cssManager.bdTheme('#f9fafb', '#1f2937')}; | 
					
						
							|  |  |  |           border-color: ${cssManager.bdTheme('#d1d5db', '#4b5563')}; | 
					
						
							| 
									
										
										
										
											2025-06-24 08:19:53 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |         .language-button.selected { | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |           background: ${cssManager.bdTheme('#f3f4f6', '#374151')}; | 
					
						
							|  |  |  |           border-color: ${cssManager.bdTheme('#9ca3af', '#6b7280')}; | 
					
						
							|  |  |  |           color: ${cssManager.bdTheme('#111827', '#f9fafb')}; | 
					
						
							| 
									
										
										
										
											2025-06-24 08:19:53 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |       </style> | 
					
						
							|  |  |  |       <div class="settings-section"> | 
					
						
							|  |  |  |         <div class="settings-label">Programming Language</div> | 
					
						
							|  |  |  |         <div class="language-grid"> | 
					
						
							|  |  |  |           ${this.getLanguages().map(lang => html`
 | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |             <div  | 
					
						
							|  |  |  |               class="language-button ${currentLanguage === lang.toLowerCase() ? 'selected' : ''}"  | 
					
						
							|  |  |  |               @click="${() => { | 
					
						
							| 
									
										
										
										
											2025-06-24 08:19:53 +00:00
										 |  |  |                 if (!block.metadata) block.metadata = {}; | 
					
						
							|  |  |  |                 block.metadata.language = lang.toLowerCase(); | 
					
						
							|  |  |  |                 onUpdate(block); | 
					
						
							|  |  |  |                  | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |                 // Close modal immediately
 | 
					
						
							|  |  |  |                 const modal = document.querySelector('dees-modal'); | 
					
						
							|  |  |  |                 if (modal && typeof (modal as any).destroy === 'function') { | 
					
						
							|  |  |  |                   (modal as any).destroy(); | 
					
						
							| 
									
										
										
										
											2025-06-24 08:19:53 +00:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |               }}" | 
					
						
							|  |  |  |               data-lang="${lang}" | 
					
						
							|  |  |  |             >${lang}</div> | 
					
						
							| 
									
										
										
										
											2025-06-24 08:19:53 +00:00
										 |  |  |           `)}
 | 
					
						
							|  |  |  |         </div> | 
					
						
							|  |  |  |       </div> | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Gets available programming languages | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   private static getLanguages(): string[] { | 
					
						
							| 
									
										
										
										
											2025-06-24 13:41:12 +00:00
										 |  |  |     return [...PROGRAMMING_LANGUAGES]; | 
					
						
							| 
									
										
										
										
											2025-06-24 08:19:53 +00:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-06-24 10:45:06 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Gets block type selector | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   private static getBlockTypeSelector( | 
					
						
							|  |  |  |     block: IBlock, | 
					
						
							|  |  |  |     onUpdate: (block: IBlock) => void | 
					
						
							|  |  |  |   ): TemplateResult { | 
					
						
							|  |  |  |     const blockTypes = WysiwygShortcuts.getSlashMenuItems().filter(item => item.type !== 'divider'); | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     return html`
 | 
					
						
							|  |  |  |       <div class="settings-section"> | 
					
						
							|  |  |  |         <div class="settings-label">Block Type</div> | 
					
						
							|  |  |  |         <div class="block-type-grid"> | 
					
						
							|  |  |  |           ${blockTypes.map(item => html`
 | 
					
						
							|  |  |  |             <div  | 
					
						
							|  |  |  |               class="block-type-button ${block.type === item.type ? 'selected' : ''}" | 
					
						
							|  |  |  |               @click="${async (e: MouseEvent) => { | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |                 const button = e.currentTarget as HTMLElement; | 
					
						
							|  |  |  |                  | 
					
						
							| 
									
										
										
										
											2025-06-24 10:45:06 +00:00
										 |  |  |                 const oldType = block.type; | 
					
						
							|  |  |  |                 block.type = item.type as IBlock['type']; | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 // Reset metadata for type change
 | 
					
						
							|  |  |  |                 if (oldType === 'code' && block.type !== 'code') { | 
					
						
							|  |  |  |                   delete block.metadata?.language; | 
					
						
							|  |  |  |                 } else if (oldType === 'list' && block.type !== 'list') { | 
					
						
							|  |  |  |                   delete block.metadata?.listType; | 
					
						
							|  |  |  |                 } else if (block.type === 'list' && !block.metadata?.listType) { | 
					
						
							|  |  |  |                   block.metadata = { listType: 'bullet' }; | 
					
						
							|  |  |  |                 } else if (block.type === 'code' && !block.metadata?.language) { | 
					
						
							|  |  |  |                   // Ask for language if changing to code block
 | 
					
						
							|  |  |  |                   const language = await this.showLanguageSelectionModal(); | 
					
						
							|  |  |  |                   if (language) { | 
					
						
							|  |  |  |                     block.metadata = { language }; | 
					
						
							|  |  |  |                   } else { | 
					
						
							|  |  |  |                     // User cancelled, revert
 | 
					
						
							|  |  |  |                     block.type = oldType; | 
					
						
							|  |  |  |                     return; | 
					
						
							|  |  |  |                   } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |                 onUpdate(block); | 
					
						
							|  |  |  |                  | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |                 // Close modal immediately
 | 
					
						
							|  |  |  |                 const modal = document.querySelector('dees-modal'); | 
					
						
							|  |  |  |                 if (modal && typeof (modal as any).destroy === 'function') { | 
					
						
							|  |  |  |                   (modal as any).destroy(); | 
					
						
							| 
									
										
										
										
											2025-06-24 10:45:06 +00:00
										 |  |  |                 } | 
					
						
							|  |  |  |               }}" | 
					
						
							|  |  |  |             > | 
					
						
							|  |  |  |               <span class="block-type-icon">${item.icon}</span> | 
					
						
							|  |  |  |               <span>${item.label}</span> | 
					
						
							|  |  |  |             </div> | 
					
						
							|  |  |  |           `)}
 | 
					
						
							|  |  |  |         </div> | 
					
						
							|  |  |  |       </div> | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-06-24 08:19:53 +00:00
										 |  |  | } |