mirror of https://github.com/docusealco/docuseal
				
				
				
			
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							132 lines
						
					
					
						
							4.0 KiB
						
					
					
				
			
		
		
	
	
							132 lines
						
					
					
						
							4.0 KiB
						
					
					
				| import { target, targetable } from '@github/catalyst/lib/targetable'
 | |
| 
 | |
| let loaderPromise = null
 | |
| 
 | |
| function loadCodeMirror () {
 | |
|   if (!loaderPromise) {
 | |
|     loaderPromise = Promise.all([
 | |
|       import(/* webpackChunkName: "email-editor" */ '@codemirror/view'),
 | |
|       import(/* webpackChunkName: "email-editor" */ '@codemirror/commands'),
 | |
|       import(/* webpackChunkName: "email-editor" */ '@codemirror/language'),
 | |
|       import(/* webpackChunkName: "email-editor" */ '@codemirror/lang-html'),
 | |
|       import(/* webpackChunkName: "email-editor" */ '@specious/htmlflow')
 | |
|     ]).then(([view, commands, language, html, htmlflow]) => {
 | |
|       return {
 | |
|         minimalSetup: [
 | |
|           commands.history(),
 | |
|           language.syntaxHighlighting(language.defaultHighlightStyle, { fallback: true }),
 | |
|           view.keymap.of([...commands.defaultKeymap, ...commands.historyKeymap])
 | |
|         ],
 | |
|         EditorView: view.EditorView,
 | |
|         html: html.html,
 | |
|         htmlflow: htmlflow.default || htmlflow
 | |
|       }
 | |
|     })
 | |
|   }
 | |
| 
 | |
|   return loaderPromise
 | |
| }
 | |
| 
 | |
| export default targetable(class extends HTMLElement {
 | |
|   static [target.static] = [
 | |
|     'codeViewTab',
 | |
|     'previewViewTab',
 | |
|     'editorContainer',
 | |
|     'previewIframe'
 | |
|   ]
 | |
| 
 | |
|   connectedCallback () {
 | |
|     this.mount()
 | |
| 
 | |
|     if (this.input.value) {
 | |
|       this.showPreviewView()
 | |
|     } else {
 | |
|       this.showCodeView()
 | |
|     }
 | |
| 
 | |
|     this.previewViewTab.addEventListener('click', this.showPreviewView)
 | |
|     this.codeViewTab.addEventListener('click', this.showCodeView)
 | |
|   }
 | |
| 
 | |
|   showCodeView = () => {
 | |
|     this.editorView.dispatch({
 | |
|       changes: { from: 0, to: this.editorView.state.doc.length, insert: this.input.value }
 | |
|     })
 | |
| 
 | |
|     this.previewViewTab.classList.remove('tab-active', 'tab-bordered')
 | |
|     this.previewViewTab.classList.add('pb-[3px]')
 | |
|     this.codeViewTab.classList.remove('pb-[3px]')
 | |
|     this.codeViewTab.classList.add('tab-active', 'tab-bordered')
 | |
|     this.editorContainer.classList.remove('hidden')
 | |
|     this.previewIframe.classList.add('hidden')
 | |
|   }
 | |
| 
 | |
|   showPreviewView = () => {
 | |
|     this.previewIframe.srcdoc = this.input.value
 | |
| 
 | |
|     this.codeViewTab.classList.remove('tab-active', 'tab-bordered')
 | |
|     this.codeViewTab.classList.add('pb-[3px]')
 | |
|     this.previewViewTab.classList.remove('pb-[3px]')
 | |
|     this.previewViewTab.classList.add('tab-active', 'tab-bordered')
 | |
|     this.editorContainer.classList.add('hidden')
 | |
|     this.previewIframe.classList.remove('hidden')
 | |
|   }
 | |
| 
 | |
|   async mount () {
 | |
|     this.input = this.querySelector('input[type="hidden"]')
 | |
|     this.input.style.display = 'none'
 | |
| 
 | |
|     const { EditorView, minimalSetup, html, htmlflow } = await loadCodeMirror()
 | |
| 
 | |
|     this.editorView = new EditorView({
 | |
|       doc: this.input.value,
 | |
|       parent: this.editorContainer,
 | |
|       extensions: [
 | |
|         html(),
 | |
|         minimalSetup,
 | |
|         EditorView.lineWrapping,
 | |
|         EditorView.updateListener.of(update => {
 | |
|           if (update.docChanged) this.input.value = update.state.doc.toString()
 | |
|         }),
 | |
|         EditorView.theme({
 | |
|           '&': {
 | |
|             backgroundColor: 'white',
 | |
|             color: 'black',
 | |
|             fontSize: '14px',
 | |
|             fontFamily: 'monospace'
 | |
|           },
 | |
|           '&.cm-focused': {
 | |
|             outline: 'none'
 | |
|           },
 | |
|           '&.cm-editor': {
 | |
|             borderRadius: '0.375rem',
 | |
|             border: 'none'
 | |
|           },
 | |
|           '.cm-gutters': {
 | |
|             display: 'none'
 | |
|           }
 | |
|         })
 | |
|       ]
 | |
|     })
 | |
| 
 | |
|     this.previewIframe.srcdoc = this.editorView.state.doc.toString()
 | |
| 
 | |
|     this.previewIframe.onload = () => {
 | |
|       const previewIframeDoc = this.previewIframe.contentDocument
 | |
| 
 | |
|       if (previewIframeDoc.body) {
 | |
|         previewIframeDoc.body.contentEditable = true
 | |
|       }
 | |
| 
 | |
|       const contentDocument = this.previewIframe.contentDocument || this.previewIframe.contentWindow.document
 | |
| 
 | |
|       contentDocument.body.addEventListener('input', async () => {
 | |
|         const html = contentDocument.documentElement.outerHTML.replace(' contenteditable="true"', '')
 | |
|         const prettifiedHtml = await htmlflow(html)
 | |
| 
 | |
|         this.input.value = prettifiedHtml
 | |
|       })
 | |
|     }
 | |
|   }
 | |
| })
 |