UNPKG

@ai-growth/nextjs

Version:

Seamlessly integrate Sanity CMS with Next.js applications for automated blog routing and rendering

189 lines (188 loc) 9.34 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.LazyImage = exports.IntersectionWrapper = exports.RouteBasedLazy = exports.LazyErrorBoundary = exports.SuspenseWrapper = exports.LazyLoadComponent = void 0; exports.withLazyLoading = withLazyLoading; exports.createLazyComponentWithSuspense = createLazyComponentWithSuspense; const jsx_runtime_1 = require("react/jsx-runtime"); /** * @fileoverview Lazy Loading React Components * * This module provides React components for implementing lazy loading patterns * with JSX support, including LazyLoadComponent and error boundaries. */ const react_1 = __importStar(require("react")); const lazy_loading_1 = require("../utils/lazy-loading"); const ErrorBoundary_1 = require("./ErrorBoundary"); /** * Lazy loading component with intersection observer */ const LazyLoadComponent = ({ component: Component, props = {}, fallback = null, errorFallback = null, className, style, intersectionOptions, immediate = false, }) => { const [elementRef, isIntersecting] = (0, lazy_loading_1.useIntersectionObserver)(intersectionOptions); const shouldLoad = immediate || isIntersecting; const { component, loading, error } = (0, lazy_loading_1.useDynamicImport)(() => Promise.resolve({ default: Component }), shouldLoad); if (error && errorFallback) { return (0, jsx_runtime_1.jsx)("div", { ref: elementRef, className: className, style: style, children: errorFallback }); } if (loading || !component) { return (0, jsx_runtime_1.jsx)("div", { ref: elementRef, className: className, style: style, children: fallback }); } const LoadedComponent = component; return ((0, jsx_runtime_1.jsx)("div", { ref: elementRef, className: className, style: style, children: (0, jsx_runtime_1.jsx)(LoadedComponent, { ...props }) })); }; exports.LazyLoadComponent = LazyLoadComponent; /** * Enhanced Suspense wrapper with error boundary */ const SuspenseWrapper = ({ children, fallback = null, errorFallback, onError = () => { }, }) => { return ((0, jsx_runtime_1.jsx)(ErrorBoundary_1.ErrorBoundary, { fallback: errorFallback ? react_1.default.createElement(errorFallback, { error: new Error('Component error'), retry: () => window.location.reload() }) : null, onError: onError, children: (0, jsx_runtime_1.jsx)(react_1.Suspense, { fallback: fallback, children: children }) })); }; exports.SuspenseWrapper = SuspenseWrapper; /** * Error boundary specifically for lazy-loaded components */ class LazyErrorBoundary extends react_1.Component { constructor(props) { super(props); this.handleRetry = () => { this.setState({ hasError: false, error: null }); }; this.state = { hasError: false, error: null }; } static getDerivedStateFromError(error) { return { hasError: true, error }; } componentDidCatch(error, errorInfo) { console.error('Lazy component loading error:', error, errorInfo); this.props.onError?.(error); } render() { if (this.state.hasError && this.state.error) { const { ErrorFallback } = this.props; return (0, jsx_runtime_1.jsx)(ErrorFallback, { error: this.state.error, retry: this.handleRetry }); } return this.props.children; } } exports.LazyErrorBoundary = LazyErrorBoundary; /** * Create a lazy-loaded higher-order component */ function withLazyLoading(loader, options = {}) { const { fallback = null, errorFallback = null, onError = () => { }, preloadOnHover = false, hoverDelay = 300, } = options; const LazyComponent = react_1.default.lazy(loader); return react_1.default.forwardRef((props, ref) => { const [isHovered, setIsHovered] = (0, react_1.useState)(false); const [shouldPreload, setShouldPreload] = (0, react_1.useState)(false); // Handle hover preloading (0, react_1.useEffect)(() => { if (preloadOnHover && isHovered && !shouldPreload) { const timer = setTimeout(() => { setShouldPreload(true); }, hoverDelay); return () => clearTimeout(timer); } return () => { }; }, [isHovered, preloadOnHover, hoverDelay, shouldPreload]); const handleMouseEnter = preloadOnHover ? () => setIsHovered(true) : undefined; const handleMouseLeave = preloadOnHover ? () => setIsHovered(false) : undefined; return ((0, jsx_runtime_1.jsx)("div", { onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, style: { display: 'contents' }, ref: ref, children: (0, jsx_runtime_1.jsx)(ErrorBoundary_1.ErrorBoundary, { fallback: errorFallback, onError: onError, children: (0, jsx_runtime_1.jsxs)(react_1.Suspense, { fallback: fallback, children: [shouldPreload && (0, jsx_runtime_1.jsx)(LazyComponent, { ...props, ref: ref }), !shouldPreload && (0, jsx_runtime_1.jsx)(LazyComponent, { ...props, ref: ref })] }) }) })); }); } // ============================================================================ // LAZY LOADING UTILITIES // ============================================================================ /** * Create lazy component with Suspense wrapper */ function createLazyComponentWithSuspense(loader, fallback = null) { const LazyComponent = (0, react_1.lazy)(loader); return function SuspenseWrapper(props) { return ((0, jsx_runtime_1.jsx)(react_1.default.Suspense, { fallback: fallback, children: (0, jsx_runtime_1.jsx)(LazyComponent, { ...props }) })); }; } /** * Route-based lazy component loader */ const RouteBasedLazy = ({ routeMap, currentRoute, componentProps = {}, fallback = null, errorFallback, }) => { const Component = routeMap[currentRoute]; if (!Component) { if (errorFallback) { const ErrorComponent = errorFallback; return ((0, jsx_runtime_1.jsx)(ErrorComponent, { error: new Error(`Route not found: ${currentRoute}`), retry: () => window.location.reload() })); } return (0, jsx_runtime_1.jsxs)("div", { children: ["Route not found: ", currentRoute] }); } const suspenseProps = { fallback, }; if (errorFallback) { suspenseProps.errorFallback = errorFallback; } return ((0, jsx_runtime_1.jsx)(exports.SuspenseWrapper, { ...suspenseProps, children: (0, jsx_runtime_1.jsx)(Component, { ...componentProps }) })); }; exports.RouteBasedLazy = RouteBasedLazy; /** * Wrapper component that only renders children when in viewport */ const IntersectionWrapper = ({ children, placeholder = null, options, className, style, onIntersect, }) => { const [elementRef, isIntersecting] = (0, lazy_loading_1.useIntersectionObserver)(options); react_1.default.useEffect(() => { if (isIntersecting && onIntersect) { onIntersect(); } }, [isIntersecting, onIntersect]); return ((0, jsx_runtime_1.jsx)("div", { ref: (node) => { if (elementRef.current !== node) { elementRef.current = node; } }, className: className, style: style, children: isIntersecting ? children : placeholder })); }; exports.IntersectionWrapper = IntersectionWrapper; const LazyImage = ({ src, alt, placeholder = 'blur', className, ...props }) => { const imgRef = react_1.default.useRef(null); const [isLoaded, setIsLoaded] = react_1.default.useState(false); react_1.default.useEffect(() => { const img = imgRef.current; if (img && img.complete) { setIsLoaded(true); } }, []); return ((0, jsx_runtime_1.jsxs)("div", { className: "image-container", children: [!isLoaded && placeholder, (0, jsx_runtime_1.jsx)("img", { ref: imgRef, src: src, alt: alt, className: className, loading: "lazy", decoding: "async", style: { display: isLoaded ? 'block' : 'none' }, onLoad: () => setIsLoaded(true), ...props })] })); }; exports.LazyImage = LazyImage;