@1urso/generic-editor 0.1.45 → 0.1.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12063,7 +12063,7 @@ const ElementContextMenu = ({ children: n, element: _ }) => {
12063
12063
  n.type
12064
12064
  ]);
12065
12065
  let aM = {
12066
- position: "absolute",
12066
+ position: "relative",
12067
12067
  left: 0,
12068
12068
  top: 0,
12069
12069
  width: "100%",
@@ -12071,13 +12071,14 @@ const ElementContextMenu = ({ children: n, element: _ }) => {
12071
12071
  minHeight: n.autoGrow ? "100%" : void 0,
12072
12072
  padding: n.type === "image" || n.type === "text" ? 0 : "8px",
12073
12073
  border: _ || B ? "2px solid var(--accent-9)" : "1px dashed transparent",
12074
- outline: _ || B ? "none" : "1px solid transparent",
12074
+ outline: "none",
12075
12075
  cursor: P ? "grabbing" : "grab",
12076
12076
  borderRadius: "var(--radius-2)",
12077
12077
  overflow: n.autoGrow ? "visible" : "hidden",
12078
12078
  whiteSpace: n.autoGrow ? "pre-wrap" : void 0,
12079
12079
  wordBreak: n.autoGrow ? "break-word" : void 0,
12080
12080
  userSelect: "none",
12081
+ boxSizing: "border-box",
12081
12082
  ...n.style,
12082
12083
  ...Qj
12083
12084
  };
@@ -12093,12 +12094,85 @@ const ElementContextMenu = ({ children: n, element: _ }) => {
12093
12094
  },
12094
12095
  onResizeStop: (_, E, A, j) => {
12095
12096
  H(!1);
12096
- let N = (n.width ?? 100) + j.width, P = (n.height ?? 100) + j.height;
12097
- n.type === "group" ? M(n.id, N, P) : O(n.id, {
12097
+ let N = (n.width ?? 100) + j.width, P = (n.height ?? 100) + j.height, z = n.x ?? 0, B = n.y ?? 0;
12098
+ E.includes("left") && (z -= j.width), E.includes("top") && (B -= j.height), n.type === "group" ? (M(n.id, N, P), O(n.id, {
12099
+ x: z,
12100
+ y: B
12101
+ }, !0)) : O(n.id, {
12098
12102
  width: N,
12099
- height: P
12103
+ height: P,
12104
+ x: z,
12105
+ y: B
12100
12106
  });
12101
12107
  },
12108
+ handleComponent: {
12109
+ topLeft: /* @__PURE__ */ jsx("div", { className: "resize-handle top-left" }),
12110
+ topRight: /* @__PURE__ */ jsx("div", { className: "resize-handle top-right" }),
12111
+ bottomLeft: /* @__PURE__ */ jsx("div", { className: "resize-handle bottom-left" }),
12112
+ bottomRight: /* @__PURE__ */ jsx("div", { className: "resize-handle bottom-right" }),
12113
+ top: /* @__PURE__ */ jsx("div", { className: "resize-handle top" }),
12114
+ bottom: /* @__PURE__ */ jsx("div", { className: "resize-handle bottom" }),
12115
+ left: /* @__PURE__ */ jsx("div", { className: "resize-handle left" }),
12116
+ right: /* @__PURE__ */ jsx("div", { className: "resize-handle right" })
12117
+ },
12118
+ handleStyles: {
12119
+ topLeft: {
12120
+ width: 10,
12121
+ height: 10,
12122
+ background: "var(--accent-9)",
12123
+ borderRadius: "50%",
12124
+ left: -5,
12125
+ top: -5,
12126
+ zIndex: 1001
12127
+ },
12128
+ topRight: {
12129
+ width: 10,
12130
+ height: 10,
12131
+ background: "var(--accent-9)",
12132
+ borderRadius: "50%",
12133
+ right: -5,
12134
+ top: -5,
12135
+ zIndex: 1001
12136
+ },
12137
+ bottomLeft: {
12138
+ width: 10,
12139
+ height: 10,
12140
+ background: "var(--accent-9)",
12141
+ borderRadius: "50%",
12142
+ left: -5,
12143
+ bottom: -5,
12144
+ zIndex: 1001
12145
+ },
12146
+ bottomRight: {
12147
+ width: 10,
12148
+ height: 10,
12149
+ background: "var(--accent-9)",
12150
+ borderRadius: "50%",
12151
+ right: -5,
12152
+ bottom: -5,
12153
+ zIndex: 1001
12154
+ },
12155
+ top: {
12156
+ height: 6,
12157
+ top: -3,
12158
+ zIndex: 1e3
12159
+ },
12160
+ bottom: {
12161
+ height: 6,
12162
+ bottom: -3,
12163
+ zIndex: 1e3
12164
+ },
12165
+ left: {
12166
+ width: 6,
12167
+ left: -3,
12168
+ zIndex: 1e3
12169
+ },
12170
+ right: {
12171
+ width: 6,
12172
+ right: -3,
12173
+ zIndex: 1e3
12174
+ }
12175
+ },
12102
12176
  style: {
12103
12177
  position: "absolute",
12104
12178
  transform: `translate(${n.x ?? 0}px, ${n.y ?? 0}px) rotate(${n.rotation || 0}deg)`,
@@ -12106,7 +12180,8 @@ const ElementContextMenu = ({ children: n, element: _ }) => {
12106
12180
  display: $j && !_ ? "none" : void 0,
12107
12181
  opacity: $j && _ ? .4 : 1,
12108
12182
  zIndex: _ ? 1e3 : void 0,
12109
- outline: _ ? "1px dashed var(--accent-9)" : void 0
12183
+ outline: "none",
12184
+ overflow: "visible"
12110
12185
  },
12111
12186
  enable: _ && !n.autoGrow ? void 0 : {
12112
12187
  top: !1,
@@ -18471,5 +18546,5 @@ const GenericEditor = (n) => /* @__PURE__ */ jsx(EditorProvider, {
18471
18546
  availableProps: n.layout.props,
18472
18547
  theme: n.theme,
18473
18548
  children: /* @__PURE__ */ jsx(EditorContent, { ...n })
18474
- }), generateHTML = (n, _, E = {}) => Function("elements", "data", "options", getRendererCode() + "\nreturn renderTemplate(elements, data, options);")(n, _, E), getRendererCode = () => "\n/**\n * Render Template\n * @param {Array} elements - The JSON configuration of elements\n * @param {Object|Array} data - The data object to inject (Object for single, Array for list)\n * @param {Object} options - { isList: boolean, listSettings: { sortProp: string, sortOrder: 'asc'|'desc', newestPosition: 'top'|'bottom', scrollDirection: 'up'|'down', containerHeight: number }, canvasHeight: number }\n * @returns {string} - The generated HTML string\n */\nfunction renderTemplate(elements, data, options = {}) {\n const { isList, listSettings, canvasHeight } = options;\n\n const measureTextHeight = (text, width, fontFamily, fontSize, lineHeightMultiplier = 1.2) => {\n if (!text) return 0;\n try {\n const canvas = document.createElement('canvas');\n const context = canvas.getContext('2d');\n if (!context) return 0;\n context.font = `${fontSize}px ${fontFamily}`;\n const words = String(text).split(' ');\n let line = '';\n let lineCount = 1;\n for (let i = 0; i < words.length; i++) {\n const testLine = line + words[i] + ' ';\n const metrics = context.measureText(testLine);\n const testWidth = metrics.width;\n if (testWidth > width && i > 0) {\n line = words[i] + ' ';\n lineCount++;\n } else {\n line = testLine;\n }\n }\n const explicitLines = String(text).split('\\n').length - 1;\n lineCount += explicitLines;\n return Math.ceil(lineCount * fontSize * lineHeightMultiplier);\n } catch (_) {\n return 0;\n }\n };\n\n const computeLayout = (elements, itemData) => {\n const layoutElements = JSON.parse(JSON.stringify(elements));\n \n const isInside = (inner, outer) => {\n const eps = 0.1;\n return (\n inner.x >= outer.x - eps &&\n inner.x + inner.width <= outer.x + outer.width + eps &&\n inner.y >= outer.y - eps &&\n inner.y + inner.height <= outer.y + outer.height + eps\n );\n };\n\n const autoGrowElements = layoutElements\n .filter(el => (el.type === 'text' || el.type === 'text-container') && el.autoGrow)\n .sort((a, b) => a.y - b.y);\n\n autoGrowElements.forEach(textEl => {\n let content = textEl.content;\n content = content.replace(/\\{\\{(.*?)\\}\\}/g, (match, key) => {\n const val = itemData[key.trim()];\n return val !== undefined && val !== null ? String(val) : match;\n });\n \n const fontSize = parseInt(String((textEl.style && textEl.style.fontSize) || 16));\n const fontFamily = String((textEl.style && textEl.style.fontFamily) || 'Arial');\n \n const isHorizontal = textEl.type === 'text-container' && textEl.containerExpansion === 'horizontal';\n \n if (isHorizontal) {\n // Horizontal expansion: Update width only\n // Requires canvas context which is available in measureTextHeight scope or we create new one\n // For simplicity, we can't easily access the measure logic here if it's not exposed, \n // but measureTextHeight is available in this scope.\n // However measureTextHeight calculates HEIGHT. We need WIDTH.\n \n try {\n const canvas = document.createElement('canvas');\n const context = canvas.getContext('2d');\n if (context) {\n context.font = `${fontSize}px ${fontFamily}`;\n const metrics = context.measureText(content);\n const padding = parseInt(String((textEl.style && textEl.style.padding) || 0)) * 2;\n const newWidth = Math.ceil(metrics.width + padding);\n if (newWidth > textEl.width) {\n textEl.width = newWidth;\n }\n }\n } catch(e) {}\n } else {\n // Vertical Expansion\n const measuredHeight = measureTextHeight(content, textEl.width, fontFamily, fontSize);\n const designHeight = textEl.height;\n const delta = measuredHeight - designHeight;\n \n if (delta > 0) {\n const originalBottom = textEl.y + designHeight;\n const originalTextRect = {\n x: textEl.x,\n y: textEl.y,\n width: textEl.width,\n height: designHeight\n };\n \n textEl.height = measuredHeight;\n \n layoutElements.forEach(other => {\n if (other.id === textEl.id) return;\n \n if (isInside(originalTextRect, other)) {\n other.height += delta;\n }\n \n if (other.y >= originalBottom) {\n other.y += delta;\n }\n });\n }\n }\n });\n \n let maxY = 0;\n layoutElements.forEach(el => {\n const bottom = el.y + el.height;\n if (bottom > maxY) maxY = bottom;\n });\n \n return { layoutElements, maxY };\n };\n\n const computeItemHeight = (elements, itemData, fallbackHeight) => {\n const { maxY } = computeLayout(elements, itemData);\n return fallbackHeight ? Math.max(maxY, fallbackHeight) : maxY;\n };\n\n const formatValue = (value, formatting) => {\n if (!formatting || formatting.type === 'text') return value !== undefined && value !== null ? String(value) : '';\n if (value === undefined || value === null) return '';\n\n if (formatting.type === 'boolean') {\n const isTrue = String(value) === 'true' || value === true || (typeof value === 'number' && value > 0);\n return isTrue ? (formatting.trueLabel || 'Sim') : (formatting.falseLabel || 'Não');\n }\n\n if (formatting.type === 'date') {\n try {\n const date = new Date(value);\n if (isNaN(date.getTime())) return String(value);\n \n if (formatting.dateFormat) {\n const d = date.getDate().toString().padStart(2, '0');\n const m = (date.getMonth() + 1).toString().padStart(2, '0');\n const y = date.getFullYear();\n const H = date.getHours().toString().padStart(2, '0');\n const M = date.getMinutes().toString().padStart(2, '0');\n const S = date.getSeconds().toString().padStart(2, '0');\n \n return formatting.dateFormat\n .replace('DD', d)\n .replace('MM', m)\n .replace('YYYY', String(y))\n .replace('HH', H)\n .replace('mm', M)\n .replace('ss', S);\n }\n return date.toLocaleDateString();\n } catch (e) { return String(value); }\n }\n\n if (formatting.type === 'number') {\n const num = parseFloat(value);\n if (isNaN(num)) return String(value);\n \n if (formatting.numberFormat === 'currency') {\n return (formatting.currencySymbol || 'R$') + ' ' + num.toFixed(formatting.decimalPlaces || 2);\n }\n if (formatting.numberFormat === 'percent') {\n return num.toFixed(formatting.decimalPlaces || 0) + '%';\n }\n if (formatting.decimalPlaces !== undefined) {\n return num.toFixed(formatting.decimalPlaces);\n }\n return num.toFixed(formatting.decimalPlaces || 0);\n }\n \n return String(value);\n };\n\n const checkCondition = (propValue, operator, ruleValue) => {\n const val = String(propValue).toLowerCase();\n const target = String(ruleValue).toLowerCase();\n \n switch (operator) {\n case 'equals': return val === target;\n case 'notEquals': return val !== target;\n case 'contains': return val.includes(target);\n case 'greaterThan': return parseFloat(val) > parseFloat(target);\n case 'lessThan': return parseFloat(val) < parseFloat(target);\n case 'truthy': return !!propValue;\n case 'falsy': return !propValue;\n default: return false;\n }\n };\n\n const camelToKebab = (string) => {\n return string.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase();\n };\n\n const hex8ToRgba = (hex) => {\n const m = /^#([0-9a-fA-F]{8})$/.exec(hex);\n if (!m) return hex;\n const h = m[1];\n const r = parseInt(h.slice(0, 2), 16);\n const g = parseInt(h.slice(2, 4), 16);\n const b = parseInt(h.slice(4, 6), 16);\n const a = parseInt(h.slice(6, 8), 16) / 255;\n return `rgba(${r}, ${g}, ${b}, ${a})`;\n };\n\n const styleObjectToString = (style) => {\n if (!style) return '';\n const pxProps = [\n 'width', 'height', 'top', 'left', 'right', 'bottom', \n 'fontSize', 'borderRadius', 'padding', 'margin', 'borderWidth',\n 'borderTopLeftRadius', 'borderTopRightRadius', 'borderBottomRightRadius', 'borderBottomLeftRadius'\n ];\n \n return Object.entries(style)\n .map(([key, value]) => {\n if (value === undefined || value === null) return '';\n const cssKey = camelToKebab(key);\n let cssValue = (typeof value === 'number' && pxProps.includes(key)) ? value + 'px' : value;\n if (typeof cssValue === 'string') {\n if (/^#([0-9a-fA-F]{8})$/.test(cssValue)) {\n cssValue = hex8ToRgba(cssValue);\n }\n }\n return `${cssKey}: ${cssValue}`;\n })\n .filter(Boolean)\n .join('; ');\n };\n\n const getAnimationStyles = (anim) => {\n if (!anim || anim.type === 'none') return {};\n return {\n animationName: anim.type,\n animationDuration: (anim.duration || 1) + 's',\n animationDelay: (anim.delay || 0) + 's',\n animationIterationCount: anim.iterationCount || 1,\n animationTimingFunction: anim.timingFunction || 'ease',\n animationFillMode: 'both'\n };\n };\n\n const keyframesCss = `\n @keyframes slideIn {\n from { opacity: 0; transform: translateY(20px); }\n to { opacity: 1; transform: translateY(0); }\n }\n @keyframes fadeIn { \n from { opacity: 0; } \n to { opacity: 1; } \n }\n @keyframes slideInLeft { \n from { opacity: 0; transform: translateX(-50px); } \n to { opacity: 1; transform: translateX(0); } \n }\n @keyframes slideInRight { \n from { opacity: 0; transform: translateX(50px); } \n to { opacity: 1; transform: translateX(0); } \n }\n @keyframes slideInUp { \n from { opacity: 0; transform: translateY(50px); } \n to { opacity: 1; transform: translateY(0); } \n }\n @keyframes slideInDown { \n from { opacity: 0; transform: translateY(-50px); } \n to { opacity: 1; transform: translateY(0); } \n }\n @keyframes zoomIn { \n from { opacity: 0; transform: scale(0.5); } \n to { opacity: 1; transform: scale(1); } \n }\n @keyframes bounceIn {\n 0% { opacity: 0; transform: scale(0.3); }\n 50% { opacity: 1; transform: scale(1.05); }\n 70% { transform: scale(0.9); }\n 100% { transform: scale(1); }\n }\n @keyframes pulse {\n 0% { transform: scale(1); }\n 50% { transform: scale(1.05); }\n 100% { transform: scale(1); }\n }\n @keyframes shake {\n 0%, 100% { transform: translateX(0); }\n 10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }\n 20%, 40%, 60%, 80% { transform: translateX(5px); }\n }\n @keyframes spin { \n from { transform: rotate(0deg); } \n to { transform: rotate(360deg); } \n }\n \n /* Improved / Smoother Animations */\n @keyframes smoothSlideUp {\n 0% { opacity: 0; transform: translateY(30px); }\n 100% { opacity: 1; transform: translateY(0); }\n }\n @keyframes popIn {\n 0% { opacity: 0; transform: scale(0.8) translateY(10px); }\n 100% { opacity: 1; transform: scale(1) translateY(0); }\n }\n @keyframes blurIn {\n 0% { opacity: 0; filter: blur(10px); }\n 100% { opacity: 1; filter: blur(0); }\n }\n `;\n\n const renderItem = (itemData, index = 0, offsetY = 0) => {\n const { layoutElements } = computeLayout(elements, itemData);\n \n // Group elements into logical rows based on Y position overlap to support dynamic height\n layoutElements.sort((a, b) => a.y - b.y);\n \n const rows = [];\n let currentRow = null;\n \n layoutElements.forEach(el => {\n const elBottom = el.y + el.height;\n if (!currentRow) {\n currentRow = { startY: el.y, endY: elBottom, elements: [el] };\n rows.push(currentRow);\n } else {\n // If this element starts significantly after the current row ends, start a new row\n if (el.y >= currentRow.endY - 5) { \n currentRow = { startY: el.y, endY: elBottom, elements: [el] };\n rows.push(currentRow);\n } else {\n currentRow.elements.push(el);\n currentRow.endY = Math.max(currentRow.endY, elBottom);\n }\n }\n });\n\n return rows.map(row => {\n // Find autoGrow elements in this row\n const flowElements = row.elements.filter(el => (el.type === 'text' || el.type === 'text-container') && el.autoGrow);\n \n // Calculate Row Min Height (based on static elements and initial size of flow elements)\n let minHeight = 0;\n row.elements.forEach(el => {\n // For static elements, bottom relative to row start\n // For flow elements, we also use their initial design height as minimum\n const bottom = (el.y - row.startY) + el.height;\n if (bottom > minHeight) minHeight = bottom;\n });\n\n const rowStyle = styleObjectToString({\n position: 'relative',\n width: '100%',\n minHeight: minHeight,\n });\n\n const childrenHtml = row.elements.map(element => {\n let content = element.content;\n let imgSrc = '';\n\n // Resolve Content & Formatting\n if (element.type === 'text' || element.type === 'text-container') {\n content = content.replace(/\\{\\{(.*?)\\}\\}/g, (match, key) => {\n const val = itemData[key.trim()];\n if (val === undefined || val === null) return match;\n if (element.formatting) {\n return formatValue(val, element.formatting);\n }\n return String(val);\n });\n } else if (element.type === 'image') {\n if (element.dataBinding) {\n const val = itemData[element.dataBinding];\n if (val !== undefined && val !== null) {\n imgSrc = String(val);\n } else {\n imgSrc = content;\n }\n } else {\n imgSrc = content.replace(/\\{\\{(.*?)\\}\\}/g, (match, key) => {\n const val = itemData[key.trim()];\n return val !== undefined && val !== null ? String(val) : match;\n });\n }\n }\n\n // Resolve Conditional Styles\n let conditionalStyles = {};\n if (element.conditions) {\n element.conditions.forEach(rule => {\n const propVal = itemData[rule.property];\n if (checkCondition(propVal, rule.operator, rule.value)) {\n conditionalStyles = { ...conditionalStyles, ...rule.style };\n }\n });\n }\n\n // Resolve Style Bindings\n let bindingStyles = {};\n if (element.styleBindings) {\n Object.entries(element.styleBindings).forEach(([styleProp, variableName]) => {\n const val = itemData[variableName];\n if (val !== undefined && val !== null) {\n bindingStyles[styleProp] = String(val);\n }\n });\n }\n\n const isFlow = flowElements.includes(element);\n const baseStyle = {\n position: isFlow ? 'relative' : 'absolute',\n left: isFlow ? undefined : element.x,\n top: isFlow ? undefined : (element.y - row.startY + offsetY),\n marginLeft: isFlow ? element.x : undefined,\n marginTop: isFlow ? (element.y - row.startY) : undefined,\n width: element.width,\n height: element.autoGrow ? 'auto' : element.height,\n transform: element.rotation ? `rotate(${element.rotation}deg)` : undefined,\n overflow: element.autoGrow ? 'visible' : 'hidden',\n whiteSpace: (element.type === 'text-container' && element.autoGrow && element.containerExpansion === 'horizontal') ? 'nowrap' : (element.autoGrow ? 'pre-wrap' : undefined),\n wordBreak: element.autoGrow ? 'break-word' : undefined,\n display: isFlow ? 'inline-block' : undefined,\n verticalAlign: isFlow ? 'top' : undefined,\n ...element.style,\n ...conditionalStyles,\n ...bindingStyles\n };\n \n const styleString = styleObjectToString(baseStyle);\n\n if (element.type === 'text' || element.type === 'text-container') {\n return `<div style=\"${styleString}\">${content}</div>`;\n } else if (element.type === 'image') {\n const imgStyle = styleObjectToString({\n width: '100%',\n height: '100%',\n objectFit: element.style?.objectFit || 'cover',\n display: 'block'\n });\n return `<div style=\"${styleString}\"><img src=\"${imgSrc}\" alt=\"Element\" style=\"${imgStyle}\" /></div>`;\n } else if (element.type === 'box') {\n return `<div style=\"${styleString}\"></div>`;\n } else if (element.type === 'checkbox') {\n let isChecked = false;\n if (element.dataBinding) {\n const val = itemData[element.dataBinding];\n isChecked = val === true || String(val) === 'true';\n }\n const checkboxStyle = styleObjectToString({\n ...baseStyle,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center'\n });\n return `<div style=\"${checkboxStyle}\"><input type=\"checkbox\" ${isChecked ? 'checked' : ''} disabled style=\"width:100%;height:100%;margin:0;\" /></div>`;\n }\n return '';\n }).join('\\n');\n \n return `<div class=\"list-row\" style=\"${rowStyle}\">${childrenHtml}</div>`;\n }).join('\\n');\n };\n\nif (isList && Array.isArray(data)) {\n // Calculate per-item height respecting autoGrow\n // Sort data\n let listData = [...data];\n if (listSettings && listSettings.sortProp) {\n const prop = listSettings.sortProp;\n const order = listSettings.sortOrder === 'asc' ? 1 : -1;\n listData.sort((a, b) => {\n const valA = a[prop];\n const valB = b[prop];\n if (valA < valB) return -1 * order;\n if (valA > valB) return 1 * order;\n return 0;\n });\n }\n\n // Handle newest position\n if (listSettings && listSettings.newestPosition === 'top') {\n listData.reverse();\n }\n\n // Generate HTML for all items\n const itemsHtml = listData.map((item, index) => {\n const itemHtml = renderItem(item, index, 0);\n const itemContainerStyle = styleObjectToString({\n position: 'relative',\n minHeight: canvasHeight + 'px',\n width: '100%'\n });\n\n return `<div class=\"list-item\" style=\"${itemContainerStyle}\">${itemHtml}</div>`;\n }).join('\\n');\n\n // Animation Styles based on settings\n const scrollDirection = (listSettings && listSettings.scrollDirection) || 'down';\n const containerHeight = (listSettings && listSettings.containerHeight) ? listSettings.containerHeight + 'px' : '100%';\n \n const justify = (listSettings && listSettings.newestPosition === 'top') ? 'flex-start' : 'flex-end';\n\n // Entry Animation from settings\n const entryAnim = listSettings && listSettings.entryAnimation ? listSettings.entryAnimation : { type: 'slideIn', duration: 0.3, timingFunction: 'ease-out' };\n const entryAnimName = entryAnim.type === 'none' ? 'none' : entryAnim.type;\n const entryAnimDuration = entryAnim.duration + 's';\n const entryAnimTiming = entryAnim.timingFunction || 'ease-out';\n\n const animationCss = `\n ${keyframesCss}\n\n .list-wrapper {\n display: flex;\n flex-direction: column;\n justify-content: ${justify};\n height: ${containerHeight};\n width: 100%;\n overflow-y: auto;\n overflow-x: hidden;\n box-sizing: border-box;\n padding: 10px;\n }\n .list-item {\n flex-shrink: 0;\n animation: ${entryAnimName} ${entryAnimDuration} ${entryAnimTiming};\n margin-bottom: 10px;\n width: 100%;\n position: relative;\n }\n `;\n \n const scrollScript = scrollDirection === 'up' \n ? `<script>\n document.addEventListener('DOMContentLoaded', () => {\n const wrapper = document.querySelector('.list-wrapper');\n if(wrapper) wrapper.scrollTop = wrapper.scrollHeight;\n });\n <\/script>`\n : '';\n\n // Inject Smart Script for Dynamic Updates\n const injectionScript = `\n <script>\n (function() {\n try {\n const elements = ${JSON.stringify(elements)};\n const formatValue = ${formatValue.toString()};\n const checkCondition = ${checkCondition.toString()};\n const camelToKebab = ${camelToKebab.toString()};\n const hex8ToRgba = ${hex8ToRgba.toString()};\n const styleObjectToString = ${styleObjectToString.toString()};\n const getAnimationStyles = ${getAnimationStyles.toString()};\n const renderItem = ${renderItem.toString()};\n\n const measureTextHeight = ${measureTextHeight.toString()};\n const computeLayout = ${computeLayout.toString()};\n const computeItemHeight = ${computeItemHeight.toString()};\n const itemHeightFallback = ${canvasHeight || 0};\n const newestPosition = \"${(listSettings && listSettings.newestPosition) || 'bottom'}\";\n const scrollDirection = \"${(listSettings && listSettings.scrollDirection) || 'down'}\";\n\n window.addItem = function(data) {\n const wrapper = document.querySelector('.list-wrapper');\n if (!wrapper) return;\n\n const itemHtml = renderItem(data, 0, 0);\n const itemContainerStyle = styleObjectToString({\n position: 'relative',\n minHeight: itemHeightFallback + 'px',\n width: '100%'\n });\n\n const div = document.createElement('div');\n div.className = 'list-item';\n div.setAttribute('style', itemContainerStyle);\n div.innerHTML = itemHtml;\n\n if (newestPosition === 'top') {\n wrapper.insertBefore(div, wrapper.firstChild);\n } else {\n wrapper.appendChild(div);\n }\n \n if (scrollDirection === 'up') {\n wrapper.scrollTop = wrapper.scrollHeight;\n }\n };\n } catch(e) { console.error(\"Smart List Init Error\", e); }\n })();\n <\/script>\n `;\n\n return `\n <style>${animationCss}</style>\n <div class=\"list-wrapper\">\n ${itemsHtml}\n </div>\n ${scrollScript}\n ${injectionScript}\n `;\n }\n\n // Single Item\n const contentHtml = renderItem(data);\n return `<div style=\"position: relative; width: 100%; height: 100%; overflow: hidden;\">${contentHtml}</div>`;\n}\n";
18549
+ }), generateHTML = (n, _, E = {}) => Function("elements", "data", "options", getRendererCode() + "\nreturn renderTemplate(elements, data, options);")(n, _, E), getRendererCode = () => "\n/**\n * Render Template\n * @param {Array} elements - The JSON configuration of elements\n * @param {Object|Array} data - The data object to inject (Object for single, Array for list)\n * @param {Object} options - { isList: boolean, listSettings: { sortProp: string, sortOrder: 'asc'|'desc', newestPosition: 'top'|'bottom', scrollDirection: 'up'|'down', containerHeight: number }, canvasHeight: number }\n * @returns {string} - The generated HTML string\n */\nfunction renderTemplate(elements, data, options = {}) {\n const { isList, listSettings, canvasHeight } = options;\n\n const measureTextHeight = (text, width, fontFamily, fontSize, lineHeightMultiplier = 1.2) => {\n if (!text) return 0;\n try {\n const canvas = document.createElement('canvas');\n const context = canvas.getContext('2d');\n if (!context) return 0;\n context.font = `${fontSize}px ${fontFamily}`;\n const words = String(text).split(' ');\n let line = '';\n let lineCount = 1;\n for (let i = 0; i < words.length; i++) {\n const testLine = line + words[i] + ' ';\n const metrics = context.measureText(testLine);\n const testWidth = metrics.width;\n if (testWidth > width && i > 0) {\n line = words[i] + ' ';\n lineCount++;\n } else {\n line = testLine;\n }\n }\n const explicitLines = String(text).split('\\n').length - 1;\n lineCount += explicitLines;\n return Math.ceil(lineCount * fontSize * lineHeightMultiplier);\n } catch (_) {\n return 0;\n }\n };\n\n const computeLayout = (elements, itemData) => {\n const layoutElements = JSON.parse(JSON.stringify(elements));\n \n const isInside = (inner, outer) => {\n const eps = 0.1;\n return (\n inner.x >= outer.x - eps &&\n inner.x + inner.width <= outer.x + outer.width + eps &&\n inner.y >= outer.y - eps &&\n inner.y + inner.height <= outer.y + outer.height + eps\n );\n };\n\n const autoGrowElements = layoutElements\n .filter(el => (el.type === 'text' || el.type === 'text-container') && el.autoGrow)\n .sort((a, b) => a.y - b.y);\n\n autoGrowElements.forEach(textEl => {\n let content = textEl.content;\n content = content.replace(/\\{\\{(.*?)\\}\\}/g, (match, key) => {\n const val = itemData[key.trim()];\n return val !== undefined && val !== null ? String(val) : match;\n });\n \n const fontSize = parseInt(String((textEl.style && textEl.style.fontSize) || 16));\n const fontFamily = String((textEl.style && textEl.style.fontFamily) || 'Arial');\n \n const isHorizontal = textEl.type === 'text-container' && textEl.containerExpansion === 'horizontal';\n \n if (isHorizontal) {\n // Horizontal expansion: Update width only\n // Requires canvas context which is available in measureTextHeight scope or we create new one\n // For simplicity, we can't easily access the measure logic here if it's not exposed, \n // but measureTextHeight is available in this scope.\n // However measureTextHeight calculates HEIGHT. We need WIDTH.\n \n try {\n const canvas = document.createElement('canvas');\n const context = canvas.getContext('2d');\n if (context) {\n context.font = `${fontSize}px ${fontFamily}`;\n const metrics = context.measureText(content);\n const padding = parseInt(String((textEl.style && textEl.style.padding) || 0)) * 2;\n const newWidth = Math.ceil(metrics.width + padding);\n if (newWidth > textEl.width) {\n textEl.width = newWidth;\n }\n }\n } catch(e) {}\n } else {\n // Vertical Expansion\n const measuredHeight = measureTextHeight(content, textEl.width, fontFamily, fontSize);\n const designHeight = textEl.height;\n const delta = measuredHeight - designHeight;\n \n if (delta > 0) {\n const originalBottom = textEl.y + designHeight;\n const originalTextRect = {\n x: textEl.x,\n y: textEl.y,\n width: textEl.width,\n height: designHeight\n };\n \n textEl.height = measuredHeight;\n \n layoutElements.forEach(other => {\n if (other.id === textEl.id) return;\n \n if (isInside(originalTextRect, other)) {\n other.height += delta;\n }\n \n if (other.y >= originalBottom) {\n other.y += delta;\n }\n });\n }\n }\n });\n \n let maxY = 0;\n layoutElements.forEach(el => {\n const bottom = el.y + el.height;\n if (bottom > maxY) maxY = bottom;\n });\n \n return { layoutElements, maxY };\n };\n\n const computeItemHeight = (elements, itemData, fallbackHeight) => {\n const { maxY } = computeLayout(elements, itemData);\n return fallbackHeight ? Math.max(maxY, fallbackHeight) : maxY;\n };\n\n const formatValue = (value, formatting) => {\n if (!formatting || formatting.type === 'text') return value !== undefined && value !== null ? String(value) : '';\n if (value === undefined || value === null) return '';\n\n if (formatting.type === 'boolean') {\n const isTrue = String(value) === 'true' || value === true || (typeof value === 'number' && value > 0);\n return isTrue ? (formatting.trueLabel || 'Sim') : (formatting.falseLabel || 'Não');\n }\n\n if (formatting.type === 'date') {\n try {\n const date = new Date(value);\n if (isNaN(date.getTime())) return String(value);\n \n if (formatting.dateFormat) {\n const d = date.getDate().toString().padStart(2, '0');\n const m = (date.getMonth() + 1).toString().padStart(2, '0');\n const y = date.getFullYear();\n const H = date.getHours().toString().padStart(2, '0');\n const M = date.getMinutes().toString().padStart(2, '0');\n const S = date.getSeconds().toString().padStart(2, '0');\n \n return formatting.dateFormat\n .replace('DD', d)\n .replace('MM', m)\n .replace('YYYY', String(y))\n .replace('HH', H)\n .replace('mm', M)\n .replace('ss', S);\n }\n return date.toLocaleDateString();\n } catch (e) { return String(value); }\n }\n\n if (formatting.type === 'number') {\n const num = parseFloat(value);\n if (isNaN(num)) return String(value);\n \n if (formatting.numberFormat === 'currency') {\n return (formatting.currencySymbol || 'R$') + ' ' + num.toFixed(formatting.decimalPlaces || 2);\n }\n if (formatting.numberFormat === 'percent') {\n return num.toFixed(formatting.decimalPlaces || 0) + '%';\n }\n if (formatting.decimalPlaces !== undefined) {\n return num.toFixed(formatting.decimalPlaces);\n }\n return num.toFixed(formatting.decimalPlaces || 0);\n }\n \n return String(value);\n };\n\n const checkCondition = (propValue, operator, ruleValue) => {\n const val = String(propValue).toLowerCase();\n const target = String(ruleValue).toLowerCase();\n \n switch (operator) {\n case 'equals': return val === target;\n case 'notEquals': return val !== target;\n case 'contains': return val.includes(target);\n case 'greaterThan': return parseFloat(val) > parseFloat(target);\n case 'lessThan': return parseFloat(val) < parseFloat(target);\n case 'truthy': return !!propValue;\n case 'falsy': return !propValue;\n default: return false;\n }\n };\n\n const camelToKebab = (string) => {\n return string.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase();\n };\n\n const hex8ToRgba = (hex) => {\n const m = /^#([0-9a-fA-F]{8})$/.exec(hex);\n if (!m) return hex;\n const h = m[1];\n const r = parseInt(h.slice(0, 2), 16);\n const g = parseInt(h.slice(2, 4), 16);\n const b = parseInt(h.slice(4, 6), 16);\n const a = parseInt(h.slice(6, 8), 16) / 255;\n return `rgba(${r}, ${g}, ${b}, ${a})`;\n };\n\n const styleObjectToString = (style) => {\n if (!style) return '';\n const pxProps = [\n 'width', 'height', 'top', 'left', 'right', 'bottom', \n 'fontSize', 'borderRadius', 'padding', 'margin', 'borderWidth',\n 'borderTopLeftRadius', 'borderTopRightRadius', 'borderBottomRightRadius', 'borderBottomLeftRadius'\n ];\n \n return Object.entries(style)\n .map(([key, value]) => {\n if (value === undefined || value === null) return '';\n const cssKey = camelToKebab(key);\n let cssValue = (typeof value === 'number' && pxProps.includes(key)) ? value + 'px' : value;\n if (typeof cssValue === 'string') {\n if (/^#([0-9a-fA-F]{8})$/.test(cssValue)) {\n cssValue = hex8ToRgba(cssValue);\n }\n }\n return `${cssKey}: ${cssValue}`;\n })\n .filter(Boolean)\n .join('; ');\n };\n\n const getAnimationStyles = (anim) => {\n if (!anim || anim.type === 'none') return {};\n return {\n animationName: anim.type,\n animationDuration: (anim.duration || 1) + 's',\n animationDelay: (anim.delay || 0) + 's',\n animationIterationCount: anim.iterationCount || 1,\n animationTimingFunction: anim.timingFunction || 'ease',\n animationFillMode: 'both'\n };\n };\n\n const keyframesCss = `\n @keyframes slideIn {\n from { opacity: 0; transform: translateY(20px); }\n to { opacity: 1; transform: translateY(0); }\n }\n @keyframes fadeIn { \n from { opacity: 0; } \n to { opacity: 1; } \n }\n @keyframes slideInLeft { \n from { opacity: 0; transform: translateX(-50px); } \n to { opacity: 1; transform: translateX(0); } \n }\n @keyframes slideInRight { \n from { opacity: 0; transform: translateX(50px); } \n to { opacity: 1; transform: translateX(0); } \n }\n @keyframes slideInUp { \n from { opacity: 0; transform: translateY(50px); } \n to { opacity: 1; transform: translateY(0); } \n }\n @keyframes slideInDown { \n from { opacity: 0; transform: translateY(-50px); } \n to { opacity: 1; transform: translateY(0); } \n }\n @keyframes zoomIn { \n from { opacity: 0; transform: scale(0.5); } \n to { opacity: 1; transform: scale(1); } \n }\n @keyframes bounceIn {\n 0% { opacity: 0; transform: scale(0.3); }\n 50% { opacity: 1; transform: scale(1.05); }\n 70% { transform: scale(0.9); }\n 100% { transform: scale(1); }\n }\n @keyframes pulse {\n 0% { transform: scale(1); }\n 50% { transform: scale(1.05); }\n 100% { transform: scale(1); }\n }\n @keyframes shake {\n 0%, 100% { transform: translateX(0); }\n 10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }\n 20%, 40%, 60%, 80% { transform: translateX(5px); }\n }\n @keyframes spin { \n from { transform: rotate(0deg); } \n to { transform: rotate(360deg); } \n }\n \n /* Improved / Smoother Animations */\n @keyframes smoothSlideUp {\n 0% { opacity: 0; transform: translateY(30px); }\n 100% { opacity: 1; transform: translateY(0); }\n }\n @keyframes popIn {\n 0% { opacity: 0; transform: scale(0.8) translateY(10px); }\n 100% { opacity: 1; transform: scale(1) translateY(0); }\n }\n @keyframes blurIn {\n 0% { opacity: 0; filter: blur(10px); }\n 100% { opacity: 1; filter: blur(0); }\n }\n `;\n\n const renderItem = (itemData, index = 0, offsetY = 0) => {\n const { layoutElements } = computeLayout(elements, itemData);\n \n // Group elements into logical rows based on Y position overlap to support dynamic height\n layoutElements.sort((a, b) => a.y - b.y);\n \n const rows = [];\n let currentRow = null;\n \n layoutElements.forEach(el => {\n const elBottom = el.y + el.height;\n if (!currentRow) {\n currentRow = { startY: el.y, endY: elBottom, elements: [el] };\n rows.push(currentRow);\n } else {\n // If this element starts significantly after the current row ends, start a new row\n if (el.y >= currentRow.endY - 5) { \n currentRow = { startY: el.y, endY: elBottom, elements: [el] };\n rows.push(currentRow);\n } else {\n currentRow.elements.push(el);\n currentRow.endY = Math.max(currentRow.endY, elBottom);\n }\n }\n });\n\n return rows.map(row => {\n // Find autoGrow elements in this row\n const flowElements = row.elements.filter(el => (el.type === 'text' || el.type === 'text-container') && el.autoGrow);\n \n // Sort flow elements by X position to render them in correct order\n // This is CRITICAL for correct flow layout\n flowElements.sort((a, b) => a.x - b.x);\n\n // Calculate Row Min Height (based on static elements and initial size of flow elements)\n let minHeight = 0;\n row.elements.forEach(el => {\n // For static elements, bottom relative to row start\n // For flow elements, we also use their initial design height as minimum\n const bottom = (el.y - row.startY) + el.height;\n if (bottom > minHeight) minHeight = bottom;\n });\n\n const rowStyle = styleObjectToString({\n position: 'relative',\n width: '100%',\n minHeight: minHeight,\n });\n\n const childrenHtml = row.elements.map(element => {\n let content = element.content;\n let imgSrc = '';\n\n // Resolve Content & Formatting\n if (element.type === 'text' || element.type === 'text-container') {\n content = content.replace(/\\{\\{(.*?)\\}\\}/g, (match, key) => {\n const val = itemData[key.trim()];\n if (val === undefined || val === null) return match;\n if (element.formatting) {\n return formatValue(val, element.formatting);\n }\n return String(val);\n });\n } else if (element.type === 'image') {\n if (element.dataBinding) {\n const val = itemData[element.dataBinding];\n if (val !== undefined && val !== null) {\n imgSrc = String(val);\n } else {\n imgSrc = content;\n }\n } else {\n imgSrc = content.replace(/\\{\\{(.*?)\\}\\}/g, (match, key) => {\n const val = itemData[key.trim()];\n return val !== undefined && val !== null ? String(val) : match;\n });\n }\n }\n\n // Resolve Conditional Styles\n let conditionalStyles = {};\n if (element.conditions) {\n element.conditions.forEach(rule => {\n const propVal = itemData[rule.property];\n if (checkCondition(propVal, rule.operator, rule.value)) {\n conditionalStyles = { ...conditionalStyles, ...rule.style };\n }\n });\n }\n\n // Resolve Style Bindings\n let bindingStyles = {};\n if (element.styleBindings) {\n Object.entries(element.styleBindings).forEach(([styleProp, variableName]) => {\n const val = itemData[variableName];\n if (val !== undefined && val !== null) {\n bindingStyles[styleProp] = String(val);\n }\n });\n }\n\n const isFlow = flowElements.includes(element);\n \n let marginLeft = undefined;\n if (isFlow) {\n // Calculate margin based on distance from previous flow element\n const flowIndex = flowElements.indexOf(element);\n if (flowIndex > 0) {\n const prev = flowElements[flowIndex - 1];\n const prevEnd = prev.x + prev.width;\n const gap = element.x - prevEnd;\n marginLeft = gap > 0 ? gap : 0;\n } else {\n marginLeft = element.x;\n }\n }\n\n const baseStyle = {\n position: isFlow ? 'relative' : 'absolute',\n left: isFlow ? undefined : element.x,\n top: isFlow ? undefined : (element.y - row.startY + offsetY),\n marginLeft: isFlow ? marginLeft : undefined,\n marginTop: isFlow ? (element.y - row.startY) : undefined,\n width: element.width,\n height: element.autoGrow ? 'auto' : element.height,\n transform: element.rotation ? `rotate(${element.rotation}deg)` : undefined,\n overflow: element.autoGrow ? 'visible' : 'hidden',\n whiteSpace: (element.type === 'text-container' && element.autoGrow && element.containerExpansion === 'horizontal') ? 'nowrap' : (element.autoGrow ? 'pre-wrap' : undefined),\n wordBreak: element.autoGrow ? 'break-word' : undefined,\n display: isFlow ? 'inline-block' : undefined,\n verticalAlign: isFlow ? 'top' : undefined,\n boxSizing: 'border-box', // Ensure padding doesn't affect dimensions\n ...element.style,\n ...conditionalStyles,\n ...bindingStyles\n };\n \n const styleString = styleObjectToString(baseStyle);\n\n if (element.type === 'text' || element.type === 'text-container') {\n return `<div style=\"${styleString}\">${content}</div>`;\n } else if (element.type === 'image') {\n const imgStyle = styleObjectToString({\n width: '100%',\n height: '100%',\n objectFit: element.style?.objectFit || 'cover',\n display: 'block'\n });\n return `<div style=\"${styleString}\"><img src=\"${imgSrc}\" alt=\"Element\" style=\"${imgStyle}\" /></div>`;\n } else if (element.type === 'box') {\n return `<div style=\"${styleString}\"></div>`;\n } else if (element.type === 'checkbox') {\n let isChecked = false;\n if (element.dataBinding) {\n const val = itemData[element.dataBinding];\n isChecked = val === true || String(val) === 'true';\n }\n const checkboxStyle = styleObjectToString({\n ...baseStyle,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center'\n });\n return `<div style=\"${checkboxStyle}\"><input type=\"checkbox\" ${isChecked ? 'checked' : ''} disabled style=\"width:100%;height:100%;margin:0;\" /></div>`;\n }\n return '';\n }).join('\\n');\n \n return `<div class=\"list-row\" style=\"${rowStyle}\">${childrenHtml}</div>`;\n }).join('\\n');\n };\n\nif (isList && Array.isArray(data)) {\n // Calculate per-item height respecting autoGrow\n // Sort data\n let listData = [...data];\n if (listSettings && listSettings.sortProp) {\n const prop = listSettings.sortProp;\n const order = listSettings.sortOrder === 'asc' ? 1 : -1;\n listData.sort((a, b) => {\n const valA = a[prop];\n const valB = b[prop];\n if (valA < valB) return -1 * order;\n if (valA > valB) return 1 * order;\n return 0;\n });\n }\n\n // Handle newest position\n if (listSettings && listSettings.newestPosition === 'top') {\n listData.reverse();\n }\n\n // Generate HTML for all items\n const itemsHtml = listData.map((item, index) => {\n const itemHtml = renderItem(item, index, 0);\n const itemContainerStyle = styleObjectToString({\n position: 'relative',\n minHeight: canvasHeight + 'px',\n width: '100%'\n });\n\n return `<div class=\"list-item\" style=\"${itemContainerStyle}\">${itemHtml}</div>`;\n }).join('\\n');\n\n // Animation Styles based on settings\n const scrollDirection = (listSettings && listSettings.scrollDirection) || 'down';\n const containerHeight = (listSettings && listSettings.containerHeight) ? listSettings.containerHeight + 'px' : '100%';\n \n const justify = (listSettings && listSettings.newestPosition === 'top') ? 'flex-start' : 'flex-end';\n\n // Entry Animation from settings\n const entryAnim = listSettings && listSettings.entryAnimation ? listSettings.entryAnimation : { type: 'slideIn', duration: 0.3, timingFunction: 'ease-out' };\n const entryAnimName = entryAnim.type === 'none' ? 'none' : entryAnim.type;\n const entryAnimDuration = entryAnim.duration + 's';\n const entryAnimTiming = entryAnim.timingFunction || 'ease-out';\n\n const animationCss = `\n ${keyframesCss}\n\n .list-wrapper {\n display: flex;\n flex-direction: column;\n justify-content: ${justify};\n height: ${containerHeight};\n width: 100%;\n overflow-y: auto;\n overflow-x: hidden;\n box-sizing: border-box;\n padding: 10px;\n }\n .list-item {\n flex-shrink: 0;\n animation: ${entryAnimName} ${entryAnimDuration} ${entryAnimTiming};\n margin-bottom: 10px;\n width: 100%;\n position: relative;\n }\n `;\n \n const scrollScript = scrollDirection === 'up' \n ? `<script>\n document.addEventListener('DOMContentLoaded', () => {\n const wrapper = document.querySelector('.list-wrapper');\n if(wrapper) wrapper.scrollTop = wrapper.scrollHeight;\n });\n <\/script>`\n : '';\n\n // Inject Smart Script for Dynamic Updates\n const injectionScript = `\n <script>\n (function() {\n try {\n const elements = ${JSON.stringify(elements)};\n const formatValue = ${formatValue.toString()};\n const checkCondition = ${checkCondition.toString()};\n const camelToKebab = ${camelToKebab.toString()};\n const hex8ToRgba = ${hex8ToRgba.toString()};\n const styleObjectToString = ${styleObjectToString.toString()};\n const getAnimationStyles = ${getAnimationStyles.toString()};\n const renderItem = ${renderItem.toString()};\n\n const measureTextHeight = ${measureTextHeight.toString()};\n const computeLayout = ${computeLayout.toString()};\n const computeItemHeight = ${computeItemHeight.toString()};\n const itemHeightFallback = ${canvasHeight || 0};\n const newestPosition = \"${(listSettings && listSettings.newestPosition) || 'bottom'}\";\n const scrollDirection = \"${(listSettings && listSettings.scrollDirection) || 'down'}\";\n\n window.addItem = function(data) {\n const wrapper = document.querySelector('.list-wrapper');\n if (!wrapper) return;\n\n const itemHtml = renderItem(data, 0, 0);\n const itemContainerStyle = styleObjectToString({\n position: 'relative',\n minHeight: itemHeightFallback + 'px',\n width: '100%'\n });\n\n const div = document.createElement('div');\n div.className = 'list-item';\n div.setAttribute('style', itemContainerStyle);\n div.innerHTML = itemHtml;\n\n if (newestPosition === 'top') {\n wrapper.insertBefore(div, wrapper.firstChild);\n } else {\n wrapper.appendChild(div);\n }\n \n if (scrollDirection === 'up') {\n wrapper.scrollTop = wrapper.scrollHeight;\n }\n };\n } catch(e) { console.error(\"Smart List Init Error\", e); }\n })();\n <\/script>\n `;\n\n return `\n <style>${animationCss}</style>\n <div class=\"list-wrapper\">\n ${itemsHtml}\n </div>\n ${scrollScript}\n ${injectionScript}\n `;\n }\n\n // Single Item\n const contentHtml = renderItem(data);\n return `<div style=\"position: relative; width: 100%; height: 100%; overflow: hidden;\">${contentHtml}</div>`;\n}\n";
18475
18550
  export { GenericEditor as EditorContent, generateHTML };