@ai-growth/nextjs
Version:
Seamlessly integrate Sanity CMS with Next.js applications for automated blog routing and rendering
233 lines (232 loc) • 9.61 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ContentBody = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = __importDefault(require("react"));
const OptimizedImage_1 = require("./OptimizedImage");
const DefaultTemplate_module_css_1 = __importDefault(require("./DefaultTemplate.module.css"));
/**
* ContentBody component for rendering content body with different strategies
*/
const ContentBody = ({ content, contentType, customRenderer, className = '', }) => {
// Use custom renderer if provided
if (customRenderer) {
return ((0, jsx_runtime_1.jsx)("div", { className: `${DefaultTemplate_module_css_1.default.contentBody} ${className}`, children: customRenderer(content) }));
}
// Handle different content types
const renderContent = () => {
if (!content) {
return ((0, jsx_runtime_1.jsx)("div", { className: DefaultTemplate_module_css_1.default.errorContainer, children: (0, jsx_runtime_1.jsx)("p", { className: DefaultTemplate_module_css_1.default.errorMessage, children: "No content available to display." }) }));
}
// Handle string content
if (typeof content === 'string') {
return renderStringContent(content);
}
// Handle array content (Portable Text or array of blocks)
if (Array.isArray(content)) {
return renderArrayContent(content);
}
// Handle object content
if (typeof content === 'object') {
return renderObjectContent(content);
}
// Fallback for unknown content types
return ((0, jsx_runtime_1.jsx)("div", { className: DefaultTemplate_module_css_1.default.errorContainer, children: (0, jsx_runtime_1.jsxs)("p", { className: DefaultTemplate_module_css_1.default.errorMessage, children: ["Unsupported content format for type: ", contentType] }) }));
};
return ((0, jsx_runtime_1.jsx)("div", { className: `${DefaultTemplate_module_css_1.default.contentBody} ${className}`, children: renderContent() }));
};
exports.ContentBody = ContentBody;
/**
* Render string content with basic HTML support
*/
const renderStringContent = (content) => {
// Check if content looks like HTML
const htmlRegex = /<[^>]*>/;
if (htmlRegex.test(content)) {
// For HTML content, use dangerouslySetInnerHTML with caution
return ((0, jsx_runtime_1.jsx)("div", { dangerouslySetInnerHTML: { __html: sanitizeHtml(content) } }));
}
// For plain text, preserve line breaks
return ((0, jsx_runtime_1.jsx)("div", { children: content.split('\n').map((line, index) => ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [line, index < content.split('\n').length - 1 && (0, jsx_runtime_1.jsx)("br", {})] }, index))) }));
};
/**
* Render array content (Portable Text blocks)
*/
const renderArrayContent = (content) => {
return ((0, jsx_runtime_1.jsx)("div", { children: content.map((block, index) => renderBlock(block, index)) }));
};
/**
* Render object content
*/
const renderObjectContent = (content) => {
// Handle Portable Text single block
if (content._type && content._type === 'block') {
return renderBlock(content, 0);
}
// Handle objects with specific content fields
if (content.html) {
return ((0, jsx_runtime_1.jsx)("div", { dangerouslySetInnerHTML: { __html: sanitizeHtml(content.html) } }));
}
if (content.text) {
return renderStringContent(content.text);
}
if (content.markdown) {
// Basic markdown support could be added here
return renderStringContent(content.markdown);
}
// Fallback: try to extract any string values
const textContent = extractTextFromObject(content);
if (textContent) {
return renderStringContent(textContent);
}
return ((0, jsx_runtime_1.jsx)("div", { className: DefaultTemplate_module_css_1.default.errorContainer, children: (0, jsx_runtime_1.jsx)("p", { className: DefaultTemplate_module_css_1.default.errorMessage, children: "Unable to render content object" }) }));
};
/**
* Render a Portable Text block
*/
const renderBlock = (block, index) => {
if (!block || !block._type) {
return null;
}
const key = block._key || index;
switch (block._type) {
case 'block':
return renderTextBlock(block, key);
case 'image':
return renderImageBlock(block, key);
case 'code':
return renderCodeBlock(block, key);
default: {
// For unknown block types, try to extract text
const text = extractTextFromObject(block);
if (text) {
return (0, jsx_runtime_1.jsx)("p", { children: text }, key);
}
return null;
}
}
};
/**
* Render a text block with style support
*/
const renderTextBlock = (block, key) => {
const { style = 'normal', children = [] } = block;
// Handle different block styles
const content = children.map((child, childIndex) => renderInlineContent(child, `${key}-${childIndex}`));
switch (style) {
case 'h1':
return (0, jsx_runtime_1.jsx)("h1", { children: content }, key);
case 'h2':
return (0, jsx_runtime_1.jsx)("h2", { children: content }, key);
case 'h3':
return (0, jsx_runtime_1.jsx)("h3", { children: content }, key);
case 'h4':
return (0, jsx_runtime_1.jsx)("h4", { children: content }, key);
case 'h5':
return (0, jsx_runtime_1.jsx)("h5", { children: content }, key);
case 'h6':
return (0, jsx_runtime_1.jsx)("h6", { children: content }, key);
case 'blockquote':
return (0, jsx_runtime_1.jsx)("blockquote", { children: content }, key);
default:
return (0, jsx_runtime_1.jsx)("p", { children: content }, key);
}
};
/**
* Render inline content with mark support
*/
const renderInlineContent = (child, key) => {
if (!child || typeof child !== 'object') {
return String(child || '');
}
const { text = '', marks = [] } = child;
if (marks.length === 0) {
return text;
}
// Apply marks (bold, italic, etc.)
let element = text;
marks.forEach((mark) => {
switch (mark) {
case 'strong':
element = (0, jsx_runtime_1.jsx)("strong", { children: element });
break;
case 'em':
element = (0, jsx_runtime_1.jsx)("em", { children: element });
break;
case 'code':
element = (0, jsx_runtime_1.jsx)("code", { children: element });
break;
case 'underline':
element = (0, jsx_runtime_1.jsx)("u", { children: element });
break;
default:
// For unknown marks, just return the text
break;
}
});
return (0, jsx_runtime_1.jsx)(react_1.default.Fragment, { children: element }, key);
};
/**
* Render an image block
*/
const renderImageBlock = (block, key) => {
const { asset, alt, caption } = block;
if (!asset) {
return null;
}
// Handle both Sanity asset objects and direct URLs
const imageAsset = typeof asset === 'string' ? asset : asset;
return ((0, jsx_runtime_1.jsxs)("figure", { children: [(0, jsx_runtime_1.jsx)(OptimizedImage_1.CmsImage, { image: imageAsset, alt: alt || 'Content image', width: 800, height: 600, quality: 85, showFallback: true, sizes: "(max-width: 768px) 100vw, (max-width: 1024px) 80vw, 800px" }), caption && (0, jsx_runtime_1.jsx)("figcaption", { children: caption })] }, key));
};
/**
* Render a code block
*/
const renderCodeBlock = (block, key) => {
const { code, language } = block;
if (!code) {
return null;
}
return ((0, jsx_runtime_1.jsx)("pre", { children: (0, jsx_runtime_1.jsx)("code", { className: language ? `language-${language}` : undefined, children: code }) }, key));
};
/**
* Extract text content from an object
*/
const extractTextFromObject = (obj) => {
if (typeof obj === 'string') {
return obj;
}
if (typeof obj !== 'object' || obj === null) {
return '';
}
// Try common text fields
const textFields = ['text', 'content', 'body', 'description', 'title'];
for (const field of textFields) {
if (obj[field] && typeof obj[field] === 'string') {
return obj[field];
}
}
// If it's an array, try to extract text from items
if (Array.isArray(obj)) {
return obj.map(item => extractTextFromObject(item)).filter(Boolean).join(' ');
}
// Try to get text from children
if (obj.children && Array.isArray(obj.children)) {
return obj.children.map((child) => child.text || extractTextFromObject(child)).filter(Boolean).join('');
}
return '';
};
/**
* Basic HTML sanitization (remove dangerous tags/attributes)
*/
const sanitizeHtml = (html) => {
// This is a basic sanitization - in production, use a proper library like DOMPurify
return html
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
.replace(/<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe>/gi, '')
.replace(/on\w+="[^"]*"/gi, '') // Remove event handlers
.replace(/javascript:/gi, ''); // Remove javascript: URLs
};
exports.default = exports.ContentBody;