PluginProbe
Smart Post – Post Grid, Post Carousel, Post Slider Gutenberg Blocks for Blog & News / 4.0.2
Smart Post – Post Grid, Post Carousel, Post Slider Gutenberg Blocks for Blog & News v4.0.2
4.0.8 4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 4.0.2 4.0.1 2.3.5 2.3.6 2.4.0 2.4.1 2.4.10 2.4.11 2.4.12 2.4.13 2.4.14 2.4.15 2.4.16 2.4.17 2.4.18 2.4.19 2.4.2 2.4.20 2.4.21 All 88 releases
post-carousel / src / blocks / shared / helpFn.js

helpFn.js in Smart Post – Post Grid, Post Carousel, Post Slider Gutenberg Blocks for Blog & News 4.0.2, at src/blocks/shared/helpFn.js

986 lines 26.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { ListStyle1, ListStyle2, ListStyle3, ListStyle4, ListStyle5, ListStyle6, ListStyle7 } from "../../icons/icons";
2
3 import { useDispatch, useSelect } from "@wordpress/data";
4 import { createBlock } from "@wordpress/blocks";
5 import { useEffect } from "@wordpress/element";
6 import { blockPreviewPanelLink } from "../../controls/constants";
7
8 // Panel body accordions close or open function.
9 export const togglePanelBody = (attributes, currentAccordionName, setAttributes) => {
10 const { openedAccordion } = attributes;
11
12 if (openedAccordion === currentAccordionName) {
13 setAttributes({ openedAccordion: "" });
14 } else {
15 setAttributes({ openedAccordion: currentAccordionName });
16 }
17 };
18
19 export function generateLayeredShadows(baseColor, layers = 5, offsetStep = 4) {
20 const result = [];
21 const rgba = toRGBA(baseColor);
22
23 for (let i = 0; i < layers; i++) {
24 const offset = offsetStep * (i + 1);
25 const alpha = 0.5 - i * 0.1; // Start at 0.5 and decrease by 0.1 each layer
26 const shadowColor = `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${alpha.toFixed(1)})`;
27 result.push(`${shadowColor} ${offset}px ${offset}px`);
28 }
29
30 return result.join(", ");
31 }
32
33 // Helper: Converts hex or rgba string to RGB components
34 function toRGBA(color) {
35 if (color?.startsWith("#")) {
36 const hex = color.replace("#", "");
37 const bigint = parseInt(
38 hex.length === 3
39 ? hex
40 .split("")
41 .map((c) => c + c)
42 .join("")
43 : hex,
44 16
45 );
46
47 return {
48 r: (bigint >> 16) & 255,
49 g: (bigint >> 8) & 255,
50 b: bigint & 255,
51 };
52 } else if (color?.startsWith("rgb")) {
53 const match = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
54 if (match) {
55 return {
56 r: parseInt(match[1]),
57 g: parseInt(match[2]),
58 b: parseInt(match[3]),
59 };
60 }
61 }
62 throw new Error("Unsupported color format. Use hex (#ff0066) or rgba().");
63 }
64
65 export const isEmptyObject = (obj) =>
66 obj && typeof obj === "object" && !Array.isArray(obj) && Object.keys(obj).length === 0;
67
68 // Css generator to object.
69 export const objectToCssString = (dynamicCss) => {
70 let css = "";
71 dynamicCss?.map((item) => {
72 if (item.styles && typeof item.styles === "object" && Object.keys(item.styles).length > 0) {
73 let propertyValue = "";
74 Object.entries(item.styles).forEach(([property, value]) => {
75 if (!["", "%", "px", " !important", "undefined"].includes(value)) {
76 propertyValue += `${property}: ${value};`;
77 }
78 });
79 css += propertyValue ? `${item.class} {${propertyValue}}` : "";
80 }
81 });
82 return css;
83 };
84
85 // Spacing property css and class generator (margin or padding).
86 // spacingValue is margin or padding value.
87 // device is (desktop, tablet, or mobile)
88 // spacingType is check (margin or padding).
89 // returnSpacingClass is just return css class. (sp-mt-8px)
90
91 export const Icon = ({ iconSourse, color }) => {
92 const icons = {
93 list1: <ListStyle1 fillColor={color} />,
94 list2: <ListStyle2 fillColor={color} />,
95 list3: <ListStyle3 fillColor={color} />,
96 list4: <ListStyle4 fillColor={color} />,
97 list5: <ListStyle5 fillColor={color} />,
98 list6: <ListStyle6 fillColor={color} />,
99 list7: <ListStyle7 fillColor={color} />,
100 };
101 return icons[iconSourse] || null;
102 };
103
104 export const formatDate = (dateString, type = "date") => {
105 const date = new Date(dateString);
106
107 if (type === "daysAgo") {
108 const now = new Date();
109 const diffTime = now - date;
110 const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
111
112 if (diffDays === 0) return "Today";
113 if (diffDays === 1) return "1 day ago";
114 return `${diffDays} days ago`;
115 }
116
117 // Default: formatted date like "Jun 19, 2025"
118 const options = { month: "short", day: "numeric", year: "numeric" };
119 return date.toLocaleDateString("en-US", options);
120 };
121
122 export const getClipPath = (style, position) => {
123 switch (style) {
124 case "one":
125 switch (position) {
126 case "right":
127 return "polygon(100% 0%, 15% 0%, 0% 0%, 15% 102%, 100% 102%)";
128 case "left":
129 return "polygon(0% 0%, 85% 0%, 100% 0%, 85% 102%, 0% 102%)";
130 default:
131 return "none";
132 }
133 case "two":
134 switch (position) {
135 case "right":
136 return "polygon(100% 0%, 15% 0%, 15% 0%, 0% 102%, 100% 102%)";
137
138 case "left":
139 return "polygon(0% 0%, 85% 0%, 85% 0%, 100% 102%, 0% 102%)";
140 default:
141 return "none";
142 }
143 case "three":
144 switch (position) {
145 case "right":
146 return "polygon(100% 0%, 15% 0%, 0% 50%, 15% 102%, 100% 102%)";
147
148 case "left":
149 return "polygon(0% 0%, 85% 0%, 100% 50%, 85% 102%, 0% 102%)";
150 default:
151 return "none";
152 }
153 case "four":
154 switch (position) {
155 case "right":
156 return "polygon( 100% -3px, 10% -7px, 10% 27%, 0% 52%, 10% 77%, 10% 102%, 100% 116%)";
157 case "left":
158 return "polygon(-5px -3px, 90% -7px, 90% 27%, 100% 52%, 90% 77%, 90% 102%, -3px 116%)";
159 default:
160 return "none";
161 }
162
163 default:
164 return "none";
165 }
166 };
167
168 export const spacingCss = (
169 spacingValue,
170 device,
171 spacingType,
172 returnSpacingClass = false,
173 currentScreen = "Desktop",
174 page = "editor"
175 ) => {
176 let generateNewCss = [];
177 let cssPrefixName = returnSpacingClass ? "sp-" : ".sp-"; // css class prefix
178 let cssUnit = spacingValue?.unit?.[device]; // css unit (px, em, %);
179 let positionKeys = Object.keys(spacingValue.device?.[device]); // position keys (top, right, bottom, left);
180
181 //Spacing css property.
182 const cssSpacingProperty = {
183 margin: {
184 top: {
185 cssProperty: "margin-top",
186 cssClass: "mt",
187 },
188 right: {
189 cssProperty: "margin-right",
190 cssClass: "mr",
191 },
192 bottom: {
193 cssProperty: "margin-bottom",
194 cssClass: "mb",
195 },
196 left: {
197 cssProperty: "margin-left",
198 cssClass: "ml",
199 },
200 },
201 padding: {
202 top: {
203 cssProperty: "padding-top",
204 cssClass: "pt",
205 },
206 right: {
207 cssProperty: "padding-right",
208 cssClass: "pr",
209 },
210 bottom: {
211 cssProperty: "padding-bottom",
212 cssClass: "pb",
213 },
214 left: {
215 cssProperty: "padding-left",
216 cssClass: "pl",
217 },
218 },
219 "border-width": {
220 top: {
221 cssProperty: "border-top-width",
222 cssClass: "bwt",
223 },
224 right: {
225 cssProperty: "border-right-width",
226 cssClass: "bwr",
227 },
228 bottom: {
229 cssProperty: "border-bottom-width",
230 cssClass: "bwb",
231 },
232 left: {
233 cssProperty: "border-left-width",
234 cssClass: "bwl",
235 },
236 },
237 "border-radius": {
238 top: {
239 cssProperty: "border-top-left-radius",
240 cssClass: "brt",
241 },
242 right: {
243 cssProperty: "border-top-right-radius",
244 cssClass: "brr",
245 },
246 bottom: {
247 cssProperty: "border-bottom-right-radius",
248 cssClass: "brb",
249 },
250 left: {
251 cssProperty: "border-bottom-left-radius",
252 cssClass: "brl",
253 },
254 },
255 };
256
257 const deviceSuffixes = {
258 Tablet: "md",
259 Mobile: "sm",
260 };
261
262 const deviceSuffix = deviceSuffixes[device] ? `-${deviceSuffixes[device]}` : "";
263
264 positionKeys?.map((spacingPosition) => {
265 let cssValue = parseInt(spacingValue.device?.[device]?.[spacingPosition]); // css value
266 let { cssProperty, cssClass } = cssSpacingProperty[spacingType][spacingPosition];
267
268 cssClass = `${cssPrefixName}${cssClass}${deviceSuffix}-${parseInt(
269 spacingValue.device?.[device][spacingPosition]
270 )}${spacingValue?.unit?.[device]}`;
271
272 let newSpaceCss = convertToClassName(cssClass); // css class pct ( .sp-mt-5% to .sp-mt-5pct)
273
274 // Return spacing class list or styles object
275 if (cssValue || cssValue === 0) {
276 if (returnSpacingClass) {
277 generateNewCss.push(`${newSpaceCss}`);
278 } else {
279 generateNewCss.push({
280 class: `.${newSpaceCss.slice(1)}`,
281 property: cssProperty,
282 value: `${cssValue}${cssUnit}${
283 page === "editor" && device.includes(currentScreen) ? " !important" : ""
284 }`,
285 });
286 }
287 }
288 });
289
290 return returnSpacingClass ? generateNewCss.join(" ") : generateNewCss;
291 };
292
293 export const buildSpacingClasses = (value, property) => {
294 const screenSizes = ["Desktop", "Tablet", "Mobile"];
295 const spacingCssCalls = screenSizes?.map((screenSize) => {
296 return spacingCss(value, screenSize, property, true);
297 });
298
299 return spacingCssCalls.filter((item) => item.trim() !== "").join(" ");
300 };
301
302 export const convertToClassName = (value) => {
303 let newValue = value.toString();
304
305 // Remove the '#' if it exists
306 if (newValue.startsWith("#")) {
307 newValue = newValue.slice(1);
308 }
309
310 // Replace spaces with hyphens
311 newValue = newValue.replace(/\s+/g, "-");
312
313 // Replace '%' with 'pct'
314 newValue = newValue.replace("%", "pct");
315
316 // Replace '.' with '-'
317 newValue = newValue.replace(/\./g, "-");
318
319 return newValue;
320 };
321
322 // Trim The excerpt content.
323 export const stringTrim = (content, attributes) => {
324 const getWords = (data, length = 10) => {
325 const words = data?.split(" ");
326 return words?.slice(0, length).join(" ");
327 };
328 const getCharacters = (data, length = 10) => {
329 if (!data) return;
330 return data?.substring(0, length);
331 };
332
333 let newContent = "";
334
335 if (typeof attributes === "object") {
336 if (attributes.unit === "words") {
337 newContent = getWords(content, attributes.value);
338 } else if (attributes.unit === "chars") {
339 newContent = getCharacters(content, attributes.value);
340 }
341 }
342
343 return "" !== newContent ? newContent : content;
344 };
345
346 export const getObjectValuesToJsArray = (object) => {
347 return Object?.values(object);
348 };
349
350 // Generate box shadow css.
351 export const boxCss = (enable, device, shadow, color) => {
352 if ("" === shadow[color]) {
353 return "";
354 }
355 if (shadow?.selectDefault !== "custom") {
356 return enable ? shadow?.selectDefault : "";
357 }
358 // If shadow have not any device property, return default box shadow.
359 if (!shadow?.device || "object" === typeof shadow?.value) {
360 return enable
361 ? `${shadow.unit.toLowerCase() === "inset" ? shadow.unit.toLowerCase() : ""} ${
362 shadow.value.top
363 }px ${shadow.value.right}px ${shadow.value.bottom}px ${shadow.value.left}px ${shadow[color]}`
364 : "none";
365 }
366 return enable
367 ? `${shadow.unit?.[device].toLowerCase() === "inset" ? shadow.unit?.[device].toLowerCase() : ""} ${
368 shadow.device?.[device].top
369 }px ${shadow.device?.[device].right}px ${shadow.device?.[device].bottom}px ${
370 shadow.device?.[device].left
371 }px ${shadow[color]}`
372 : "none";
373 };
374
375 export const jsonStringify = (data) => {
376 return JSON.stringify(data);
377 };
378
379 export const jsonParse = (data) => {
380 try {
381 return JSON.parse(data);
382 } catch (e) {
383 return console.error(e); // error in the above string (in this case, yes)
384 }
385 };
386
387 export const getBlockShortName = (name) => {
388 const shortName = name.replace("sp-smart-post-show/", "");
389 return shortName;
390 };
391
392 // export const arrayChunk = (arr, n = 1) => (arr.length ? [arr.slice(0, n), ...arrayChunk(arr.slice(n), n)] : []);
393 export const arrayChunk = (arr, n = 1) => {
394 if (!Array.isArray(arr)) return [];
395 if (n <= 0) n = 1;
396
397 const chunks = [];
398 for (let i = 0; i < arr.length; i += n) {
399 chunks.push(arr.slice(i, i + n));
400 }
401 return chunks;
402 };
403
404 // Color controls fn.
405 export const colorControls = (colorType, normalColor, gradientColor, bgImgObject = {}) => {
406 if (!colorType) {
407 return "";
408 }
409
410 const imageUrl = bgImgObject?.url;
411
412 const backgroundMap = {
413 transparent: "transparent",
414 bgColor: normalColor,
415 gradient: gradientColor,
416 image: imageUrl ? `url(${imageUrl})` : "none",
417 };
418
419 return backgroundMap[colorType] ?? normalColor;
420 };
421
422 export const hexToRgba = (hex, opacity) => {
423 const r = parseInt(hex.slice(1, 3), 16);
424 const g = parseInt(hex.slice(3, 5), 16);
425 const b = parseInt(hex.slice(5, 7), 16);
426 return `rgba(${r}, ${g}, ${b}, ${opacity})`;
427 };
428
429 export const toggleEqualHeight = (containerId, equalHeightEnable) => {
430 let container = document.getElementById(containerId);
431
432 if (!container) {
433 const iframe = document.getElementsByTagName("iframe")[0];
434 const iframeDoc = iframe?.contentWindow?.document;
435 container = iframeDoc?.getElementById(containerId) || null;
436 }
437
438 if (container) {
439 const cards = container.querySelectorAll(".sp-smart-post-card");
440 if (cards.length > 0) {
441 if (equalHeightEnable) {
442 let maxHeight = 0;
443 let prevCardHeight = 0;
444
445 const onAllImageLoad = () => {
446 // Determine the tallest card
447 cards.forEach((card) => {
448 prevCardHeight = card.style.height;
449 card.style.height = "auto"; // Reset height to get the true offsetHeight
450 const cardHeight = card.offsetHeight;
451 if (cardHeight > maxHeight) {
452 maxHeight = cardHeight;
453 }
454 });
455
456 // Set all cards to the tallest height
457 cards.forEach((card) => {
458 card.style.height = `${maxHeight}px`;
459 });
460 };
461
462 const imagePromise = Array.from(container.querySelectorAll(".sp-smart-post-card-image img")).map(
463 (img) =>
464 new Promise((resolve) => {
465 if (img.complete) {
466 resolve();
467 } else {
468 img.addEventListener("load", resolve);
469 img.addEventListener("error", resolve); // Handle broken images
470 }
471 })
472 );
473 Promise.all(imagePromise).then(onAllImageLoad);
474 } else {
475 // Set all cards to the tallest height
476 cards.forEach((card) => {
477 card.style.height = "";
478 });
479 }
480 }
481 }
482 };
483
484 export const wrapInMediaQuery = (cssString, mediaQuery) => {
485 return `@media ${mediaQuery} { ${cssString} }`;
486 };
487
488 export const scrollToTop = (topPosition) => {
489 window.scrollTo({
490 top: topPosition,
491 behavior: "smooth",
492 });
493 };
494
495 export const innerBlockTemplate = (title) => [["sp-smart-post-show/section-heading", { sectionHeadingLabel: title }]];
496
497 export const removeEmptyCss = (classString, property, value, importance) => {
498 return value
499 ? [
500 {
501 class: classString,
502 styles: {
503 [property]: `${value}${importance ? " !important" : ""}`,
504 },
505 },
506 ]
507 : [];
508 };
509
510 export const setDefaultValue = (attributes, attrKey, value, deviceType = "Desktop", position = false) => {
511 const { device } = attributes;
512
513 const deviceValue = device && !position ? { device: { ...attributes.device, [deviceType]: value } } : {};
514
515 const positionValue = position
516 ? {
517 device: {
518 ...attributes.device,
519 [deviceType]: {
520 ...attributes.device?.[deviceType],
521 [position]: value,
522 },
523 },
524 }
525 : {};
526
527 let newData = {
528 [attrKey]: {
529 ...attributes,
530 ...deviceValue,
531 ...positionValue,
532 },
533 };
534
535 return newData;
536 };
537
538 export const randomSolidColor = (colorType) => {
539 if ("multi-solid" === colorType) {
540 return `rgba(${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)}, ${Math.floor(
541 Math.random() * 255
542 )}, .7)`;
543 }
544 if ("multi-gradient" === colorType) {
545 const colorList = [
546 `linear-gradient(293deg, rgba(251, 218, 97, .7) -0.37%, rgba(255, 90, 205, .7) 100%)`,
547 `linear-gradient(90deg, rgba(203, 173, 109, .7) 0%, rgba(213, 51, 105, .7) 100%)`,
548 `linear-gradient(90deg, rgba(36, 198, 220, .7) 0%, rgba(81, 74, 157, .7) 100%)`,
549 `linear-gradient(90deg, rgba(255, 224, 0, .7) 0%, rgba(255, 75, 31, .7) 100%)`,
550 `linear-gradient(90deg, rgba(28, 216, 210, .7) 0%, rgba(147, 237, 199, .7) 100%)`,
551 `linear-gradient(90deg, rgba(255, 212, 82, .7) 0%, rgba(84, 74, 125, .7) 100%)`,
552 `linear-gradient(90deg, rgba(91, 134, 229, .7) 0%, rgba(54, 209, 220, .7) 100%)`,
553 `linear-gradient(90deg, rgba(253, 185, 155, .7) 0%, rgba(167, 112, 239, .7) 100%)`,
554 ];
555 return colorList[Math.floor(Math.random() * 8)];
556 }
557 };
558
559 export const spacingGenerate = (attr, device = "Desktop") => {
560 if (!attr) {
561 return "";
562 }
563 if (!attr?.device && "object" === typeof attr?.value) {
564 let unit = attr?.unit;
565 const { top, bottom, left, right } = attr?.value;
566 const keys = ["top", "bottom", "left", "right"];
567 const allEmpty = keys.every((key) => attr?.value[key] === "");
568 if (allEmpty) return "";
569 if (attr?.allChange) {
570 return `${top || 0}${unit}`;
571 }
572 return `${top || 0}${unit} ${right || 0}${unit} ${bottom || 0}${unit} ${left || 0}${unit}`;
573 }
574
575 let unit = attr?.unit?.[device];
576 const { top, bottom, left, right } = attr?.device?.[device] || {};
577 const keys = ["top", "bottom", "left", "right"];
578 const allEmpty = keys.every((key) => attr?.device?.[device][key] === "");
579 if (allEmpty) return "";
580 // if ( attr?.allChange ) { return `${top || 0 }${unit}` };
581
582 let space = `${top || 0}${unit} ${right || 0}${unit} ${bottom || 0}${unit} ${left || 0}${unit}`;
583 let margin = `${space}`;
584 return margin;
585 };
586
587 // Override old attribute value.
588 export const changeAttrData = (parentAttr, attr) => {
589 let newData = {
590 ...parentAttr,
591 };
592 attr?.forEach((data) => {
593 const { attrName, value } = data;
594
595 const dataType = parentAttr[attrName]?.type;
596 const deviceType = data?.deviceType ? data?.deviceType : "Desktop";
597
598 switch (dataType) {
599 case "string":
600 case "boolean":
601 newData = {
602 ...newData,
603 [attrName]: {
604 ...parentAttr[attrName],
605 default: value,
606 },
607 };
608 break;
609 case "object":
610 const { device, value: singleValue } = parentAttr[attrName]?.default;
611
612 const single = {
613 value,
614 };
615
616 const deviceValue = {
617 device: {
618 ...device,
619 [deviceType]: value,
620 },
621 };
622
623 const newValue = device ? deviceValue : single;
624
625 let defaultValue = {
626 ...parentAttr[attrName].default,
627 ...newValue,
628 };
629
630 newData = {
631 ...newData,
632 [attrName]: {
633 ...parentAttr[attrName],
634 default: defaultValue,
635 },
636 };
637 break;
638 }
639 });
640 return newData;
641 };
642
643 export const bgColor = (attr, colorType = "color") => {
644 const bgStyle = "bgColor" === attr[colorType]?.style ? "solidColor" : attr[colorType]?.style;
645 return `${attr[colorType][bgStyle]}`;
646 };
647
648 export const checkInArray = (value, arr = "default") => {
649 if (!value) return;
650 let defaultArray = arr === "default" ? ["social-profiles-layout-four", "social-profiles-layout-five"] : arr;
651 return defaultArray.includes(value) ? true : false;
652 };
653
654 export const rangerCss = (attr, device = "Desktop") => {
655 if ("" === attr?.device?.[device]) {
656 return "";
657 }
658 const unit = attr?.unit?.[device] || "";
659 return `${attr?.device?.[device]}${unit}`;
660 };
661
662 export const borderCss = (attr, device = "Desktop", colorType = "color") => {
663 if (attr?.device && "object" === typeof attr?.device) {
664 return {
665 "border-width": spacingGenerate(attr, device),
666 };
667 }
668 if (attr?.style && "string" === typeof attr?.style) {
669 return "none" !== attr.style
670 ? {
671 "border-style": attr.style,
672 "border-color": attr[colorType],
673 }
674 : {};
675 }
676 };
677
678 export const rearrangeAboveTitle = (items) => {
679 const taxonomyIndex = items?.findIndex((item) => item.value === "taxonomy");
680 const titleIndex = items.findIndex((item) => item.value === "title");
681
682 // Only move if both are found and taxonomy is not already before title
683 if (taxonomyIndex !== -1 && titleIndex !== -1 && taxonomyIndex !== titleIndex - 1) {
684 const taxonomyItem = items.splice(taxonomyIndex, 1)[0];
685 const newTitleIndex = items.findIndex((item) => item.value === "title");
686 items.splice(newTitleIndex, 0, taxonomyItem);
687 }
688 };
689 export const useAddChildBlock = (liveFilterEnable, paginationEnable, clientId) => {
690 const { replaceBlocks, replaceInnerBlocks, selectBlock } = useDispatch("core/block-editor");
691
692 const currentBlock = useSelect((select) => select("core/block-editor").getBlock(clientId), [clientId]);
693
694 const parentClientId = useSelect(
695 (select) => {
696 const { getBlock, getBlockParents } = select("core/block-editor");
697
698 // Get all parent IDs (closest first)
699 const parentIds = getBlockParents(clientId);
700
701 if (!parentIds?.length) return null;
702
703 // Find the first parent with the target block name
704 return (
705 parentIds.find((parentId) => {
706 const parentBlock = getBlock(parentId);
707 return parentBlock?.name === "sp-smart-post-show/smart-post-parent";
708 }) || null
709 );
710 },
711 [clientId]
712 );
713 const parentBlock = useSelect(
714 (select) => parentClientId && select("core/block-editor").getBlock(parentClientId),
715 [parentClientId]
716 );
717
718 useEffect(() => {
719 if (!currentBlock) return;
720
721 const isWrapped = parentBlock?.name === "sp-smart-post-show/smart-post-parent";
722 const shouldWrap = liveFilterEnable;
723 const optionalBlocks = [
724 {
725 name: "sp-smart-post-show/live-filter",
726 enable: liveFilterEnable,
727 },
728 {
729 name: currentBlock.name,
730 attrs: currentBlock.attributes,
731 innerBlocks: currentBlock.innerBlocks,
732 alwaysInclude: true,
733 },
734 // {
735 // name: 'sp-smart-post-show/pagination',
736 // enable: paginationEnable,
737 // }
738 ];
739 if (!isWrapped && shouldWrap) {
740 // Wrap current block inside a parent with optional children
741 const newInnerBlocks = optionalBlocks
742 .filter((b) => b.alwaysInclude || b.enable)
743 .map((b) => createBlock(b.name, b.attrs || {}, b.innerBlocks || []));
744
745 selectBlock(newInnerBlocks[0]?.clientId);
746 const newParentBlock = createBlock(
747 "sp-smart-post-show/smart-post-parent",
748 { align: currentBlock?.attributes?.align },
749 newInnerBlocks
750 );
751 replaceBlocks([clientId], newParentBlock);
752 return;
753 }
754
755 if (!shouldWrap && isWrapped) {
756 // Unwrap: restore the main block alone
757 const mainBlock = parentBlock.innerBlocks.find((b) => b.name === currentBlock.name);
758
759 mainBlock.attributes["align"] = currentBlock?.attributes?.align;
760
761 if (mainBlock) {
762 const newBlock = createBlock(mainBlock.name, mainBlock.attributes, mainBlock.innerBlocks);
763 selectBlock(newBlock.clientId);
764 replaceBlocks([parentClientId], [newBlock]);
765 }
766 return;
767 }
768 }, [liveFilterEnable, paginationEnable, clientId]);
769 };
770
771 export function removeEmptyValues(obj) {
772 if (!obj) return;
773 return Object.entries(obj).reduce((acc, [key, value]) => {
774 if (!["", " ", "%", "undefined", "px"].includes(value)) {
775 acc[key] = value;
776 }
777 return acc;
778 }, {});
779 }
780
781 export const getPaginationBlock = (clientId) => {
782 const { selectBlock, replaceInnerBlocks } = useDispatch("core/block-editor");
783
784 const currentBlock = useSelect((select) => select("core/block-editor").getBlock(clientId), [clientId]);
785
786 const paginationBlock = currentBlock?.innerBlocks?.find((block) => block.name === "sp-smart-post-show/pagination");
787
788 return {
789 select: selectBlock,
790 block: paginationBlock,
791 };
792 };
793 export const getSimpleRadiusCss = (attr, deviceType = "Desktop") => {
794 if (!attr) {
795 return "";
796 }
797 if (attr.device && "object" === typeof attr.device) {
798 const { top, right, bottom, left } = attr.device?.[deviceType];
799 const unit = attr?.unit;
800 if (!top && !right && !bottom && !left) {
801 return "";
802 }
803 return `${top || 0}${unit} ${right || 0}${unit} ${bottom || 0}${unit} ${left || 0}${unit}`;
804 }
805 if (!attr.device) {
806 const { top, right, bottom, left } = attr;
807 const unit = attr?.unit;
808 if (!top && !right && !bottom && !left) {
809 return "";
810 }
811 return `${top || 0}${unit} ${right || 0}${unit} ${bottom || 0}${unit} ${left || 0}${unit}`;
812 }
813
814 return "";
815 };
816
817 export const ensureHttps = (url) => {
818 if (!url.startsWith("http://") && !url.startsWith("https://")) {
819 return "https://" + url;
820 }
821 return url;
822 };
823
824 export const getClipPathForShape = (shape) => {
825 switch (shape) {
826 case "backgroundShapeHexagon":
827 return "polygon(0% 25%, 50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%)";
828 case "backgroundShapeDiamond":
829 return "polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)";
830 case "backgroundShapeStarburst":
831 return "polygon( 100% 50%, 92% 58%, 98% 68%, 88% 72%, 92% 84%, 82% 82%, 80% 94%, 70% 88%,66% 100%, 58% 92%, 50% 100%,42% 92%,34% 100%,30% 88%,20% 94%, 18% 82%, 8% 84%, 12% 72%,2% 68%, 8% 58%, 0% 50%,8% 42%, 2% 32%,12% 28%,8% 16%, 18% 18%, 20% 6%,30% 12%,34% 0%, 42% 8%, 50% 0%,58% 8%, 66% 0%,70% 12%,80% 6%,82% 18%, 92% 16%, 88% 28%, 98% 32%, 92% 42%)";
832 default:
833 return "";
834 }
835 };
836
837 export const getFormatDate = (dateTimeFormat) => {
838 switch (dateTimeFormat) {
839 case "publish-date":
840 return "Oct 7, 2025";
841
842 case "time-only":
843 return "2:20 PM";
844
845 case "full-date":
846 return "October 7, 2025";
847
848 case "date-time-short":
849 return "Oct 7, 2025 2:20 PM";
850
851 case "date-time-long":
852 return "October 7, 2025 2:20 PM";
853
854 case "day-month-year":
855 return "7 Oct 2025";
856
857 case "day-month-year-time":
858 return "7 Oct 2025 2:20 PM";
859
860 case "time-ago":
861 return "6 hours ago";
862
863 case "wp-default-date":
864 return "December 4, 2025";
865
866 case "wp-default-datetime":
867 return "December 4, 2025 4:18 am";
868
869 default:
870 return "Oct 7, 2025";
871 }
872 };
873
874 export const getTimeAgo = (dateString) => {
875 const seconds = Math.floor((new Date() - new Date(dateString)) / 1000);
876
877 let interval = seconds / 31536000;
878 if (interval > 1) {
879 return Math.floor(interval) + " years ago";
880 }
881
882 interval = seconds / 2592000;
883 if (interval > 1) {
884 return Math.floor(interval) + " months ago";
885 }
886
887 interval = seconds / 86400;
888 if (interval > 1) {
889 return Math.floor(interval) + " days ago";
890 }
891
892 interval = seconds / 3600;
893 if (interval > 1) {
894 return Math.floor(interval) + " hours ago";
895 }
896
897 interval = seconds / 60;
898 if (interval > 1) {
899 return Math.floor(interval) + " minutes ago";
900 }
901
902 return "Just now";
903 };
904
905 export const bgImageGradientStyle = (
906 className,
907 attr,
908 hover = false,
909 hoverClass = "",
910 customOpacity = 1,
911 ) => {
912 // 1. Setup selectors
913 const hoverSelector = hoverClass || `${className}:hover`;
914
915 // 2. Get the correct color settings
916 const settings = hover ? attr?.hover : attr?.color;
917 const background = colorControls(
918 settings?.style,
919 settings?.solidColor,
920 settings?.gradient
921 );
922
923 // 3. Logic for Normal State (Applied directly to the class)
924 if ( settings?.style === "bgColor" ) {
925 const updateClassName = hover ? hoverSelector : className;
926 return [
927 {
928 class: `${updateClassName}`,
929 styles: {
930 //position: "relative", // Required for ::after to position correctly
931 background,
932 transition: "all 0.3s ease-in-out",
933 },
934 },
935 ]
936 }
937
938 // 4. Logic for Hover State (Applied to ::after)
939 return !hover ? [
940 {
941 class: `${className}`,
942 styles: {
943 //position: "relative", // Required for ::after to position correctly
944 background: background,
945 opacity: customOpacity,
946 "z-index": 1,
947 overflow: "hidden", // Ensures overlay doesn't spill out
948 },
949 },
950 ] : [
951 {
952 class: `${className}::after`,
953 styles: {
954 content: "''",
955 position: "absolute",
956 top: 0,
957 left: 0,
958 width: "100%",
959 height: "100%",
960 background: background,
961 opacity: 0, // Hidden by default
962 transition: "opacity 0.3s ease-in-out",
963 "z-index": 2,
964 "pointer-events": "none",
965 },
966 },
967 {
968 class: `${hoverSelector}::after`,
969 styles: {
970 opacity: customOpacity, // Fades in to 0.6 on hover
971 },
972 },
973 ];
974 };
975
976 export const priceLink = "https://wpsmartpost.com/pricing/?ref=1";
977
978 export const ProTopBar = ({blockName, title}) => {
979 return (
980 <div className="sp-smart-pro-block-top-bar">
981 <span>Unlock this premium <strong>{title}</strong> block with Pro</span>
982 <a className="sp-smart-pro-block-demo-btn" style={{marginLeft: "auto"}} rel="noreferrer" target="_blank" href={blockPreviewPanelLink[blockName]}>View Demo</a>
983 <a className="sp-upgrade-to-pro-btn" rel="noreferrer" target="_blank" href={priceLink}>Upgrade Pro</a>
984 </div>
985 )
986 }