Enhance fcTemplateLang Schema with New Style Attributes and Placeholder Support

- Added 'margin', 'header', and 'footer' to the list of style attributes, allowing for more flexible styling options.
- Updated the logic for 'vertical', 'header', and 'footer' to include boolean values in their attribute definitions.
- Introduced pagination placeholders ('!pageNumber' and '!totalPages') to support dynamic content in templates.
- Implemented a completion source function for placeholders, enhancing user experience by providing context-aware suggestions.
This commit is contained in:
Tom Butcher 2026-09-15 01:02:07 +01:00
parent eea7e1f724
commit c9751dd5ef

View File

@ -3,8 +3,11 @@
* Mirrors custom elements / style attrs handled in farmcontrol-ws TemplateManager. * Mirrors custom elements / style attrs handled in farmcontrol-ws TemplateManager.
*/ */
import { syntaxTree } from '@codemirror/language'
const styleAttrs = [ const styleAttrs = [
'padding', 'padding',
'margin',
'width', 'width',
'height', 'height',
'minWidth', 'minWidth',
@ -17,6 +20,8 @@ const styleAttrs = [
'border', 'border',
'borderRadius', 'borderRadius',
'vertical', 'vertical',
'header',
'footer',
'grow', 'grow',
'shrink', 'shrink',
'color', 'color',
@ -48,7 +53,9 @@ const alignValues = [
export const fcTemplateAttributes = [ export const fcTemplateAttributes = [
...styleAttrs.map((name) => { ...styleAttrs.map((name) => {
const attr = { name, global: true } const attr = { name, global: true }
if (name === 'vertical') attr.values = ['true', 'false'] if (name === 'vertical' || name === 'header' || name === 'footer') {
attr.values = ['true', 'false']
}
if (name === 'justify') attr.values = justifyValues if (name === 'justify') attr.values = justifyValues
if (name === 'align') attr.values = alignValues if (name === 'align') attr.values = alignValues
if (name === 'textAlign') { if (name === 'textAlign') {
@ -141,6 +148,69 @@ export const fcEmailTemplateHelpers = {
} }
} }
/** Pagination placeholders replaced after layout (`!pageNumber`, `!totalPages`). */
export const fcTemplatePlaceholders = [
{
name: '!pageNumber',
detail: 'Current page number'
},
{
name: '!totalPages',
detail: 'Total page count'
}
]
export function placeholderCompletionSource() {
const options = fcTemplatePlaceholders.map((placeholder) => ({
label: placeholder.name,
apply: placeholder.name,
type: 'variable',
detail: placeholder.detail
}))
return (context) => {
if (isPlaceholderBlockedContext(context.state, context.pos)) {
return null
}
const before = context.state.sliceDoc(Math.max(0, context.pos - 48), context.pos)
const match = before.match(/![a-zA-Z]*$/)
if (!match) {
return null
}
return {
from: context.pos - match[0].length,
options
}
}
}
function isPlaceholderBlockedContext(state, position) {
let node = syntaxTree(state).resolveInner(position, -1)
while (node) {
if (
node.name === 'AttributeValue' ||
node.name === 'AttributeName' ||
node.name === 'Attribute' ||
node.name === 'OpenTag' ||
node.name === 'CloseTag' ||
node.name === 'StartTag' ||
node.name === 'TagName' ||
node.name === 'JavascriptExpression' ||
node.name === 'Scriptlet' ||
node.name === 'Output' ||
node.name === 'Comment'
) {
return true
}
node = node.parent
}
return false
}
export const fcTemplateHelpers = fcDocumentTemplateHelpers export const fcTemplateHelpers = fcDocumentTemplateHelpers
export function getFcTemplateHelpers(templateType) { export function getFcTemplateHelpers(templateType) {