blueprint/lib/StandardElementGenerator.js
2025-03-27 17:29:28 +01:00

179 lines
No EOL
5.6 KiB
JavaScript

/**
* Generates HTML for standard HTML elements.
*/
class StandardElementGenerator {
/**
* Creates a new StandardElementGenerator instance.
* @param {Object} options - Generator options
* @param {CSSGenerator} cssGenerator - CSS generator instance
* @param {HTMLGenerator} htmlGenerator - HTML generator instance
*/
constructor(options = {}, cssGenerator, htmlGenerator) {
this.options = options;
this.cssGenerator = cssGenerator;
this.htmlGenerator = htmlGenerator;
this.parentGenerator = htmlGenerator;
}
/**
* Determines whether this generator can handle a given node.
* @param {Object} node - Node to check
* @returns {boolean} - True if this generator can handle the node
*/
canHandle(node) {
return node.type === "element";
}
/**
* Generates HTML for a standard element.
* @param {Object} node - Node to generate HTML for
* @returns {string} - Generated HTML
*/
generate(node) {
if (this.options.debug) {
console.log(`[StandardElementGenerator] Processing element node: ${node.tag || node.name}`);
}
const mapping = ELEMENT_MAPPINGS ? ELEMENT_MAPPINGS[node.tag] : null;
const tagName = mapping ? mapping.tag : (node.name || node.tag || "div");
let className = "";
if (this.cssGenerator) {
className = this.cssGenerator.generateClassName(node.tag);
const { cssProps, nestedRules } = this.cssGenerator.nodeToCSSProperties(node);
this.cssGenerator.cssRules.set(`.${className}`, {
cssProps,
nestedRules,
});
}
const fullClassName = this.generateClassName(node);
if (className && fullClassName) {
className = `${className} ${fullClassName}`;
} else if (fullClassName) {
className = fullClassName;
}
const attributes = this.generateAttributes(node, node.elementId, className);
let content = "";
const children = node.children || [];
for (const child of children) {
if (
child.type === "client" ||
child.type === "server" ||
(child.type === "element" && (child.name === "script" || child.tag === "script"))
) {
continue;
}
child.parent = node;
content += this.htmlGenerator.generateHTML(child);
if (!this.options.minified) {
content += "\n";
}
}
return `<${tagName}${attributes}>${this.options.minified ? "" : "\n"}${content}${this.options.minified ? "" : ""}</${tagName}>${this.options.minified ? "" : "\n"}`;
}
/**
* Generates a className string for the element.
* @param {Object} node - The node to generate class for
* @returns {string} - The generated class name
*/
generateClassName(node) {
let classNames = [];
if (node.attributes && Array.isArray(node.attributes)) {
const classAttr = node.attributes.find(attr => attr.name === "class");
if (classAttr && classAttr.value) {
classNames.push(classAttr.value);
}
}
if (node.props && Array.isArray(node.props)) {
for (const prop of node.props) {
if (typeof prop === "string") {
if (prop.startsWith("class:")) {
classNames.push(prop.substring(prop.indexOf(":") + 1).trim().replace(/^"|"$/g, ""));
}
}
}
}
return classNames.join(" ");
}
/**
* Generates an attributes string for the element.
* @param {Object} node - The node to generate attributes for
* @param {string} id - The element ID
* @param {string} className - The element class name
* @returns {string} - The generated attributes string
*/
generateAttributes(node, id, className) {
let attributes = "";
if (id) {
attributes += ` id="${id}"`;
} else if (node.props && Array.isArray(node.props)) {
const idProp = node.props.find(p => typeof p === "string" && p.startsWith("id:"));
if (idProp) {
const idValue = idProp.substring(idProp.indexOf(":") + 1).trim().replace(/^"|"$/g, "");
attributes += ` id="${idValue}"`;
node.elementId = idValue;
}
}
if (className) {
attributes += ` class="${className}"`;
}
if (node.props && Array.isArray(node.props)) {
const dataProps = node.props.filter(p => typeof p === "string" && p.startsWith("data-"));
if (dataProps.length) {
attributes += " " + dataProps.join(" ");
}
}
if (node.attributes && Array.isArray(node.attributes)) {
for (const attr of node.attributes) {
if (attr.name === "id" || attr.name === "class") continue;
if (attr.value === true) {
attributes += ` ${attr.name}`;
} else if (attr.value !== false && attr.value !== undefined && attr.value !== null) {
attributes += ` ${attr.name}="${attr.value}"`;
}
}
}
if (node.props && Array.isArray(node.props)) {
for (const prop of node.props) {
if (typeof prop === "string") {
if (prop.startsWith("id:") || prop.startsWith("class:") || prop.startsWith("data-")) continue;
const colonIndex = prop.indexOf(":");
if (colonIndex !== -1) {
const name = prop.substring(0, colonIndex).trim();
const value = prop.substring(colonIndex + 1).trim().replace(/^"|"$/g, "");
if (!attributes.includes(` ${name}="`)) {
attributes += ` ${name}="${value}"`;
}
} else {
if (!attributes.includes(` ${prop}`)) {
attributes += ` ${prop}`;
}
}
}
}
}
return attributes;
}
}
module.exports = StandardElementGenerator;