/**
* WordPress dependencies
*/
import { __ } from "@wordpress/i18n";
import { useEffect, useState, createRef, useRef, memo } from "@wordpress/element";
import { safeHTML } from "@wordpress/dom";
import { applyFilters } from "@wordpress/hooks";
import { dateI18n, format, getSettings } from "@wordpress/date";
import { select } from "@wordpress/data";
/**
* External depencencies
*/
import Slider from "react-slick";
import parse from "html-react-parser";
/**
* Internal depencencies
*/
import Inspector from "./inspector";
import Style from "./style";
import PostCarouselIcon from "./icon";
import { Templates } from './templates/templates';
import defaultAttributes from './attributes';
import {
ebJsonStringCheck,
CustomQuery,
DynamicInputValueHandler,
BrowseTemplate,
BlockProps,
withBlockContext,
sanitizeIconValue
} from "@essential-blocks/controls";
const SlickArrow = (props) => {
const { className, style, onClick, faClass } = props;
return (
<div className={className} style={{ ...style }} onClick={onClick}>
<i className={sanitizeIconValue(faClass)}></i>
</div>
);
};
const Edit = (props) => {
const { attributes, setAttributes, isSelected } = props;
const {
blockId,
preset,
showThumbnail,
showTitle,
titleLength,
titleTag,
showContent,
contentLength,
expansionIndicator,
showReadMore,
readmoreText,
showMeta,
headerMeta,
footerMeta,
authorPrefix,
datePrefix,
classHook,
arrows,
adaptiveHeight,
autoplay,
autoplaySpeed,
dots,
infinite,
pauseOnHover,
speed,
slideToShowRange,
TABslideToShowRange,
MOBslideToShowRange,
dotPreset,
leftArrowIcon,
rightArrowIcon,
cover,
showBlockContent,
showFallbackImg,
fallbackImgUrl,
fallbackImgAlt,
isRTLEnable
} = attributes;
const [queryResults, setQueryResults] = useState(false);
// const [didMount, setDidMount] = useState(false);
const didMount = useRef(false)
const dateFormat = getSettings().formats.date;
// you must declare this variable
const enhancedProps = {
...props,
blockPrefix: 'eb-post-carousel',
style: <Style {...props} />
};
useEffect(() => {
if (eb_conditional_localize && eb_conditional_localize.editor_type == 'edit-site') {
const { isRTL } = select('core/edit-site').getSettings();
setAttributes({ isRTLEnable: isRTL });
} else {
const { isRTL } = select("core/editor").getEditorSettings();
setAttributes({ isRTLEnable: isRTL });
}
}, []);
// this useEffect is for creating a unique id for each block's unique className by a random unique number
useEffect(() => {
if (showBlockContent && typeof queryResults === 'object') {
didMount.current = true
}
else {
didMount.current = false
}
}, [showBlockContent]);
const TABslideToShowRanges = TABslideToShowRange ? TABslideToShowRange : 2;
const MOBslideToShowRanges = MOBslideToShowRange ? MOBslideToShowRange : 1;
/**
* func slideToShowCount
* @param {number} range
* @param {object} posts
* @returns {number}
*/
const slideToShowCount = (range, posts) => {
if (typeof posts === 'object' && Object.keys(posts).length < range) {
return Object.keys(posts).length
}
return range
}
const settings = {
arrows,
autoplay,
autoplaySpeed,
dots,
infinite,
pauseOnHover,
slidesToShow: slideToShowCount(slideToShowRange, queryResults),
speed,
rtl: isRTLEnable,
prevArrow: <SlickArrow faClass={leftArrowIcon} />,
nextArrow: <SlickArrow faClass={rightArrowIcon} />,
responsive: [
{
breakpoint: 1024,
settings: {
slidesToShow: slideToShowCount(TABslideToShowRanges, queryResults),
},
},
{
breakpoint: 767,
settings: {
slidesToShow: slideToShowCount(MOBslideToShowRanges, queryResults),
},
},
],
};
const slider = createRef();
return cover.length ? (
<div>
<img src={cover} alt="post carousel" style={{ maxWidth: "100%" }} />
</div>
) : (
<>
{isSelected && showBlockContent && <Inspector {...props} slider={slider} setQueryResults={setQueryResults} />}
{showBlockContent && didMount.current === false && (
<>
{queryResults === false && (
<div className="eb-loading">
<img src={`${EssentialBlocksLocalize?.image_url}/ajax-loader.gif`} alt="Loading..." />
</div>
)}
<CustomQuery
isEdit={true}
attributes={attributes}
setAttributes={setAttributes}
setQueryResults={setQueryResults}
/>
</>
)}
<BlockProps.Edit {...enhancedProps}>
<BrowseTemplate
{...props}
Icon={PostCarouselIcon}
title={"Post Carousel"}
description={"Choose a template for the Post Carousel or start blank."}
patterns={Templates}
/>
{showBlockContent && (
<>
{queryResults !== false && (
<div className={`eb-parent-wrapper eb-parent-${blockId} ${classHook}`}>
<div
className={`eb-post-carousel-wrapper ${blockId} ${dotPreset} ${preset} ${dots ? "eb-slider-dots" : ""
} ${adaptiveHeight ? "equal-height" : ""}`}
data-id={blockId}
>
<Slider {...settings} ref={slider}>
{typeof queryResults === "object" &&
queryResults.length > 0 &&
queryResults.map((post, index) => {
//Generate Featured Image
const {
featuredImageInfo: { url: imageSourceUrl, alt: featuredImageAlt } = {},
} = post;
const featuredImage = showThumbnail && (
<img key={index} src={imageSourceUrl} alt={featuredImageAlt} />
);
//Generate Title
const title = post?.title?.rendered;
const titleWithLimitWords =
titleLength >= 0 ? title.trim().split(" ", titleLength).join(" ") : title;
const titleHTML = safeHTML(`
<${titleTag} class="ebpg-entry-title">
<a class="ebpg-carousel-post-link" href="#" title="${titleWithLimitWords}">
${titleWithLimitWords}
</a>
</${titleTag}>
`);
//Generate Excerpt & Read More
let excerpt = post?.excerpt ? post?.excerpt?.rendered : post?.content?.rendered;
const excerptElement = document.createElement("div");
excerptElement.innerHTML = excerpt ? excerpt : "";
excerpt = excerptElement.textContent || excerptElement.innerText || "";
const excerptWithLimitWords =
contentLength >= 0
? excerpt.trim().split(" ", contentLength).join(" ")
: excerpt;
const avatarUrl = (author) => {
if (author.avatar_urls && author.avatar_urls[96]) {
return author.avatar_urls[96];
} else {
return "http://1.gravatar.com/avatar/467ceabf70aaa0e555b7dd11c9729241?s=96&d=mm&r=g";
}
};
const avatar =
post._embedded && post._embedded.author ? (
<div className="ebpg-author-avatar">
<a href={post._embedded.author[0].link}>
<img
alt={
post._embedded.author[0].name
? post._embedded.author[0].name
: post._embedded.author[0].slug
}
src={avatarUrl(post._embedded.author[0])}
/>
</a>
</div>
) : (
""
);
const date = (
<span className="ebpg-posted-on">
<DynamicInputValueHandler
value={datePrefix}
tagName="span"
onChange={(datePrefix) =>
setAttributes({
datePrefix,
})
}
readOnly={true}
/>{" "}
<time
dateTime={format(
"c",
post.date_gmt
)}
>
{dateI18n(
dateFormat,
post.date_gmt
)}
</time>
</span>
);
const author =
post._embedded && post._embedded.author ? (
<span className="ebpg-posted-by">
<DynamicInputValueHandler
value={authorPrefix}
tagName="span"
onChange={(frontTitle) =>
setAttributes({
frontTitle,
})
}
readOnly={true}
/>{" "}
<a
href="#"
title={
post._embedded.author[0].name
? post._embedded.author[0].name
: post._embedded.author[0].slug
}
rel="author"
>
{post._embedded.author[0].name
? post._embedded.author[0].name
: post._embedded.author[0].slug}
</a>
</span>
) : (
""
);
const postTermsVal = {};
post._embedded &&
post._embedded["wp:term"] &&
post._embedded["wp:term"].length > 0 &&
post._embedded["wp:term"].map((item) => {
let termObj = {};
let termName = "";
item.length > 0 &&
item.map((term) => {
termName = term.taxonomy;
termObj[term.slug] = {
name: term.name,
id: term.id,
link: term.link,
slug: term.slug,
};
});
postTermsVal[termName] = termObj;
});
const postTermsHtml = {};
if (Object.keys(postTermsVal).length > 0) {
Object.keys(postTermsVal).map((term) => {
let termClass = term;
if (term === "category") {
termClass = "categories";
} else if (term === "post_tag") {
termClass = "tags";
}
let markup = `<div className="ebpg-meta ebpg-${termClass}-meta">`;
Object.keys(postTermsVal[term]).length > 0 &&
Object.keys(postTermsVal[term]).map((item, index) => {
markup += `
<a
key=${index}
href=${postTermsVal[term][item].link}
title=${postTermsVal[term][item].name}
>
${postTermsVal[term][item].name}
</a>
`;
});
markup += `</div>`;
postTermsHtml[term] = markup;
});
}
const categories = postTermsVal.category ? (
<div className="ebpg-meta ebpg-categories-meta">
{Object.keys(postTermsVal.category).map((item, index) => (
<a
key={index}
href={postTermsVal.category[item].link}
title={postTermsVal.category[item].name}
>
{postTermsVal.category[item].name}
</a>
))}
</div>
) : (
""
);
const tags = postTermsVal.post_tag ? (
<div className="ebpg-meta ebpg-tags-meta">
{Object.keys(postTermsVal.post_tag).map((item, index) => (
<a
key={index}
href={postTermsVal.post_tag[item].link}
title={postTermsVal.post_tag[item].name}
>
{postTermsVal.post_tag[item].name}
</a>
))}
</div>
) : (
""
);
const metaObject = {
date,
author,
categories,
tags,
};
const headerMetaItems = ebJsonStringCheck(headerMeta)
? JSON.parse(headerMeta).map((item) => item.value)
: [];
const headerMetaHtml = showMeta ? (
<div className="ebpg-entry-meta ebpg-header-meta">
{headerMetaItems.includes("avatar") && avatar}
<div className="ebpg-entry-meta-items">
{headerMetaItems.map((item) => {
if (metaObject.hasOwnProperty(item)) {
return metaObject[item];
} else if (postTermsHtml.hasOwnProperty(item)) {
return parse(postTermsHtml[item]);
} else {
if (item === "avatar") {
return;
}
return applyFilters("essential_blocks_post_carousel_dynamic_fields_markup",
"",
item
);
}
})}
</div>
</div>
) : (
""
);
const footerMetaItems = ebJsonStringCheck(footerMeta)
? JSON.parse(footerMeta).map((item) => item.value)
: [];
const footerMetaHtml = showMeta ? (
<div className="ebpg-entry-meta ebpg-footer-meta">
{footerMetaItems.includes("avatar") && avatar}
<div className="ebpg-entry-meta-items">
{footerMetaItems.map((item) => {
if (metaObject.hasOwnProperty(item)) {
return metaObject[item];
} else if (postTermsHtml.hasOwnProperty(item)) {
return parse(postTermsHtml[item]);
} else {
if (item === "avatar") {
return;
}
return applyFilters("essential_blocks_post_carousel_dynamic_fields_markup",
"",
item
);
}
})}
</div>
</div>
) : (
""
);
return (
<article
className="ebpg-carousel-post ebpg-post-carousel-column"
data-id={post.id}
>
<div className="ebpg-carousel-post-holder">
{preset === "style-4" && (
<a className="ebpg-post-link-wrapper" href="#"></a>
)}
<div className="ebpg-entry-media">
{showThumbnail && (
<div className="ebpg-entry-thumbnail">
{preset !== "style-4" && (
<a className="ebpg-post-link-wrapper" href="#"></a>
)}
{post._embedded &&
post._embedded["wp:featuredmedia"] &&
post._embedded["wp:featuredmedia"].length > 0 && (
<img
src={
post._embedded["wp:featuredmedia"][0]
.source_url
}
alt={post?.title?.alt_text}
/>
)}
{post._embedded && !post._embedded["wp:featuredmedia"] && (
<>
{showFallbackImg && fallbackImgUrl && (
<img
src={fallbackImgUrl}
alt={`${fallbackImgAlt ? fallbackImgAlt : `No Thumbnail Available`}`}
/>
)}
{(!showFallbackImg || (showFallbackImg && fallbackImgUrl === undefined)) && (
<img
src={EssentialBlocksLocalize?.placeholder_image}
alt="No Thumbnail Available"
/>
)}
</>
)}
</div>
)}
</div>
<div className="ebpg-entry-wrapper">
{/* Header Meta */}
{showMeta && headerMetaHtml}
{showTitle && (
<header
className="ebpg-entry-header"
dangerouslySetInnerHTML={{
__html: titleHTML,
}}
></header>
)}
<div className="ebpg-entry-content">
{showContent && excerptWithLimitWords && (
<div className="ebpg-carousel-post-excerpt">
<p>
{excerptWithLimitWords}
{__(expansionIndicator)}
</p>
</div>
)}
{showReadMore && (
<div className="ebpg-readmore-btn">
<a href="#">{__(readmoreText)}</a>
</div>
)}
</div>
{/* Footer Meta */}
{showMeta && footerMetaHtml}
</div>
</div>
</article>
);
})}
{typeof queryResults != "undefined" && queryResults.length < 1 && <p>No Posts Found</p>}
</Slider>
</div>
</div>
)}
</>
)}
</BlockProps.Edit>
</>
);
}
export default memo(withBlockContext(defaultAttributes)(Edit))