import { useCallback, useMemo, memo, useState, useEffect } from "@wordpress/element"; import { FilterPart, HeaderWithFilter } from "./filter/filterPart"; import { __ } from "@wordpress/i18n"; import { HeartIcon, HeartIconFull, ImportIcon, PreviewIcon, ProBadgeIcon, RotateIcon } from "./icons"; import { PATTERN_CATEGORIES, DEFAULTS, KEYBOARD_KEYS, API_ENDPOINTS } from "./constants"; import { decodeEntities } from "@wordpress/html-entities"; import FilterSidebar from "./filter/sidebarFilter"; // Pattern categories moved to constants.js const Skeleton = (props) => { const { type, size, loop, unit, c_s, classes } = props; const getSize = () => { let css = {}; switch (type) { case "image": case "circle": css = { width: size ? size + "px" : "300px", height: size ? size + "px" : "300px" }; break; case "title": css = { width: `${size ? size : "100"}${unit ? unit : "%"}` }; break; case "button": css = { width: size ? size + "px" : "90px" }; break; case "custom_size": css = { width: `${c_s.size1 ? c_s.size1 : "100"}${c_s.unit1 ? c_s.unit1 : "%"}`, height: `${c_s.size2 ? c_s.size2 : "20"}${c_s.unit2 ? c_s.unit2 : "px"}`, borderRadius: c_s.br ? c_s.br + "px" : "0px", }; break; default: break; } return css; }; return ( <> {loop ? ( <> {Array(parseInt(loop)) .fill("1") .map((x, i) => { return (
); })} ) : (
)} ); }; const Tooltip = ({text}) => { return (
{text}
) } // Individual Card Component. export const PremadeCard = memo(({ data, localizedData, wishListArr, setWListAction, reload, reloadId, onImport }) => { const isInWishlist = wishListArr?.includes(String(data.ID)); const isLoading = reload && reloadId === data.ID; const isPro = data?.pro; const handleWishlistClick = useCallback(() => { setWListAction(data.ID, isInWishlist ? "remove" : ""); }, [data.ID, isInWishlist, setWListAction]); const handleImportClick = useCallback(() => { if(data?.pages?.length > 0) { return onImport(data.pages); } if (!isPro) { onImport(data.ID, data.pro); } }, [data.ID, data.pro, isPro, onImport]); return (
{data.name}
{decodeEntities(data?.name || "")}
{ if (e.key === KEYBOARD_KEYS.ENTER || e.key === KEYBOARD_KEYS.SPACE) { handleWishlistClick(); } }} aria-label={ isInWishlist ? __("Remove from wishlist", "post-carousel") : __("Add to wishlist", "post-carousel") } > {isInWishlist ? : } {isPro ? ( {__("Pro", "post-carousel")} ) : ( { if (e.key === KEYBOARD_KEYS.ENTER || e.key === KEYBOARD_KEYS.SPACE) { handleImportClick(); } }} aria-label={__("Import", "post-carousel") + " " + data.name} style={isLoading ? { pointerEvents: "none", opacity: 0.6 } : {}} > {isLoading ? ( ) : ( <> {__("Import", "post-carousel")} )} )}
); }); // Loading Skeleton Component. export const LoadingSkeleton = memo(() => (
{Array(25) .fill(null) .map((_, i) => (
))}
)); // Empty State Component. export const EmptyState = memo(() => ( {__("No Data found...", "post-carousel")} )); // Main Grid Component. export const PremadeGrid = memo( ({ premadeLists, column, searchQuery, freePro, showWishList, wishListArr, localizedData, setWListAction, reload, reloadId, onImport, loading, }) => { // Memoize filter function const filteredLists = useMemo(() => { return premadeLists.filter((data) => { const searchMatch = !searchQuery || data.name?.toLowerCase().includes(searchQuery.toLowerCase()); const freeProMatch = freePro === "all" || (freePro === "pro" && data.pro) || (freePro === "free" && !data.pro); const wishListMatch = !showWishList || wishListArr?.includes(String(data.ID)); return searchMatch && freeProMatch && wishListMatch; }); }, [premadeLists, searchQuery, freePro, showWishList, wishListArr]); // Loading state. if (loading) { return ; } // Empty state if (filteredLists.length === 0) { return ; } // Grid with data return (
{filteredLists.map((data) => ( ))}
); } ); // Main StarterSites Component const ReadyPatterns = (props) => { const { filterValue, state, setState, // useState, // useEffect, // useRef, _changeVal, filterByCategoryKey, setWListAction, wishListArr, _fetchFile, onClose, currentBlockName, isSingleBlock, } = props; const { current, designs, reload, reloadId, fetching, loading } = state; const localizedData = sp_smart_post_block_localize || {}; // Local state management. const [column, setColumn] = useState(DEFAULTS.COLUMN); const [searchQuery, setSearchQuery] = useState(DEFAULTS.SEARCH_QUERY); const [trend, setTrend] = useState(DEFAULTS.TREND); const [freePro, setFreePro] = useState(DEFAULTS.FREE_PRO); const [showWishList, setShowWishList] = useState(false); // Memoize sorted lists. const premadeLists = useMemo(() => { if (!current.length) return []; const lists = [...current]; if (trend === "latest" || trend === "all") { return lists.sort((a, b) => b.ID - a.ID); } if (trend === "popular" && lists[0]?.hit !== undefined) { return lists.sort((a, b) => b.hit - a.hit); } return lists; }, [current, trend]); // Handle filter changes. const handleFilterChange = useCallback( (type) => { const filteredData = type === "all" ? designs : filterByCategoryKey(designs, type); setState((prev) => ({ ...prev, designFilter: type, current: filteredData, })); }, [designs, filterByCategoryKey, setState] ); // Unified state change handler. const changeStates = useCallback( (type, value) => { const handlers = { freePro: () => setFreePro(value), search: () => setSearchQuery(value), column: () => setColumn(value), wishlist: () => setShowWishList(value), trend: () => setTrend(value), filter: () => handleFilterChange(value), }; handlers[type]?.(); }, [handleFilterChange] ); // Memoize import handler. const handleImport = useCallback( (templateID, isPro) => { _changeVal(templateID, isPro); }, [_changeVal] ); return ( <> {isSingleBlock && ( )}
{/*

{__("Ready Patterns", "post-carousel")}

*/}
{!isSingleBlock && ( )}
); }; export default ReadyPatterns;