@gosalesdeal/blog 0.2.0 → 0.3.0
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.
- package/README.md +265 -34
- package/dist/client/blogClient.js +36 -7
- package/dist/client/blogClient.js.map +1 -1
- package/dist/components/BlogPost.js +7 -8
- package/dist/components/BlogPost.js.map +1 -1
- package/dist/components/landing/BlogLayoutComponents.d.ts +14 -0
- package/dist/components/landing/BlogLayoutComponents.js +198 -0
- package/dist/components/landing/BlogLayoutComponents.js.map +1 -0
- package/dist/components/landing/FlexibleLayoutComponent.d.ts +40 -0
- package/dist/components/landing/FlexibleLayoutComponent.js +352 -0
- package/dist/components/landing/FlexibleLayoutComponent.js.map +1 -0
- package/dist/components/landing/HeroFeatureBanner.d.ts +36 -0
- package/dist/components/landing/HeroFeatureBanner.js +88 -0
- package/dist/components/landing/HeroFeatureBanner.js.map +1 -0
- package/dist/components/landing/HeroFeatureBannerComponent.d.ts +4 -0
- package/dist/components/landing/HeroFeatureBannerComponent.js +100 -0
- package/dist/components/landing/HeroFeatureBannerComponent.js.map +1 -0
- package/dist/components/landing/HeroFeatureBannerVideo.d.ts +34 -0
- package/dist/components/landing/HeroFeatureBannerVideo.js +261 -0
- package/dist/components/landing/HeroFeatureBannerVideo.js.map +1 -0
- package/dist/components/landing/HeroFeatureBannerVideoComponent.d.ts +5 -0
- package/dist/components/landing/HeroFeatureBannerVideoComponent.js +69 -0
- package/dist/components/landing/HeroFeatureBannerVideoComponent.js.map +1 -0
- package/dist/components/landing/VarietyReviewslistComponent.d.ts +4 -0
- package/dist/components/landing/VarietyReviewslistComponent.js +172 -0
- package/dist/components/landing/VarietyReviewslistComponent.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +21 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/resolveMediaSrc.d.ts +2 -0
- package/dist/lib/resolveMediaSrc.js +21 -0
- package/dist/lib/resolveMediaSrc.js.map +1 -0
- package/dist/render/BlockRenderer.js +5 -0
- package/dist/render/BlockRenderer.js.map +1 -1
- package/dist/render/LandingPageRenderer.d.ts +5 -0
- package/dist/render/LandingPageRenderer.js +50 -0
- package/dist/render/LandingPageRenderer.js.map +1 -0
- package/dist/render/landingPageHtml.d.ts +3 -1
- package/dist/render/landingPageHtml.js +18 -2
- package/dist/render/landingPageHtml.js.map +1 -1
- package/dist/styles.css +13 -0
- package/dist/types/blog.d.ts +5 -2
- package/package.json +6 -2
|
@@ -178,9 +178,25 @@ function generateHtmlFromSchema(schema) {
|
|
|
178
178
|
css
|
|
179
179
|
};
|
|
180
180
|
}
|
|
181
|
+
function parseSchemaJson(value) {
|
|
182
|
+
if (!value) return null;
|
|
183
|
+
if (typeof value === "string") {
|
|
184
|
+
try {
|
|
185
|
+
return parseSchemaJson(JSON.parse(value));
|
|
186
|
+
} catch {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (typeof value !== "object") return null;
|
|
191
|
+
const schema = value;
|
|
192
|
+
return schema.components?.length ? schema : null;
|
|
193
|
+
}
|
|
181
194
|
function getLandingPageSchema(post) {
|
|
182
|
-
const
|
|
183
|
-
|
|
195
|
+
const fromVersion = parseSchemaJson(post?.landingPage?.publishedVersion?.schema_json);
|
|
196
|
+
if (fromVersion) return fromVersion;
|
|
197
|
+
const fromLandingPage = parseSchemaJson(post?.landingPage?.landing_page_schema);
|
|
198
|
+
if (fromLandingPage) return fromLandingPage;
|
|
199
|
+
return parseSchemaJson(post?.landing_page_schema);
|
|
184
200
|
}
|
|
185
201
|
export {
|
|
186
202
|
generateHtmlFromSchema,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"landingPageHtml.js","sources":["../../src/render/landingPageHtml.ts"],"sourcesContent":["/** Page-builder schema shape stored on blog posts via landingPage.publishedVersion.schema_json. */\nexport interface LandingPageSchema {\n components?: Array<{ id?: string; type?: string; props?: Record<string, unknown> }>;\n settings?: { customCss?: string; maxWidth?: string; backgroundColor?: string };\n}\n\nfunction escapeHtml(text: string): string {\n if (!text) return '';\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nfunction sanitizeSrc(src: string): string {\n if (!src) return '';\n if (\n src.startsWith('data:') ||\n src.startsWith('http://') ||\n src.startsWith('https://') ||\n src.startsWith('/')\n ) {\n return src.replace(/\"/g, '"');\n }\n return escapeHtml(src);\n}\n\nfunction extractYouTubeId(url: string): string {\n const match = url.match(\n /(?:youtube\\.com\\/(?:[^/]+\\/.+\\/|(?:v|e(?:mbed)?)\\/|.*[?&]v=)|youtu\\.be\\/)([^\"&?/\\s]{11})/,\n );\n return match ? match[1] : '';\n}\n\nfunction renderHeroComponent(props: Record<string, unknown>, id: string): string {\n const headline = String(props.headline ?? props.title ?? '');\n const subheadline = String(props.subheadline ?? props.subtitle ?? '');\n const topHeadline = String(props.topHeadline ?? '');\n const showTopHeadline = props.showTopHeadline !== false && Boolean(topHeadline);\n const backgroundImage = String(props.backgroundImage ?? '');\n const backgroundColor = String(props.backgroundColor ?? '#f3f4f6');\n const headlineColor = String(props.headlineColor ?? props.textColor ?? '#000000');\n const subheadlineColor = String(props.subheadlineColor ?? props.textColor ?? '#4b5563');\n const topHeadlineColor = String(props.topHeadlineColor ?? '#9ca3af');\n const paddingY = String(props.paddingY ?? '80px');\n const paddingX = String(props.paddingX ?? '20px');\n const textAlign = props.isCentered === false ? String(props.alignment ?? 'left') : 'center';\n const headlineFontSize = String(props.headlineFontSize ?? '3rem');\n const subheadlineFontSize = String(props.subheadlineFontSize ?? '1.25rem');\n const topHeadlineFontSize = String(props.topHeadlineFontSize ?? '14px');\n const ctaText = String(props.ctaText ?? '');\n const ctaLink = String(props.ctaLink ?? props.ctaUrl ?? '');\n const ctaBgColor = String(props.ctaBgColor ?? '#007bff');\n const ctaTextColor = String(props.ctaTextColor ?? '#ffffff');\n const buttonBorderRadius = String(props.buttonBorderRadius ?? '6px');\n const buttonPaddingX = String(props.buttonPaddingX ?? '36px');\n const buttonPaddingY = String(props.buttonPaddingY ?? '14px');\n const containerMaxWidth = String(props.containerMaxWidth ?? '1200px');\n const overlayOpacity = Number(props.overlayOpacity ?? 0);\n\n const backgroundStyle = backgroundImage\n ? `background-image: url('${escapeHtml(backgroundImage)}'); background-size: cover; background-position: center;`\n : `background-color: ${backgroundColor};`;\n\n return `\n <section class=\"ggblog-lp-hero\" data-component-id=\"${escapeHtml(id)}\" style=\"${backgroundStyle} position: relative; padding: ${paddingY} ${paddingX};\">\n ${overlayOpacity > 0 ? `<div style=\"position:absolute;inset:0;background:rgba(0,0,0,${overlayOpacity});\"></div>` : ''}\n <div style=\"position:relative;z-index:1;max-width:${containerMaxWidth};margin:0 auto;text-align:${textAlign};\">\n ${showTopHeadline ? `<p style=\"margin:0 0 1rem;font-size:${topHeadlineFontSize};color:${topHeadlineColor};letter-spacing:0.15em;text-transform:uppercase;\">${escapeHtml(topHeadline)}</p>` : ''}\n ${headline ? `<h2 style=\"margin:0 0 1rem;font-size:${headlineFontSize};line-height:1.1;color:${headlineColor};font-weight:700;\">${escapeHtml(headline)}</h2>` : ''}\n ${subheadline ? `<p style=\"margin:0 0 2rem;font-size:${subheadlineFontSize};line-height:1.6;color:${subheadlineColor};max-width:650px;${textAlign === 'center' ? 'margin-left:auto;margin-right:auto;' : ''}\">${escapeHtml(subheadline)}</p>` : ''}\n ${ctaText && ctaLink ? `<a href=\"${escapeHtml(ctaLink)}\" style=\"display:inline-block;padding:${buttonPaddingY} ${buttonPaddingX};border-radius:${buttonBorderRadius};text-decoration:none;font-weight:700;background:${ctaBgColor};color:${ctaTextColor};\">${escapeHtml(ctaText)}</a>` : ''}\n </div>\n </section>\n `;\n}\n\nfunction renderTextComponent(props: Record<string, unknown>, id: string): string {\n const content = String(props.content ?? '');\n const alignment = String(props.alignment ?? 'left');\n const fontSize = String(props.fontSize ?? '16px');\n return `<div class=\"ggblog-lp-text\" data-component-id=\"${escapeHtml(id)}\" style=\"text-align:${alignment};font-size:${fontSize};\">${content}</div>`;\n}\n\nfunction renderImageComponent(props: Record<string, unknown>, id: string): string {\n const src = String(props.src ?? props.url ?? '');\n const alt = String(props.alt ?? '');\n const alignment = String(props.alignment ?? 'center');\n return `\n <figure class=\"ggblog-lp-image\" data-component-id=\"${escapeHtml(id)}\" style=\"text-align:${alignment};margin:20px 0;\">\n <img src=\"${escapeHtml(src)}\" alt=\"${escapeHtml(alt)}\" style=\"max-width:100%;height:auto;\" loading=\"lazy\" />\n </figure>\n `;\n}\n\nfunction renderButtonComponent(props: Record<string, unknown>, id: string): string {\n const text = String(props.text ?? 'Click me');\n const url = String(props.url ?? props.href ?? '#');\n const alignment = String(props.alignment ?? 'center');\n return `\n <div class=\"ggblog-lp-button\" data-component-id=\"${escapeHtml(id)}\" style=\"text-align:${alignment};margin:20px 0;\">\n <a href=\"${escapeHtml(url)}\" style=\"display:inline-block;padding:12px 24px;border-radius:6px;text-decoration:none;font-weight:600;background:#007bff;color:#fff;\">${escapeHtml(text)}</a>\n </div>\n `;\n}\n\nfunction renderColumnsComponent(props: Record<string, unknown>, id: string): string {\n const columns = Array.isArray(props.columns) ? props.columns : [];\n const gap = String(props.gap ?? '20px');\n const columnHtml = columns\n .map((col) => {\n const components = Array.isArray((col as { components?: unknown[] }).components)\n ? (col as { components: Array<{ id?: string; type?: string; props?: Record<string, unknown> }> })\n .components\n : [];\n const content = components.map((c) => renderComponentToHtml(c)).join('');\n return `<div class=\"ggblog-lp-column\" style=\"flex:1;min-width:240px;\">${content}</div>`;\n })\n .join('');\n return `<div class=\"ggblog-lp-columns\" data-component-id=\"${escapeHtml(id)}\" style=\"display:flex;gap:${gap};flex-wrap:wrap;\">${columnHtml}</div>`;\n}\n\nfunction renderSpacerComponent(props: Record<string, unknown>, id: string): string {\n const height = String(props.height ?? '40px');\n return `<div class=\"ggblog-lp-spacer\" data-component-id=\"${escapeHtml(id)}\" style=\"height:${height};\"></div>`;\n}\n\nfunction renderDividerComponent(props: Record<string, unknown>, id: string): string {\n const color = String(props.color ?? '#e0e0e0');\n const thickness = String(props.thickness ?? '1px');\n const style = String(props.style ?? 'solid');\n const margin = String(props.margin ?? '20px 0');\n return `<hr class=\"ggblog-lp-divider\" data-component-id=\"${escapeHtml(id)}\" style=\"border:none;border-top:${thickness} ${style} ${color};margin:${margin};\" />`;\n}\n\nfunction renderVideoComponent(props: Record<string, unknown>, id: string): string {\n const url = String(props.url ?? '');\n const poster = String(props.poster ?? '');\n const controls = props.controls !== false;\n const autoplay = props.autoplay === true;\n const muted = props.muted === true;\n const loop = props.loop === true;\n\n if (url.includes('youtube.com') || url.includes('youtu.be')) {\n const videoId = extractYouTubeId(url);\n return `\n <div class=\"ggblog-lp-video\" data-component-id=\"${escapeHtml(id)}\" style=\"position:relative;padding-bottom:56.25%;height:0;overflow:hidden;\">\n <iframe src=\"https://www.youtube.com/embed/${videoId}${autoplay ? '?autoplay=1' : ''}\" style=\"position:absolute;top:0;left:0;width:100%;height:100%;\" frameborder=\"0\" allowfullscreen loading=\"lazy\"></iframe>\n </div>\n `;\n }\n\n return `\n <div class=\"ggblog-lp-video\" data-component-id=\"${escapeHtml(id)}\">\n <video src=\"${escapeHtml(url)}\" poster=\"${escapeHtml(poster)}\" ${controls ? 'controls' : ''} ${autoplay ? 'autoplay' : ''} ${muted ? 'muted' : ''} ${loop ? 'loop' : ''} playsinline style=\"max-width:100%;\"></video>\n </div>\n `;\n}\n\nfunction renderCtaComponent(props: Record<string, unknown>, id: string): string {\n return renderHeroComponent(\n {\n headline: props.title ?? props.headline,\n subheadline: props.description ?? props.subheadline,\n ctaText: props.buttonText ?? props.ctaText,\n ctaLink: props.buttonUrl ?? props.ctaLink,\n backgroundColor: props.backgroundColor,\n textColor: props.textColor,\n },\n id,\n );\n}\n\nfunction renderComponentToHtml(component: {\n id?: string;\n type?: string;\n props?: Record<string, unknown>;\n}): string {\n const { type, props = {}, id = '' } = component;\n if (!type) return '';\n\n if (type.startsWith('custom_navbar')) {\n const logo = String(props.logo ?? '');\n const companyName = String(props.companyName ?? '');\n return `<nav class=\"ggblog-lp-navbar\" data-component-id=\"${escapeHtml(id)}\" style=\"padding:12px 20px;background:${String(props.backgroundColor ?? '#fff')};\">${logo ? `<img src=\"${sanitizeSrc(logo)}\" alt=\"${escapeHtml(companyName)}\" style=\"height:48px;\" />` : escapeHtml(companyName)}</nav>`;\n }\n\n switch (type) {\n case 'hero':\n return renderHeroComponent(props, id);\n case 'text':\n return renderTextComponent(props, id);\n case 'image':\n return renderImageComponent(props, id);\n case 'button':\n return renderButtonComponent(props, id);\n case 'columns':\n return renderColumnsComponent(props, id);\n case 'spacer':\n return renderSpacerComponent(props, id);\n case 'divider':\n return renderDividerComponent(props, id);\n case 'video':\n return renderVideoComponent(props, id);\n case 'cta':\n return renderCtaComponent(props, id);\n case 'html':\n return String(props.content ?? '');\n default:\n return `<div class=\"ggblog-lp-component ggblog-lp-${escapeHtml(type)}\" data-component-id=\"${escapeHtml(id)}\">${String(props.content ?? props.headline ?? props.text ?? '')}</div>`;\n }\n}\n\n/** Convert a page-builder schema to trusted HTML (+ optional CSS) for blog post bodies. */\nexport function generateHtmlFromSchema(schema: LandingPageSchema | null | undefined): {\n html: string;\n css: string;\n} {\n if (!schema?.components?.length) {\n return { html: '', css: '' };\n }\n\n const html = schema.components.map((component) => renderComponentToHtml(component)).join('');\n const css = schema.settings?.customCss ?? '';\n\n return {\n html: `<div class=\"ggblog-landing-page-content\">${html}</div>`,\n css,\n };\n}\n\n/** Read landing page schema from a blog post API payload. */\nexport function getLandingPageSchema(\n post: { landingPage?: { publishedVersion?: { schema_json?: LandingPageSchema } } } | null | undefined,\n): LandingPageSchema | null {\n const schema = post?.landingPage?.publishedVersion?.schema_json;\n return schema?.components?.length ? schema : null;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"landingPageHtml.js","sources":["../../src/render/landingPageHtml.ts"],"sourcesContent":["/** Page-builder schema shape stored on blog posts via landingPage.publishedVersion.schema_json. */\n\nexport interface LandingPageSchema {\n components?: Array<{ id?: string; type?: string; props?: Record<string, unknown> }>;\n settings?: { customCss?: string; maxWidth?: string; backgroundColor?: string };\n}\n\nfunction escapeHtml(text: string): string {\n if (!text) return '';\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nfunction sanitizeSrc(src: string): string {\n if (!src) return '';\n if (\n src.startsWith('data:') ||\n src.startsWith('http://') ||\n src.startsWith('https://') ||\n src.startsWith('/')\n ) {\n return src.replace(/\"/g, '"');\n }\n return escapeHtml(src);\n}\n\nfunction extractYouTubeId(url: string): string {\n const match = url.match(\n /(?:youtube\\.com\\/(?:[^/]+\\/.+\\/|(?:v|e(?:mbed)?)\\/|.*[?&]v=)|youtu\\.be\\/)([^\"&?/\\s]{11})/,\n );\n return match ? match[1] : '';\n}\n\nfunction renderHeroComponent(props: Record<string, unknown>, id: string): string {\n const headline = String(props.headline ?? props.title ?? '');\n const subheadline = String(props.subheadline ?? props.subtitle ?? '');\n const topHeadline = String(props.topHeadline ?? '');\n const showTopHeadline = props.showTopHeadline !== false && Boolean(topHeadline);\n const backgroundImage = String(props.backgroundImage ?? '');\n const backgroundColor = String(props.backgroundColor ?? '#f3f4f6');\n const headlineColor = String(props.headlineColor ?? props.textColor ?? '#000000');\n const subheadlineColor = String(props.subheadlineColor ?? props.textColor ?? '#4b5563');\n const topHeadlineColor = String(props.topHeadlineColor ?? '#9ca3af');\n const paddingY = String(props.paddingY ?? '80px');\n const paddingX = String(props.paddingX ?? '20px');\n const textAlign = props.isCentered === false ? String(props.alignment ?? 'left') : 'center';\n const headlineFontSize = String(props.headlineFontSize ?? '3rem');\n const subheadlineFontSize = String(props.subheadlineFontSize ?? '1.25rem');\n const topHeadlineFontSize = String(props.topHeadlineFontSize ?? '14px');\n const ctaText = String(props.ctaText ?? '');\n const ctaLink = String(props.ctaLink ?? props.ctaUrl ?? '');\n const ctaBgColor = String(props.ctaBgColor ?? '#007bff');\n const ctaTextColor = String(props.ctaTextColor ?? '#ffffff');\n const buttonBorderRadius = String(props.buttonBorderRadius ?? '6px');\n const buttonPaddingX = String(props.buttonPaddingX ?? '36px');\n const buttonPaddingY = String(props.buttonPaddingY ?? '14px');\n const containerMaxWidth = String(props.containerMaxWidth ?? '1200px');\n const overlayOpacity = Number(props.overlayOpacity ?? 0);\n\n const backgroundStyle = backgroundImage\n ? `background-image: url('${escapeHtml(backgroundImage)}'); background-size: cover; background-position: center;`\n : `background-color: ${backgroundColor};`;\n\n return `\n <section class=\"ggblog-lp-hero\" data-component-id=\"${escapeHtml(id)}\" style=\"${backgroundStyle} position: relative; padding: ${paddingY} ${paddingX};\">\n ${overlayOpacity > 0 ? `<div style=\"position:absolute;inset:0;background:rgba(0,0,0,${overlayOpacity});\"></div>` : ''}\n <div style=\"position:relative;z-index:1;max-width:${containerMaxWidth};margin:0 auto;text-align:${textAlign};\">\n ${showTopHeadline ? `<p style=\"margin:0 0 1rem;font-size:${topHeadlineFontSize};color:${topHeadlineColor};letter-spacing:0.15em;text-transform:uppercase;\">${escapeHtml(topHeadline)}</p>` : ''}\n ${headline ? `<h2 style=\"margin:0 0 1rem;font-size:${headlineFontSize};line-height:1.1;color:${headlineColor};font-weight:700;\">${escapeHtml(headline)}</h2>` : ''}\n ${subheadline ? `<p style=\"margin:0 0 2rem;font-size:${subheadlineFontSize};line-height:1.6;color:${subheadlineColor};max-width:650px;${textAlign === 'center' ? 'margin-left:auto;margin-right:auto;' : ''}\">${escapeHtml(subheadline)}</p>` : ''}\n ${ctaText && ctaLink ? `<a href=\"${escapeHtml(ctaLink)}\" style=\"display:inline-block;padding:${buttonPaddingY} ${buttonPaddingX};border-radius:${buttonBorderRadius};text-decoration:none;font-weight:700;background:${ctaBgColor};color:${ctaTextColor};\">${escapeHtml(ctaText)}</a>` : ''}\n </div>\n </section>\n `;\n}\n\nfunction renderTextComponent(props: Record<string, unknown>, id: string): string {\n const content = String(props.content ?? '');\n const alignment = String(props.alignment ?? 'left');\n const fontSize = String(props.fontSize ?? '16px');\n return `<div class=\"ggblog-lp-text\" data-component-id=\"${escapeHtml(id)}\" style=\"text-align:${alignment};font-size:${fontSize};\">${content}</div>`;\n}\n\nfunction renderImageComponent(props: Record<string, unknown>, id: string): string {\n const src = String(props.src ?? props.url ?? '');\n const alt = String(props.alt ?? '');\n const alignment = String(props.alignment ?? 'center');\n return `\n <figure class=\"ggblog-lp-image\" data-component-id=\"${escapeHtml(id)}\" style=\"text-align:${alignment};margin:20px 0;\">\n <img src=\"${escapeHtml(src)}\" alt=\"${escapeHtml(alt)}\" style=\"max-width:100%;height:auto;\" loading=\"lazy\" />\n </figure>\n `;\n}\n\nfunction renderButtonComponent(props: Record<string, unknown>, id: string): string {\n const text = String(props.text ?? 'Click me');\n const url = String(props.url ?? props.href ?? '#');\n const alignment = String(props.alignment ?? 'center');\n return `\n <div class=\"ggblog-lp-button\" data-component-id=\"${escapeHtml(id)}\" style=\"text-align:${alignment};margin:20px 0;\">\n <a href=\"${escapeHtml(url)}\" style=\"display:inline-block;padding:12px 24px;border-radius:6px;text-decoration:none;font-weight:600;background:#007bff;color:#fff;\">${escapeHtml(text)}</a>\n </div>\n `;\n}\n\nfunction renderColumnsComponent(props: Record<string, unknown>, id: string): string {\n const columns = Array.isArray(props.columns) ? props.columns : [];\n const gap = String(props.gap ?? '20px');\n const columnHtml = columns\n .map((col) => {\n const components = Array.isArray((col as { components?: unknown[] }).components)\n ? (col as { components: Array<{ id?: string; type?: string; props?: Record<string, unknown> }> })\n .components\n : [];\n const content = components.map((c) => renderComponentToHtml(c)).join('');\n return `<div class=\"ggblog-lp-column\" style=\"flex:1;min-width:240px;\">${content}</div>`;\n })\n .join('');\n return `<div class=\"ggblog-lp-columns\" data-component-id=\"${escapeHtml(id)}\" style=\"display:flex;gap:${gap};flex-wrap:wrap;\">${columnHtml}</div>`;\n}\n\nfunction renderSpacerComponent(props: Record<string, unknown>, id: string): string {\n const height = String(props.height ?? '40px');\n return `<div class=\"ggblog-lp-spacer\" data-component-id=\"${escapeHtml(id)}\" style=\"height:${height};\"></div>`;\n}\n\nfunction renderDividerComponent(props: Record<string, unknown>, id: string): string {\n const color = String(props.color ?? '#e0e0e0');\n const thickness = String(props.thickness ?? '1px');\n const style = String(props.style ?? 'solid');\n const margin = String(props.margin ?? '20px 0');\n return `<hr class=\"ggblog-lp-divider\" data-component-id=\"${escapeHtml(id)}\" style=\"border:none;border-top:${thickness} ${style} ${color};margin:${margin};\" />`;\n}\n\nfunction renderVideoComponent(props: Record<string, unknown>, id: string): string {\n const url = String(props.url ?? '');\n const poster = String(props.poster ?? '');\n const controls = props.controls !== false;\n const autoplay = props.autoplay === true;\n const muted = props.muted === true;\n const loop = props.loop === true;\n\n if (url.includes('youtube.com') || url.includes('youtu.be')) {\n const videoId = extractYouTubeId(url);\n return `\n <div class=\"ggblog-lp-video\" data-component-id=\"${escapeHtml(id)}\" style=\"position:relative;padding-bottom:56.25%;height:0;overflow:hidden;\">\n <iframe src=\"https://www.youtube.com/embed/${videoId}${autoplay ? '?autoplay=1' : ''}\" style=\"position:absolute;top:0;left:0;width:100%;height:100%;\" frameborder=\"0\" allowfullscreen loading=\"lazy\"></iframe>\n </div>\n `;\n }\n\n return `\n <div class=\"ggblog-lp-video\" data-component-id=\"${escapeHtml(id)}\">\n <video src=\"${escapeHtml(url)}\" poster=\"${escapeHtml(poster)}\" ${controls ? 'controls' : ''} ${autoplay ? 'autoplay' : ''} ${muted ? 'muted' : ''} ${loop ? 'loop' : ''} playsinline style=\"max-width:100%;\"></video>\n </div>\n `;\n}\n\nfunction renderCtaComponent(props: Record<string, unknown>, id: string): string {\n return renderHeroComponent(\n {\n headline: props.title ?? props.headline,\n subheadline: props.description ?? props.subheadline,\n ctaText: props.buttonText ?? props.ctaText,\n ctaLink: props.buttonUrl ?? props.ctaLink,\n backgroundColor: props.backgroundColor,\n textColor: props.textColor,\n },\n id,\n );\n}\n\nfunction renderComponentToHtml(component: {\n id?: string;\n type?: string;\n props?: Record<string, unknown>;\n}): string {\n const { type, props = {}, id = '' } = component;\n if (!type) return '';\n\n if (type.startsWith('custom_navbar')) {\n const logo = String(props.logo ?? '');\n const companyName = String(props.companyName ?? '');\n return `<nav class=\"ggblog-lp-navbar\" data-component-id=\"${escapeHtml(id)}\" style=\"padding:12px 20px;background:${String(props.backgroundColor ?? '#fff')};\">${logo ? `<img src=\"${sanitizeSrc(logo)}\" alt=\"${escapeHtml(companyName)}\" style=\"height:48px;\" />` : escapeHtml(companyName)}</nav>`;\n }\n\n switch (type) {\n case 'hero':\n return renderHeroComponent(props, id);\n case 'text':\n return renderTextComponent(props, id);\n case 'image':\n return renderImageComponent(props, id);\n case 'button':\n return renderButtonComponent(props, id);\n case 'columns':\n return renderColumnsComponent(props, id);\n case 'spacer':\n return renderSpacerComponent(props, id);\n case 'divider':\n return renderDividerComponent(props, id);\n case 'video':\n return renderVideoComponent(props, id);\n case 'cta':\n return renderCtaComponent(props, id);\n case 'html':\n return String(props.content ?? '');\n default:\n return `<div class=\"ggblog-lp-component ggblog-lp-${escapeHtml(type)}\" data-component-id=\"${escapeHtml(id)}\">${String(props.content ?? props.headline ?? props.text ?? '')}</div>`;\n }\n}\n\n/** Convert a page-builder schema to trusted HTML (+ optional CSS) for blog post bodies. */\nexport function generateHtmlFromSchema(schema: LandingPageSchema | null | undefined): {\n html: string;\n css: string;\n} {\n if (!schema?.components?.length) {\n return { html: '', css: '' };\n }\n\n const html = schema.components.map((component) => renderComponentToHtml(component)).join('');\n const css = schema.settings?.customCss ?? '';\n\n return {\n html: `<div class=\"ggblog-landing-page-content\">${html}</div>`,\n css,\n };\n}\n\nfunction parseSchemaJson(value: unknown): LandingPageSchema | null {\n if (!value) return null;\n if (typeof value === 'string') {\n try {\n return parseSchemaJson(JSON.parse(value) as unknown);\n } catch {\n return null;\n }\n }\n if (typeof value !== 'object') return null;\n const schema = value as LandingPageSchema;\n return schema.components?.length ? schema : null;\n}\n\n/** Read landing page schema from a blog post API payload. */\nexport function getLandingPageSchema(\n post:\n | {\n landingPage?: {\n publishedVersion?: { schema_json?: LandingPageSchema | string };\n landing_page_schema?: LandingPageSchema | string;\n };\n landing_page_schema?: LandingPageSchema | string;\n }\n | null\n | undefined,\n): LandingPageSchema | null {\n const fromVersion = parseSchemaJson(post?.landingPage?.publishedVersion?.schema_json);\n if (fromVersion) return fromVersion;\n\n const fromLandingPage = parseSchemaJson(post?.landingPage?.landing_page_schema);\n if (fromLandingPage) return fromLandingPage;\n\n return parseSchemaJson(post?.landing_page_schema);\n}\n"],"names":[],"mappings":"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"}
|
package/dist/styles.css
CHANGED
|
@@ -22,6 +22,17 @@
|
|
|
22
22
|
margin: 0 auto;
|
|
23
23
|
padding: 32px 20px 64px;
|
|
24
24
|
}
|
|
25
|
+
|
|
26
|
+
/* Landing-page posts: allow full-bleed sections inside the article */
|
|
27
|
+
.ggblog-post:has(.ggblog-landing-page) {
|
|
28
|
+
max-width: none;
|
|
29
|
+
padding-left: 0;
|
|
30
|
+
padding-right: 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.ggblog-post:has(.ggblog-landing-page) .ggblog-post-body {
|
|
34
|
+
max-width: none;
|
|
35
|
+
}
|
|
25
36
|
.ggblog-post-title {
|
|
26
37
|
font-size: clamp(28px, 5vw, 44px);
|
|
27
38
|
font-weight: 800;
|
|
@@ -101,6 +112,8 @@
|
|
|
101
112
|
.ggblog-button-medium { padding: 10px 22px; font-size: 15px; }
|
|
102
113
|
.ggblog-button-large { padding: 13px 30px; font-size: 16px; }
|
|
103
114
|
|
|
115
|
+
.ggblog-html { overflow-wrap: anywhere; }
|
|
116
|
+
.ggblog-html img, .ggblog-html video, .ggblog-html iframe { max-width: 100%; height: auto; }
|
|
104
117
|
.ggblog-embed-frame { width: 100%; border: 0; border-radius: var(--ggblog-radius); }
|
|
105
118
|
.ggblog-gallery-item { margin: 0; }
|
|
106
119
|
.ggblog-gallery-item img { width: 100%; height: auto; border-radius: 8px; display: block; }
|
package/dist/types/blog.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type BlockType = 'text' | 'heading' | 'image' | 'video' | 'quote' | 'code' | 'list' | 'divider' | 'spacer' | 'button' | 'embed' | 'gallery' | 'table' | 'accordion' | 'tabs' | 'alert' | 'columns';
|
|
1
|
+
export type BlockType = 'text' | 'heading' | 'image' | 'video' | 'quote' | 'code' | 'list' | 'divider' | 'spacer' | 'button' | 'embed' | 'html' | 'gallery' | 'table' | 'accordion' | 'tabs' | 'alert' | 'columns';
|
|
2
2
|
export interface BlockSettings {
|
|
3
3
|
alignment?: 'left' | 'center' | 'right' | 'justify';
|
|
4
4
|
margin?: {
|
|
@@ -97,6 +97,9 @@ export interface ButtonBlockContent {
|
|
|
97
97
|
icon?: string;
|
|
98
98
|
openInNewTab?: boolean;
|
|
99
99
|
}
|
|
100
|
+
export interface HtmlBlockContent {
|
|
101
|
+
html: string;
|
|
102
|
+
}
|
|
100
103
|
export interface EmbedBlockContent {
|
|
101
104
|
html: string;
|
|
102
105
|
url?: string;
|
|
@@ -168,7 +171,7 @@ export interface DividerBlockContent {
|
|
|
168
171
|
thickness?: number;
|
|
169
172
|
width?: number;
|
|
170
173
|
}
|
|
171
|
-
export type BlockContent = TextBlockContent | HeadingBlockContent | ImageBlockContent | VideoBlockContent | QuoteBlockContent | CodeBlockContent | ListBlockContent | ButtonBlockContent | EmbedBlockContent | GalleryBlockContent | TableBlockContent | AccordionBlockContent | TabsBlockContent | AlertBlockContent | ColumnsBlockContent | SpacerBlockContent | DividerBlockContent;
|
|
174
|
+
export type BlockContent = TextBlockContent | HeadingBlockContent | ImageBlockContent | VideoBlockContent | QuoteBlockContent | CodeBlockContent | ListBlockContent | ButtonBlockContent | EmbedBlockContent | HtmlBlockContent | GalleryBlockContent | TableBlockContent | AccordionBlockContent | TabsBlockContent | AlertBlockContent | ColumnsBlockContent | SpacerBlockContent | DividerBlockContent;
|
|
172
175
|
export interface ContentBlock extends BaseBlock {
|
|
173
176
|
content: BlockContent;
|
|
174
177
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gosalesdeal/blog",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Render GoEnterprise blog content (list + post) straight from the BlogPosts API. Server-component friendly
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Render GoEnterprise blog content (list + post) straight from the BlogPosts API. Server-component friendly.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"sideEffects": [
|
|
@@ -44,6 +44,8 @@
|
|
|
44
44
|
"goenterprise"
|
|
45
45
|
],
|
|
46
46
|
"peerDependencies": {
|
|
47
|
+
"next": ">=14",
|
|
48
|
+
"primereact": ">=10",
|
|
47
49
|
"react": ">=18",
|
|
48
50
|
"react-dom": ">=18"
|
|
49
51
|
},
|
|
@@ -57,6 +59,8 @@
|
|
|
57
59
|
"@types/react": "^19.0.7",
|
|
58
60
|
"@types/react-dom": "^19.0.3",
|
|
59
61
|
"@vitejs/plugin-react": "^4.3.4",
|
|
62
|
+
"next": "^16.1.3",
|
|
63
|
+
"primereact": "^10.9.7",
|
|
60
64
|
"react": "^19.0.0",
|
|
61
65
|
"react-dom": "^19.0.0",
|
|
62
66
|
"rollup-preserve-directives": "^1.1.3",
|