diff --git a/src/assets/css/skeleton-loader.css b/src/assets/css/skeleton-loader.css new file mode 100644 index 0000000..063bdcb --- /dev/null +++ b/src/assets/css/skeleton-loader.css @@ -0,0 +1,33 @@ +:root { + --loader-width: auto; + --loader-height: auto; + --text-container-width: 400px; + --text-container-height: auto; + --line-height: 1.5; +} + +.image-skeleton-loader, +.blog-text-skeleton-loader { + width: var(--loader-width); + height: var(--loader-height); + background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); + background-size: 200% 100%; + animation: loading 1.5s infinite; +} + +.image-skeleton-loader{ + border-radius: 10px; +} + +.blog-text-skeleton-loader { + line-height: calc(var(--text-container-height) * var(--line-height)); +} + +@keyframes loading { + 0% { + background-position: -200% 0; + } + 100% { + background-position: 200% 0; + } +} \ No newline at end of file diff --git a/src/components/Blog/Blog.js b/src/components/Blog/Blog.js index d9d901c..c276172 100644 --- a/src/components/Blog/Blog.js +++ b/src/components/Blog/Blog.js @@ -1,21 +1,62 @@ -import React from "react"; import singlePost from "../../assets/images/single-post/1.jpg"; +import { BlogLoader, ImageLoader } from "../../lib/SkeletonLoaders"; +import LazyImage from "../../lib/LazyImage"; -function Blog({ blogItem, imgUrl }) { - const blogImg = - blogItem?.meta_value != null - ? `${imgUrl}/${blogItem?.meta_value}` - : singlePost; +/** + * Renders a blog post component. + * @returns {JSX.Element} The rendered blog post component. + */ +function Blog({ blogItem, imgUrl, loader }) { + // Generate a unique ID + const uniqueId = `element_${Math.random().toString(36).substr(2, 9)}`; + + const blogImg = `${imgUrl}/${blogItem?.meta_value || singlePost}`; + + const imgLoaderStyles = { + "--loader-width": "750px", + "--loader-height": "550px", + }; + + const headerLoaderStyles = { + "--text-container-width": "300px", + "--text-container-height": "34px", + }; + + const bodyTextLoaderStyles = { + "--text-container-width": "770px", + "--text-container-height": "150px", + }; return ( <>
I don't want no agro brilliant are you taking the piss skive off super diff --git a/src/components/Blog/BlogDetail.js b/src/components/Blog/BlogDetail.js index 85acbf4..0b02c21 100644 --- a/src/components/Blog/BlogDetail.js +++ b/src/components/Blog/BlogDetail.js @@ -16,16 +16,20 @@ import StickyHeaderNav from "../StickyHeader/StickyHeaderNav"; */ function BlogDetail() { const [drawer, drawerAction] = useToggle(false); + const [isLoading, setIsLoading] = useState(false); const [blogs, setBlogs] = useState([]); const { id } = useParams(); useEffect(() => { const fetchBlogs = async () => { + setIsLoading(true); try { const res = await BlogData(); setBlogs(res.data); } catch (err) { console.log("Error loading blogdata", err); + } finally { + setIsLoading(false); } }; @@ -52,7 +56,7 @@ function BlogDetail() { { link: "/blog", title: "Blogs" }, { link: `/blog/blogdetail/${id}`, - title: blogItem ? blogItem.post_title : "Post not found", + title: isLoading ? "please wait..." : blogItem ? blogItem.post_title : "Post not found", }, ]} /> @@ -62,7 +66,7 @@ function BlogDetail() {
-+ diff --git a/src/components/News/Blogs.js b/src/components/News/Blogs.js index 371ec3e..7d6abc0 100644 --- a/src/components/News/Blogs.js +++ b/src/components/News/Blogs.js @@ -26,7 +26,7 @@ function Blogs({ pathname }) { const renderBlogs = () => { return blogs?.blogdata?.map((blog, index) => { const options = { - weekday: "long", + weekday: "short", year: "numeric", month: "long", day: "numeric", diff --git a/src/lib/LazyImage.js b/src/lib/LazyImage.js new file mode 100644 index 0000000..0d3770c --- /dev/null +++ b/src/lib/LazyImage.js @@ -0,0 +1,52 @@ +import React, { useEffect, useRef, useState } from 'react'; + +/** + * Renders an image lazily using the Intersection Observer API. + * The image is initially hidden and becomes visible once it enters the viewport. + * This approach improves performance by only loading images that are actually visible to the user. + * + * @returns {JSX.Element} - The lazy image component. + */ +function LazyImage({ src, alt }) { + const imgRef = useRef(); + const [isVisible, setIsVisible] = useState(false); + + useEffect(() => { + const observer = new IntersectionObserver( + (entries) => { + const [entry] = entries; + if (entry.isIntersecting) { + setIsVisible(true); + observer.unobserve(imgRef.current); // Stop observing once the image is in the viewport + } + }, + { + root: null, // Viewport + rootMargin: '0px', // No margin + threshold: 0.1, // Percentage of the image that needs to be visible + } + ); + + if (imgRef.current) { + observer.observe(imgRef.current); + } + + return () => { + if (imgRef.current) { + observer.unobserve(imgRef.current); + } + }; + }, []); + + return ( + + ); +} + +export default LazyImage; diff --git a/src/lib/SkeletonLoaders.js b/src/lib/SkeletonLoaders.js new file mode 100644 index 0000000..3564679 --- /dev/null +++ b/src/lib/SkeletonLoaders.js @@ -0,0 +1,14 @@ +import "../assets/css/skeleton-loader.css" + +/** + * Renders a placeholder `
` element with the class name "image-skeleton-loader". + * This component is typically used as a placeholder for an image while it is being loaded. + * The CSS class "image-skeleton-loader or blog-text-skeleton-loader" can be used to style the placeholder element. + * + * @returns {React.Element} The rendered `` element with the class name "image-skeleton-loader or blog-text-skeleton-loader". + */ +export const ImageLoader = () => ; + +export const BlogLoader = () => + + \ No newline at end of file