PluginProbe
Smart Post – Post Grid, Post Carousel, Post Slider Gutenberg Blocks for Blog & News / 4.0.4
Smart Post – Post Grid, Post Carousel, Post Slider Gutenberg Blocks for Blog & News v4.0.4
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 / controls / controls.js

controls.js in Smart Post – Post Grid, Post Carousel, Post Slider Gutenberg Blocks for Blog & News 4.0.4, at src/controls/controls.js

730 lines 20.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { useSelect, select } from "@wordpress/data";
2 import { useEffect, useRef, useState } from "@wordpress/element";
3 import axios from "axios";
4 import { colorControls, convertToClassName } from "../blocks/shared/helpFn";
5
6 // Device type fn.
7 export const useDeviceType = () => {
8 const { deviceType } = useSelect((select) => {
9 const coreEditor = select("core/editor");
10
11 // Old WP (5.9) → fallback
12 if (!coreEditor?.getDeviceType) {
13 return { deviceType: "Desktop" };
14 }
15
16 // New WP (6.2+) → use core API
17 return { deviceType: coreEditor.getDeviceType() };
18 }, []);
19 return deviceType || "Desktop";
20 };
21
22 export const cssString = (css) => {
23 let result = "";
24 for (const selector in css) {
25 let cssProps = "";
26 for (const property in css[selector]) {
27 if (css[selector][property] && css[selector][property].length > 0) {
28 cssProps += property + ":" + css[selector][property] + ";";
29 }
30 }
31 result += "" !== cssProps ? selector + "{" + cssProps + "}" : "";
32 }
33 return result;
34 };
35
36 // this function create for capitalize any strings first word.
37 export const capitalizeString = (name) => {
38 if (name === "post_format") {
39 return "Post_Format";
40 }
41 return name.charAt(0).toUpperCase() + name.slice(1);
42 };
43
44 // this function accept an random array, one label key and value key and return a array of objects that use for select fields options.
45 export const filterSelectOptions = (dataArray, labelKey, valueKey, indexKey = false) => {
46 const selectFieldData = dataArray?.map((data, index) => {
47 return {
48 id: indexKey ? data[indexKey] : index,
49 label: data[labelKey],
50 value:
51 "number" === typeof data[valueKey] ? data[valueKey] : data[valueKey].replace(/\s+/g, "_").toLowerCase(),
52 };
53 });
54 return selectFieldData;
55 };
56
57 // Calculate time ago (Human Readable) for metadata.
58 export const humanReadableTimeAgo = (postDate) => {
59 const dateNow = new Date();
60 const secondsPast = Math.floor((dateNow.getTime() - parseInt(postDate.getTime())) / 1000);
61
62 if (secondsPast < 60) {
63 return `${secondsPast} seconds ago`;
64 }
65 if (secondsPast < 3600) {
66 const minutes = Math.floor(secondsPast / 60);
67 return `${minutes} minutes ago`;
68 }
69 if (secondsPast < 86400) {
70 const hours = Math.floor(secondsPast / 3600);
71 return `${hours} hours ago`;
72 }
73 if (secondsPast < 604800) {
74 // 30 days
75 const days = Math.floor(secondsPast / 86400);
76 return `${days} days ago`;
77 }
78 if (secondsPast < 2592000) {
79 // 30 days
80 const weeks = Math.floor(secondsPast / 604800);
81 return `${weeks} weeks ago`;
82 }
83 if (secondsPast < 31536000) {
84 // 365 days
85 const months = Math.floor(secondsPast / 2592000);
86 return `${months} months ago`;
87 }
88 const years = Math.floor(secondsPast / 31536000);
89 return `${years} years ago`;
90 };
91
92 // Strip Gutenberg Comments for metadata reading time.
93 export const stripGutenbergComments = (content) => {
94 // Remove gutenberg comments.
95 content = content.replace(/<!-- wp:.*? -->/g, "");
96 content = content.replace(/<!-- \/wp:.*? -->/g, "");
97
98 // Remove HTML Tag.
99 content = content.replace(/<\/?[^>]+(>|$)/g, "");
100
101 return content.trim();
102 };
103
104 // Count total word and character of the content.
105 export const countWordAndCharacter = (content) => {
106 const text = stripGutenbergComments(content).trim();
107 const characterCount = text.length;
108 const wordCount = text.split(/\s+/).filter(Boolean).length;
109 return {
110 words: wordCount,
111 chars: characterCount,
112 };
113 };
114
115 export const filterDndSelectValues = (values) => {
116 const updatedValues = Array.isArray(values) && values?.map((val) => val.value);
117 return updatedValues;
118 };
119
120 export const filteredTaxonomiesValues = (taxonomies) => {
121 const filteredTaxonomy = taxonomies?.map((taxonomy) => {
122 return { ...taxonomy, value: filterDndSelectValues(taxonomy?.value) };
123 });
124 return filteredTaxonomy;
125 };
126
127 export const findDataFromArray = (itemsArray, firstKey, secondKey) => {
128 const result = itemsArray?.find((f) => f[firstKey] === secondKey);
129 return result;
130 };
131
132 export const unit = (attributes, deviceType) => {
133 if ("object" !== typeof attributes.unit) {
134 return attributes.unit || "";
135 }
136 return attributes.unit?.[deviceType];
137 };
138
139 export const filterActivatedAjaxLiveFilter = (liveFilters) => {
140 return liveFilters.filter(
141 (liveFilter) => liveFilter?.ajaxLiveOderDirect === true && liveFilter.value !== "keywordSearch"
142 );
143 };
144
145 export const getMediaUrlFromContent = (tags, content) => {
146 const parser = new DOMParser();
147 const doc = parser.parseFromString(content, "text/html");
148 const contentImages = doc.querySelectorAll("img");
149
150 if (tags === "img_gallery_in_post_content" && contentImages.length > 0) {
151 return Array.from(contentImages)?.map((el) => ({ source_url: el.src }));
152 } else if (Array.isArray(tags)) {
153 const values = tags.map((tag) => {
154 const element = doc.querySelector(tag);
155 return element ? { type: tag, url: element.src } : null;
156 });
157
158 const filterValues = values.filter((val) => val);
159 return filterValues.length > 0 ? filterValues[0] : null;
160 }
161
162 return null;
163 };
164
165 export const debounce = (func, delay) => {
166 let timeoutId;
167 return (...args) => {
168 if (timeoutId) {
169 clearTimeout(timeoutId);
170 }
171 timeoutId = setTimeout(() => {
172 func(...args);
173 }, delay);
174 };
175 };
176
177 export const getMediaUrl = (attachment, attributes) => {
178 const { attachment_metadata, content, attachment_url } = attachment;
179 const {
180 imageReplaceWith,
181 imageFallbackReplace,
182 toggleCustomFallbackBg,
183 imageReplaceWithImage,
184 imageReplaceWithVideo,
185 post_thumbnail_url,
186 imageGallerySource,
187 } = attributes;
188
189 const sourceImages =
190 "img_gallery_in_post_content" === imageGallerySource
191 ? getMediaUrlFromContent("img_gallery_in_post_content", content)
192 : [];
193
194 let metadataImgUrl = "";
195 let mediaFromContent = {};
196 if (post_thumbnail_url) {
197 metadataImgUrl = post_thumbnail_url;
198 } else if (!attachment_metadata && imageFallbackReplace === "source") {
199 const replaceWith = imageReplaceWith?.length > 0 ? imageReplaceWith : ["img", "video", "audio"];
200 mediaFromContent = getMediaUrlFromContent(replaceWith, content);
201 sourceImages?.shift();
202 } else if (!attachment_metadata && imageFallbackReplace === "custom") {
203 const items = {
204 img: {
205 type: "img",
206 url: imageReplaceWithImage?.url,
207 },
208 video: {
209 type: "video",
210 url: imageReplaceWithVideo?.url,
211 },
212 };
213 mediaFromContent = items[toggleCustomFallbackBg];
214 } else if (!metadataImgUrl && attachment_url) {
215 metadataImgUrl = attachment_url;
216 }
217 return { mediaUrl: metadataImgUrl, mediaFromContent, sourceImages };
218 };
219
220 export const uniqueIdToClientId = (uniqueId, blockName) => {
221 const removeSlice = `sp-smart-${blockName}-`;
222 return uniqueId?.replace(removeSlice, "");
223 };
224
225 export const getPaginationUniqueId = (uniqueId, blockName) => {
226 const clientId = uniqueIdToClientId(uniqueId, blockName);
227 const paginationUniqueId = `sp-smart-post-show-pagination-${clientId}`;
228 return paginationUniqueId;
229 };
230
231 const accordionStore = {
232 get: () => localStorage.getItem("sp-opened-accordion"),
233 set: (e) => localStorage.setItem("sp-opened-accordion", e),
234 };
235
236 export const manageOpenAccordion = () => {
237 const [openedAccordion, setOpenAccordion] = useState(accordionStore.get());
238
239 const togglePanelBody = (val) => {
240 if (openedAccordion === val) {
241 setOpenAccordion("");
242 accordionStore.set("");
243 } else {
244 setOpenAccordion(val);
245 accordionStore.set(val);
246 }
247 };
248
249 return { togglePanelBody, openedAccordion };
250 };
251
252 export const useHandleScroll = () => {
253 const [height, setHeight] = useState();
254 const [heightUnit, setHeightUnit] = useState("px");
255 const ref = useRef(null);
256 let newHeight = 0;
257
258 const handleScroll = (e) => {
259 e.defaultPrevent;
260 const scrollPosition = window.scrollY + 200;
261 const elementPosition = ref.current?.getBoundingClientRect().top + window.scrollY;
262
263 if (ref.current?.getBoundingClientRect().bottom - 518 < 0) {
264 setHeightUnit("%");
265 setHeight(100);
266 } else {
267 setHeightUnit("px");
268 }
269
270 if (scrollPosition > elementPosition && ref.current?.getBoundingClientRect().bottom - 518 > 0) {
271 newHeight = scrollPosition + 320 - elementPosition;
272 setHeight(newHeight);
273 }
274 };
275 window.addEventListener("scroll", handleScroll);
276 return { height, heightUnit, ref };
277 };
278
279 export const queryFn = async (data) => {
280 const response = await axios.post(sp_smart_post_block_localize.ajaxUrl, data);
281 return response.data;
282 };
283
284 ///
285 // Create css link for responsive device,
286 export const createCssLink = (Id, linkId, href) => {
287 // Tablet / Mobile Starts.
288 const tabletPreview = document.getElementsByClassName("is-tablet-preview");
289 const mobilePreview = document.getElementsByClassName("is-mobile-preview");
290 const canvas = document.getElementsByClassName("edit-site-visual-editor__editor-canvas");
291
292 if (0 !== tabletPreview.length || 0 !== mobilePreview.length || 0 !== canvas.length) {
293 const preview = tabletPreview[0] || mobilePreview[0] || canvas[0];
294
295 let iframe = false;
296
297 if (preview) {
298 iframe = preview.getElementsByTagName("iframe")[0];
299 if (0 !== canvas.length) {
300 iframe = preview;
301 }
302 }
303
304 const IframeDocument = iframe?.contentWindow.document || iframe?.contentDocument;
305
306 linkId = IframeDocument.getElementById(linkId);
307
308 if (null === linkId || undefined === linkId) {
309 const $link = document.createElement("link");
310 $link.setAttribute("id", Id);
311 $link.setAttribute("href", href);
312 $link.setAttribute("media", "all");
313 $link.setAttribute("rel", "stylesheet");
314
315 IframeDocument.head.appendChild($link);
316 }
317 }
318 };
319
320 // Create js link for responsive device,
321 export const createJsLink = (Id, linkId, href) => {
322 // Tablet / Mobile Starts.
323 const tabletPreview = document.getElementsByClassName("is-tablet-preview");
324 const mobilePreview = document.getElementsByClassName("is-mobile-preview");
325 const canvas = document.getElementsByClassName("edit-site-visual-editor__editor-canvas");
326
327 if (0 !== tabletPreview.length || 0 !== mobilePreview.length || 0 !== canvas.length) {
328 const preview = tabletPreview[0] || mobilePreview[0] || canvas[0];
329
330 let iframe = false;
331
332 if (preview) {
333 iframe = preview.getElementsByTagName("iframe")[0];
334 if (0 !== canvas.length) {
335 iframe = preview;
336 }
337 }
338
339 const IframeDocument = iframe?.contentWindow.document || iframe?.contentDocument;
340
341 linkId = IframeDocument.getElementById(linkId);
342
343 if (null === linkId || undefined === linkId) {
344 const $link = document.createElement("script");
345 $link.setAttribute("id", Id);
346 $link.setAttribute("src", href);
347 $link.setAttribute("type", "text/javascript");
348
349 IframeDocument.body.appendChild($link);
350 }
351 }
352 };
353 // path edit fn.
354 const shortUniqueId = (uniqueId) => {
355 return uniqueId.slice(uniqueId.length - 8, uniqueId.length);
356 };
357
358 export const spSplit = (data, splitBy) => {
359 return data?.split([splitBy]);
360 };
361
362 export const inArray = (array, value) => {
363 return array.includes(value);
364 };
365
366 export const useSPLocation = (id) => {
367 const { search } = window.location;
368 const liveFilterArray = ["author", "order_by", "order"];
369 let queries = {};
370 let keywordSearchFromUrl = "";
371 let queriesArray = [];
372
373 if (!search || !inArray(search, shortUniqueId(id))) {
374 return { queries, keywordSearchFromUrl };
375 }
376 const searchArrayFromUrl = spSplit(search, "&&");
377 const keywordSearch = spSplit(searchArrayFromUrl[searchArrayFromUrl.length - 1], "=");
378 queriesArray = spSplit(spSplit(searchArrayFromUrl[1], "+")[1], "&");
379 keywordSearchFromUrl = keywordSearch[0] === "search" ? decodeURIComponent(keywordSearch[1]) : "";
380
381 queriesArray?.forEach((query) => {
382 const queryKeyName = spSplit(query, "=")[0];
383 const queryValueName = spSplit(query, "=")[1];
384 const categoryName = inArray(liveFilterArray, queryKeyName) ? undefined : queryKeyName;
385 const type = inArray(liveFilterArray, queryKeyName) ? queryKeyName : "taxonomy";
386 const label = categoryName ? categoryName : type;
387 const id =
388 inArray(["order", "order_by"], type) || queryValueName === "all"
389 ? queryValueName
390 : parseInt(queryValueName);
391
392 queries = {
393 ...queries,
394 [label]: { id, type, taxonomy_type: categoryName },
395 };
396 });
397 return { queries, keywordSearchFromUrl };
398 };
399
400 export const setSPCustomPath = ({ uniqueId, queries, keywordSearch }) => {
401 const id = shortUniqueId(uniqueId);
402 const allQueries = Object.values(queries)?.filter((query) => query.id !== "all");
403
404 const filter = allQueries?.reduce((acc, { id, type, taxonomy_type }, index) => {
405 return `${acc}${index === 0 ? "" : "&"}${taxonomy_type ? taxonomy_type : type}=${id}`;
406 }, "");
407
408 const query = `?sps=${id}${filter.length > 0 ? `&&filter+${filter}` : ""}${
409 keywordSearch.length > 0 ? `&&search=${keywordSearch}` : ""
410 }`;
411 const baseUrl = window.location.pathname;
412 const url = filter.length > 0 || keywordSearch.length > 0 ? query : baseUrl;
413 history.pushState({}, "", url);
414 };
415
416 export const breakpoint = () => {
417 if (typeof select("core/edit-site") !== "undefined" || typeof select("core/editor") !== "undefined") {
418 return useDeviceType();
419 }
420
421 let breakpoints = {
422 "(min-width: 1024px)": "Desktop",
423 "(min-width: 600px) and (max-width: 1023.98px)": "Tablet",
424 "(min-width: 0px) and (max-width: 599.98px)": "Mobile",
425 };
426
427 for (let media in breakpoints) {
428 if (window.matchMedia(media).matches) {
429 return breakpoints[media];
430 }
431 }
432
433 return "Desktop";
434 };
435
436 export const paginationDotType = (swiperDotsRef, type, uniqueId = "") => {
437 const carousel_id = uniqueId ? `#${uniqueId}` : "";
438 const paginationDotStyle = {
439 dots: {
440 el: swiperDotsRef?.current || `${carousel_id} .sp-pagination-horizontal`,
441 clickable: true,
442 },
443 strokes: {
444 el: swiperDotsRef?.current || `${carousel_id} .sp-pagination-horizontal`,
445 clickable: true,
446 },
447 dynamic: {
448 el: swiperDotsRef?.current || `${carousel_id} .sp-pagination-horizontal`,
449 dynamicBullets: true,
450 clickable: true,
451 },
452 fraction: {
453 el: swiperDotsRef?.current || `${carousel_id} .sp-pagination-horizontal`,
454 type: "fraction",
455 },
456 numbers: {
457 el: swiperDotsRef?.current || `${carousel_id} .sp-pagination-horizontal`,
458 clickable: true,
459 renderBullet: function (index, className) {
460 return '<span class="' + className + '">' + (index + 1) + "</span>";
461 },
462 paginationType: "number",
463 },
464 scrollbar: false,
465 };
466
467 return paginationDotStyle[type];
468 };
469
470 export const maxValueFromObject = (objName) => {
471 const newArray = Object.values(objName);
472 const arrayOfNumbers = newArray.map((value) => (value === "" ? 0 : Number(value)));
473 return Math.max(...arrayOfNumbers);
474 };
475
476 export const swiperPaddingForBoxShadow = (normalBool, normalObj, hoverBool, hoverObj) => {
477 const normalValue = normalBool ? maxValueFromObject(normalObj) || 0 : 0;
478 const hoverValue = hoverBool ? maxValueFromObject(hoverObj) || 0 : 0;
479
480 const maxValue = Math.max(normalValue, hoverValue);
481
482 return `0 ${maxValue}px ${maxValue}px`;
483 };
484
485 export const classNameDeviceType = (
486 property,
487 pValue,
488 deviceType,
489 currentScreen = "Desktop",
490 page = "editor",
491 important = false,
492 extraClass = false,
493 customUnit = ""
494 ) => {
495 const devicePrefix = {
496 Desktop: "",
497 Tablet: "md",
498 Mobile: "sm",
499 };
500 const classNamePrefix = {
501 "font-size": "font",
502 "letter-spacing": "ls",
503 "line-height": "lh",
504 height: "h",
505 width: "w",
506 "column-gap": "c-gap",
507 gap: "gap",
508 top: "vertical",
509 left: "horizontal",
510 };
511
512 const deviceValue = pValue?.device?.[deviceType];
513 const classNameValue = typeof deviceValue === "object" && side ? deviceValue?.[side] : deviceValue;
514
515 if (classNameValue == null || !classNamePrefix[property]) {
516 console.warn(`Invalid inputs: property=${property}, deviceType=${deviceType}, pValue=`, pValue);
517 return null;
518 }
519
520 const unit = customUnit ? customUnit : pValue?.unit?.[deviceType] || "";
521 const endTag = extraClass && extraClass === "meta-svg" ? " svg" : "";
522 const importantText =
523 page === "editor" ? (deviceType.includes(currentScreen) ? " !important" : "") : important ? " !important" : "";
524 const finalValue = `${classNameValue}${unit}${importantText}`;
525 const suffix = devicePrefix[deviceType];
526
527 return ![undefined, null].includes(classNameValue)
528 ? {
529 class: `.sp-${extraClass ? `${extraClass}-` : ""}${classNamePrefix[property]}${
530 suffix ? `-${suffix}` : ""
531 }-${convertToClassName(classNameValue.toString() + unit)}${endTag}`,
532 property,
533 value: finalValue,
534 }
535 : {};
536 };
537
538 export const buildFontClasses = (property, value, extraClass = false, customUnit = "") => {
539 const deviceSuffixes = {
540 Desktop: "",
541 Mobile: "sm",
542 Tablet: "md",
543 };
544
545 // Abbreviations for CSS properties
546 const propertyAbbreviations = {
547 "font-size": "font",
548 "letter-spacing": "ls",
549 "line-height": "lh",
550 height: "h",
551 width: "w",
552 "column-gap": "c-gap",
553 gap: "gap",
554 top: "vertical",
555 left: "horizontal",
556 };
557
558 const shortProperty = propertyAbbreviations[property] || property;
559
560 return Object.keys(deviceSuffixes).reduce((classes, device) => {
561 const suffix = deviceSuffixes[device];
562
563 // Ensure 0 is a valid value by explicitly checking for undefined or null
564 if (value?.device?.[device] !== "" && (value?.unit?.[device] !== "" || customUnit)) {
565 const className = `sp${extraClass ? `-${extraClass}` : ""}-${shortProperty}${
566 suffix ? `-${suffix}` : ""
567 }-${convertToClassName(value.device?.[device] + (customUnit ? customUnit : value?.unit?.[device]))}`;
568 classes[className] = true;
569 }
570 return classes;
571 }, {});
572 };
573
574 export const isObjectEmpty = (obj) => {
575 if (!obj || typeof obj !== "object") return false;
576 return Object.values(obj).every((value) => (typeof value === "object" ? isObjectEmpty(value) : value === ""));
577 };
578
579 export const isEditor = () => {
580 // return typeof select( 'core/editor' ) !== 'undefined' ? true : false;
581 return true;
582 };
583
584 // Google fonts list controls fn.
585 export const googleFonts = (fonts, page = "edit") => {
586 if (!fonts?.length) {
587 return [];
588 }
589
590 let returnFont = "";
591
592 if (page !== "edit") {
593 // Frontend fonts array (unique + cleaned).
594 const fontList = fonts
595 .map((font) =>
596 font?.typography?.family?.length > 0 ? `${font.typography.family}:${font.typography.fontWeight}` : ""
597 )
598 .filter(Boolean); // remove empty strings
599
600 // Return unique values only
601 returnFont = [...new Set(fontList)];
602 } else {
603 // Edit page (Google Fonts import)
604 const fontList = fonts
605 .map((font) =>
606 font?.typography?.family?.length > 0
607 ? `family=${font.typography.family.replaceAll(" ", "+")}:wght@${font.typography.fontWeight}&`
608 : ""
609 )
610 .filter(Boolean);
611
612 // Deduplicate imports
613 const uniqueFonts = [...new Set(fontList)];
614
615 const updatedUniqueFonts =
616 uniqueFonts.length > 0 &&
617 uniqueFonts?.filter((item) => {
618 const [name] = item.split(":");
619 return name.trim() !== "";
620 });
621
622 returnFont =
623 updatedUniqueFonts.length > 0
624 ? `@import url('https://fonts.googleapis.com/css2?${updatedUniqueFonts.join("")}display=swap');`
625 : "";
626 }
627 return returnFont;
628 };
629
630 // Show/hide on device type (Desktop, Tablet, Mobile)
631 export const showHide = (deviceCss, id, hide, defaultVal = "") => {
632 return [
633 ...deviceCss,
634 {
635 class: `#${id}`,
636 styles: {
637 display: hide ? "none" : defaultVal,
638 },
639 },
640 ];
641 };
642
643 export const backgroundStyle = (attributes, bgImageObj = {}, hover = false, condition = true) => {
644 if (!attributes && !condition) return {};
645
646 if (attributes?.color?.style === "video") {
647 return {};
648 }
649 if ((!hover && attributes?.color?.style === "image") || (hover && attributes?.hover?.style === "image")) {
650 return {
651 "background-image": Object.keys(bgImageObj).length !== 0 ? `url(${bgImageObj?.url})` : "",
652 "background-position": "center",
653 "background-attachment": "scroll",
654 "background-repeat": "no-repeat",
655 "background-size": "cover",
656 };
657 }
658 // Handle hover style
659 const styleSource = hover ? attributes?.hover : attributes?.color;
660
661 return {
662 background: colorControls(styleSource?.style, styleSource?.solidColor, styleSource?.gradient),
663 };
664 };
665
666 export const gradientHoverStyle = (selector, bgAttr, imgObj = "", alt = "") => {
667 if (!bgAttr) return;
668
669 const normalBGStyle = [
670 {
671 class: `${selector}:hover`,
672 styles: {
673 ...(backgroundStyle(bgAttr, imgObj, true) || backgroundStyle(bgAttr, alt, true)),
674 },
675 },
676 ];
677 const gradientBGStyle = [
678 {
679 class: `${selector}::before`,
680 styles: {
681 content: '""',
682 display: "block",
683 position: "absolute",
684 width: "100%",
685 height: "100%",
686 top: "0",
687 left: "0",
688 "z-index": "1",
689 opacity: "0",
690 ...backgroundStyle(bgAttr, "", true),
691 transition: "opacity 0.3s ease-in-out",
692 },
693 },
694 {
695 class: `${selector}`,
696 styles: {
697 position: "relative",
698 },
699 },
700 {
701 class: `${selector}:hover::before`,
702 styles: {
703 opacity: "1",
704 },
705 },
706 ];
707 return bgAttr?.hover?.style !== "gradient" ? normalBGStyle : gradientBGStyle;
708 };
709
710 export const capitalizeWords = (str) =>
711 str
712 .trim()
713 .toLowerCase()
714 .split(" ")
715 .map((word) => {
716 const i = ["(", "-", "/", "{", "[", "_"].includes(word.charAt(0)) ? 1 : 0;
717 return word.slice(0, i) + word.charAt(i).toUpperCase() + word.slice(i + 1);
718 })
719 .join(" ");
720
721 export const typographyCss = (attr) => {
722 return {
723 "font-family": attr?.typography?.family,
724 "font-weight": attr?.typography?.fontWeight,
725 "font-style": attr?.typography?.style || "normal",
726 "text-decoration": attr?.typography?.decoration || "none",
727 "text-transform": attr?.typography?.transform,
728 };
729 };
730