templates/editor: variavel mobile insere na posicao do cursor
Antes: variavel inseria sempre no fim do texto no mobile.
Causa: usavamos append direto no form (form[field] += tag) porque
o foco estava no drawer e Jodit.insertHTML travava.
Fix: capturar selection ANTES do drawer abrir, restaurar antes de
inserir.
JoditEmailEditor expose API estendida:
- saveSelection() -> retorna markers (jodit.selection.save())
- restoreSelection(markers) -> re-foca editor + restaura markers
- focus() -> foca o editor
DocumentTemplateEditor:
- ref savedSelection capturada em openDrawer('vars'): snapshot dos
markers do Jodit no momento (cursor original)
- insertVariable mobile: setTimeout 280ms apos fechar drawer ->
restaura markers -> insertHTML (cursor volta pra onde estava ->
variavel aparece no ponto exato)
- Fallback append no form se restore falhar
- savedSelection limpa em fecharDrawer + apos insert
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -194,7 +194,24 @@ watch(
|
|||||||
|
|
||||||
// ── API exposta ───────────────────────────────────────────────
|
// ── API exposta ───────────────────────────────────────────────
|
||||||
defineExpose({
|
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()
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -66,31 +66,43 @@ const editorRodape = ref(null)
|
|||||||
|
|
||||||
function insertVariable(varKey) {
|
function insertVariable(varKey) {
|
||||||
const tag = `{{${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 = {
|
const editorMap = {
|
||||||
cabecalho_html: editorCabecalho,
|
cabecalho_html: editorCabecalho,
|
||||||
corpo_html: editorCorpo,
|
corpo_html: editorCorpo,
|
||||||
rodape_html: editorRodape
|
rodape_html: editorRodape
|
||||||
}
|
}
|
||||||
const editorRef = editorMap[cursorField.value]
|
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) {
|
if (editorRef?.value?.insertHTML) {
|
||||||
editorRef.value.insertHTML(tag)
|
editorRef.value.insertHTML(tag)
|
||||||
} else {
|
} else {
|
||||||
@@ -119,11 +131,32 @@ function _onMqMobileChange(e) {
|
|||||||
isMobile.value = e.matches;
|
isMobile.value = e.matches;
|
||||||
if (!e.matches) drawerOpen.value = false;
|
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) {
|
function openDrawer(tab) {
|
||||||
drawerTab.value = tab || 'form';
|
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;
|
drawerOpen.value = true;
|
||||||
}
|
}
|
||||||
function fecharDrawer() { drawerOpen.value = false; }
|
function fecharDrawer() {
|
||||||
|
drawerOpen.value = false;
|
||||||
|
savedSelection.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (typeof window !== 'undefined' && window.matchMedia) {
|
if (typeof window !== 'undefined' && window.matchMedia) {
|
||||||
|
|||||||
Reference in New Issue
Block a user