diff --git a/src/components/ui/JoditEmailEditor.vue b/src/components/ui/JoditEmailEditor.vue index 7d594cb..ab8f256 100644 --- a/src/components/ui/JoditEmailEditor.vue +++ b/src/components/ui/JoditEmailEditor.vue @@ -194,7 +194,24 @@ watch( // ── API exposta ─────────────────────────────────────────────── defineExpose({ - insertHTML: (html) => jodit?.selection.insertHTML(html) + insertHTML: (html) => jodit?.selection.insertHTML(html), + // Salva markers da seleção atual antes do foco sair do editor + // (ex: usuário abre drawer e perde o cursor). Retorna o array de + // markers que pode ser passado pra restoreSelection depois. + saveSelection: () => { + if (!jodit) return null; + try { return jodit.selection.save(); } + catch { return null; } + }, + // Restaura selection a partir dos markers salvos. Re-foca o editor. + restoreSelection: (markers) => { + if (!jodit) return; + try { + jodit.focus(); + if (markers) jodit.selection.restore(markers); + } catch { /* silencioso */ } + }, + focus: () => jodit?.focus() }); diff --git a/src/features/documents/components/DocumentTemplateEditor.vue b/src/features/documents/components/DocumentTemplateEditor.vue index 1dc0bb9..ae026d1 100644 --- a/src/features/documents/components/DocumentTemplateEditor.vue +++ b/src/features/documents/components/DocumentTemplateEditor.vue @@ -66,31 +66,43 @@ const editorRodape = ref(null) function insertVariable(varKey) { const tag = `{{${varKey}}}` - - // No mobile: 1) fecha drawer (imediato); 2) deferir append do form e - // re-render do Jodit pra DEPOIS da transição (250ms). Modificar o form - // durante a transição causa concorrência entre repaint do drawer - // saindo + Jodit re-rendering com novo HTML → trava. - if (isMobile.value) { - drawerOpen.value = false; - const field = cursorField.value; - // setTimeout > duração da transição CSS (.dte-mobile-drawer = 250ms) - setTimeout(() => { - form.value[field] = (form.value[field] || '') + tag; - if (!form.value.variaveis.includes(varKey)) { - form.value.variaveis = [...form.value.variaveis, varKey]; - } - }, 280); - return; - } - - // Desktop: insertHTML mantém posição do cursor const editorMap = { cabecalho_html: editorCabecalho, corpo_html: editorCorpo, rodape_html: editorRodape } const editorRef = editorMap[cursorField.value] + + // No mobile: fecha drawer + defere insertHTML pós-transição. + // Restaura a selection capturada quando o drawer abriu (cursor + // original do usuário) antes de inserir → variável aparece no + // ponto certo do texto, não no final. + if (isMobile.value) { + drawerOpen.value = false; + const markers = savedSelection.value; + setTimeout(() => { + try { + if (markers && editorRef?.value?.restoreSelection) { + editorRef.value.restoreSelection(markers); + } + if (editorRef?.value?.insertHTML) { + editorRef.value.insertHTML(tag); + } else { + // Fallback se a API expose falhar + form.value[cursorField.value] = (form.value[cursorField.value] || '') + tag; + } + } catch { + form.value[cursorField.value] = (form.value[cursorField.value] || '') + tag; + } + if (!form.value.variaveis.includes(varKey)) { + form.value.variaveis = [...form.value.variaveis, varKey]; + } + savedSelection.value = null; + }, 280); + return; + } + + // Desktop: insertHTML mantém posição do cursor (foco já tá no editor) if (editorRef?.value?.insertHTML) { editorRef.value.insertHTML(tag) } else { @@ -119,11 +131,32 @@ function _onMqMobileChange(e) { isMobile.value = e.matches; if (!e.matches) drawerOpen.value = false; } +// Selection salva do editor ativo no momento de abrir o drawer de +// variáveis. Permite inserir na posição original do cursor mesmo +// depois do user navegar pelo drawer/perder foco. +const savedSelection = ref(null); + function openDrawer(tab) { drawerTab.value = tab || 'form'; + // Quando abre "Variáveis", salva selection do editor ativo agora + // (cursor original do usuário) pra restaurar depois da inserção. + if (tab === 'vars') { + const editorMap = { + cabecalho_html: editorCabecalho, + corpo_html: editorCorpo, + rodape_html: editorRodape + }; + const editorRef = editorMap[cursorField.value]; + savedSelection.value = editorRef?.value?.saveSelection?.() || null; + } else { + savedSelection.value = null; + } drawerOpen.value = true; } -function fecharDrawer() { drawerOpen.value = false; } +function fecharDrawer() { + drawerOpen.value = false; + savedSelection.value = null; +} onMounted(() => { if (typeof window !== 'undefined' && window.matchMedia) {