import type {
Component,
ComponentData,
ElementNodeModel,
} from '@toddledev/core/dist/component/component.types'
import type {
FormulaContext,
ToddleEnv,
} from '@toddledev/core/dist/formula/formula'
import { applyFormula } from '@toddledev/core/dist/formula/formula'
import { isDefined, toBoolean } from '@toddledev/core/dist/utils/util'
const REGEXP_QUOTE = /"/g
const REGEXP_LT = //g
const validAttrTypes = ['string', 'number', 'boolean']
export const escapeAttrValue = (value: any) => {
if (!isDefined(value) || !validAttrTypes.includes(typeof value)) {
return ''
}
return escapeHtml(escapeQuote(String(value)))
}
const escapeQuote = (value: string) => {
return value.replace(REGEXP_QUOTE, '"')
}
const escapeHtml = (html: string) => {
return html.replace(REGEXP_LT, '<').replace(REGEXP_GT, '>')
}
/**
* Escape a string to valid HTML text similar to how set innerText would work in the browser
*/
export const toEncodedText = (str: string) => {
return str
.replaceAll('&', '&')
.replaceAll('<', '<')
.replaceAll('>', '>')
.replaceAll('"', '"')
.replaceAll("'", ''')
.replaceAll('\n', '
')
}
export function getNodeAttrs({
node,
data,
component,
packageName,
env,
toddle,
}: {
node: Pick
data: ComponentData
component: Component
packageName: string | undefined
env: ToddleEnv
toddle: FormulaContext['toddle']
}) {
const { style, ...restAttrs } = node.attrs
const nodeAttrs = Object.entries(restAttrs).reduce(
(appliedAttributes, [name, attrValue]) => {
const value = applyFormula(attrValue, {
data,
component,
package: packageName,
env,
toddle,
})
if (toBoolean(value)) {
appliedAttributes.push(`${name}="${escapeAttrValue(value)}"`)
}
return appliedAttributes
},
[],
)
const styleVariables = Object.values(node['style-variables'] ?? {}).map(
({ name, formula, unit }) =>
`--${name}: ${
String(
applyFormula(formula, {
data,
component,
package: packageName,
env,
toddle,
}),
) + (unit ?? '')
}`,
)
// Handle the style-attribute independently to merge with style variables
const styles = [
...(style
? [
applyFormula(style, {
data,
component,
package: packageName,
env,
toddle,
}),
]
: []),
...styleVariables,
]
.filter(Boolean)
.map(String)
.join('; ')
if (styles.length > 0) {
return [...nodeAttrs, `style="${escapeAttrValue(styles)};"`].join(' ')
}
return nodeAttrs.join(' ')
}