fix(web): jinja render template transform
This commit is contained in:
@@ -118,6 +118,7 @@ const Jinja2AutocompletePlugin: FC<{ options: Suggestion[] }> = ({ options }) =>
|
|||||||
selection.focus.offset = newOffset;
|
selection.focus.offset = newOffset;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
document.dispatchEvent(new CustomEvent('jinja2-variable-inserted', { detail: { value: suggestion.value } }));
|
||||||
setShowSuggestions(false);
|
setShowSuggestions(false);
|
||||||
setExpandedParent(null);
|
setExpandedParent(null);
|
||||||
setChildPanelTop(0);
|
setChildPanelTop(0);
|
||||||
|
|||||||
@@ -34,15 +34,23 @@ const JinjaRender: FC<JinjaRenderProps> = ({ selectedNode, options, templateOpti
|
|||||||
const prevMappingNamesRef = useRef<string[]>([])
|
const prevMappingNamesRef = useRef<string[]>([])
|
||||||
const prevTemplateVarsRef = useRef<string[]>([])
|
const prevTemplateVarsRef = useRef<string[]>([])
|
||||||
const isSyncingRef = useRef(false)
|
const isSyncingRef = useRef(false)
|
||||||
const lastSyncSourceRef = useRef<'mapping' | 'template' | null>(null)
|
|
||||||
const editorKeyRef = useRef(0)
|
const editorKeyRef = useRef(0)
|
||||||
|
const insertedVarsRef = useRef<Set<string>>(new Set())
|
||||||
|
|
||||||
|
// Collect variables inserted via autocomplete
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = (e: Event) => {
|
||||||
|
insertedVarsRef.current.add((e as CustomEvent).detail.value)
|
||||||
|
}
|
||||||
|
document.addEventListener('jinja2-variable-inserted', handler)
|
||||||
|
return () => document.removeEventListener('jinja2-variable-inserted', handler)
|
||||||
|
}, [])
|
||||||
|
|
||||||
// Reset refs when node changes
|
// Reset refs when node changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedNode?.getData()?.id) {
|
if (selectedNode?.getData()?.id) {
|
||||||
prevMappingNamesRef.current = []
|
prevMappingNamesRef.current = []
|
||||||
prevTemplateVarsRef.current = []
|
prevTemplateVarsRef.current = []
|
||||||
lastSyncSourceRef.current = null
|
|
||||||
}
|
}
|
||||||
}, [selectedNode?.getData()?.id])
|
}, [selectedNode?.getData()?.id])
|
||||||
|
|
||||||
@@ -50,7 +58,6 @@ const JinjaRender: FC<JinjaRenderProps> = ({ selectedNode, options, templateOpti
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
isSyncingRef.current ||
|
isSyncingRef.current ||
|
||||||
lastSyncSourceRef.current === 'mapping' ||
|
|
||||||
selectedNode?.data?.type !== 'jinja-render' ||
|
selectedNode?.data?.type !== 'jinja-render' ||
|
||||||
!values?.mapping ||
|
!values?.mapping ||
|
||||||
!values?.template
|
!values?.template
|
||||||
@@ -81,101 +88,63 @@ const JinjaRender: FC<JinjaRenderProps> = ({ selectedNode, options, templateOpti
|
|||||||
|
|
||||||
if (updatedTemplate !== form.getFieldValue('template')) {
|
if (updatedTemplate !== form.getFieldValue('template')) {
|
||||||
isSyncingRef.current = true
|
isSyncingRef.current = true
|
||||||
lastSyncSourceRef.current = 'mapping'
|
|
||||||
|
|
||||||
prevTemplateVarsRef.current = extractTemplateVars(updatedTemplate)
|
prevTemplateVarsRef.current = extractTemplateVars(updatedTemplate)
|
||||||
prevMappingNamesRef.current = currentMappingNames
|
prevMappingNamesRef.current = currentMappingNames
|
||||||
form.setFieldValue('template', updatedTemplate)
|
form.setFieldValue('template', updatedTemplate)
|
||||||
editorKeyRef.current++
|
editorKeyRef.current++
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => { isSyncingRef.current = false }, 0)
|
||||||
isSyncingRef.current = false
|
|
||||||
lastSyncSourceRef.current = null
|
|
||||||
}, 0)
|
|
||||||
} else {
|
} else {
|
||||||
prevMappingNamesRef.current = currentMappingNames
|
prevMappingNamesRef.current = currentMappingNames
|
||||||
}
|
}
|
||||||
}, [values?.mapping, selectedNode?.data?.type, form])
|
}, [values?.mapping, selectedNode?.data?.type, form])
|
||||||
|
|
||||||
// Sync mapping when template variables change
|
// Track template vars; add mapping only for autocomplete-inserted variables
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (isSyncingRef.current || selectedNode?.data?.type !== 'jinja-render' || !values?.template) return
|
||||||
isSyncingRef.current ||
|
|
||||||
lastSyncSourceRef.current === 'template' ||
|
|
||||||
selectedNode?.data?.type !== 'jinja-render' ||
|
|
||||||
!values?.template ||
|
|
||||||
!values?.mapping
|
|
||||||
) return
|
|
||||||
|
|
||||||
const templateVars = extractTemplateVars(String(values.template))
|
const templateVars = extractTemplateVars(String(values.template))
|
||||||
if (JSON.stringify(prevTemplateVarsRef.current) === JSON.stringify(templateVars)) return
|
const prevVars = prevTemplateVarsRef.current
|
||||||
|
|
||||||
const isTemplateEditor = document.activeElement?.closest('[data-editor-type="template"]')
|
if (JSON.stringify(prevVars) === JSON.stringify(templateVars)) return
|
||||||
if (!isTemplateEditor) {
|
|
||||||
prevTemplateVarsRef.current = templateVars
|
const newVars = templateVars.filter(v => !prevVars.includes(v))
|
||||||
return
|
const insertedNew = newVars.filter(v => insertedVarsRef.current.has(v))
|
||||||
}
|
insertedVarsRef.current.clear()
|
||||||
|
|
||||||
|
prevTemplateVarsRef.current = templateVars
|
||||||
|
|
||||||
|
if (insertedNew.length === 0 || !values?.mapping) return
|
||||||
|
|
||||||
const updatedMapping: MappingItem[] = Array.isArray(values.mapping)
|
const updatedMapping: MappingItem[] = Array.isArray(values.mapping)
|
||||||
? [...values.mapping.filter((item: MappingItem) => item)]
|
? [...values.mapping.filter((item: MappingItem) => item)]
|
||||||
: []
|
: []
|
||||||
const existingNames = getMappingNames(updatedMapping)
|
|
||||||
let updatedTemplate = String(values.template)
|
let updatedTemplate = String(values.template)
|
||||||
|
|
||||||
// Update existing mapping names based on position
|
insertedNew.forEach(varName => {
|
||||||
if (prevTemplateVarsRef.current.length > 0) {
|
const alreadyExists = updatedMapping.some(item => item.value === `{{${varName}}}`)
|
||||||
prevTemplateVarsRef.current.forEach((oldVar, index) => {
|
const baseName = varName.includes('.') ? varName.split('.').pop()! : varName
|
||||||
const newVar = templateVars[index]
|
const regex = new RegExp(`{{\\s*${varName.replace(/\./, '\\.')}\\s*}}`, 'g')
|
||||||
if (newVar && oldVar !== newVar && updatedMapping[index]) {
|
if (alreadyExists) {
|
||||||
updatedMapping[index] = { ...updatedMapping[index], name: newVar }
|
const existing = updatedMapping.find(item => item.value === `{{${varName}}}`)!
|
||||||
}
|
updatedTemplate = updatedTemplate.replace(regex, `{{${existing.name}}}`)
|
||||||
})
|
return
|
||||||
}
|
|
||||||
|
|
||||||
// Add new mappings and normalize template
|
|
||||||
templateVars.forEach(varName => {
|
|
||||||
const existingMapping = updatedMapping.find(item => item.value === `{{${varName}}}`)
|
|
||||||
const regex = new RegExp(`{{\\s*${varName.replace(/\./g, '\\.')}\\s*}}`, 'g')
|
|
||||||
|
|
||||||
if (existingMapping) {
|
|
||||||
updatedTemplate = updatedTemplate.replace(regex, `{{${existingMapping.name}}}`)
|
|
||||||
} else if (!existingNames.includes(varName)) {
|
|
||||||
const baseName = varName.includes('.') ? varName.split('.').pop() || varName : varName
|
|
||||||
const usedNames = getMappingNames(updatedMapping)
|
|
||||||
let mappingName = baseName
|
|
||||||
let counter = 1
|
|
||||||
while (usedNames.includes(mappingName)) {
|
|
||||||
mappingName = `${baseName}_${counter++}`
|
|
||||||
}
|
|
||||||
updatedMapping.push({ name: mappingName, value: `{{${varName}}}` })
|
|
||||||
updatedTemplate = updatedTemplate.replace(regex, `{{${mappingName}}}`)
|
|
||||||
}
|
}
|
||||||
})
|
const usedNames = getMappingNames(updatedMapping)
|
||||||
|
let mappingName = baseName
|
||||||
// Remove duplicates only
|
let counter = 1
|
||||||
const seenNames = new Set<string>()
|
while (usedNames.includes(mappingName)) mappingName = `${baseName}_${counter++}`
|
||||||
const finalMapping = updatedMapping.filter(item => {
|
updatedMapping.push({ name: mappingName, value: `{{${varName}}}` })
|
||||||
if (!item.name || seenNames.has(item.name)) return false
|
updatedTemplate = updatedTemplate.replace(regex, `{{${mappingName}}}`)
|
||||||
seenNames.add(item.name)
|
|
||||||
return true
|
|
||||||
})
|
})
|
||||||
|
|
||||||
isSyncingRef.current = true
|
isSyncingRef.current = true
|
||||||
lastSyncSourceRef.current = 'template'
|
prevMappingNamesRef.current = getMappingNames(updatedMapping)
|
||||||
prevMappingNamesRef.current = getMappingNames(finalMapping)
|
prevTemplateVarsRef.current = extractTemplateVars(updatedTemplate)
|
||||||
prevTemplateVarsRef.current = templateVars
|
form.setFieldValue('mapping', updatedMapping)
|
||||||
|
form.setFieldValue('template', updatedTemplate)
|
||||||
if (JSON.stringify(finalMapping) !== JSON.stringify(values.mapping)) {
|
editorKeyRef.current++
|
||||||
form.setFieldValue('mapping', finalMapping)
|
setTimeout(() => { isSyncingRef.current = false }, 0)
|
||||||
}
|
|
||||||
if (updatedTemplate !== String(values.template)) {
|
|
||||||
form.setFieldValue('template', updatedTemplate)
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
isSyncingRef.current = false
|
|
||||||
lastSyncSourceRef.current = null
|
|
||||||
}, 50)
|
|
||||||
}, [values?.template, selectedNode?.data?.type, form])
|
}, [values?.template, selectedNode?.data?.type, form])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user