PluginProbe
Smart Post – Post Grid, Post Carousel, Post Slider Gutenberg Blocks for Blog & News / 4.0.8
Smart Post – Post Grid, Post Carousel, Post Slider Gutenberg Blocks for Blog & News v4.0.8
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 / blocks / assets / js / smart-search.js

smart-search.js in Smart Post – Post Grid, Post Carousel, Post Slider Gutenberg Blocks for Blog & News 4.0.8, at blocks/assets/js/smart-search.js

249 lines 7.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 document.addEventListener("DOMContentLoaded", function () {
2 const searchContainers = document.querySelectorAll(
3 ".sp-smart-post-search-wrapper"
4 );
5 function debounce(fn, delay) {
6 let timer;
7 return function (...args) {
8 clearTimeout(timer);
9 timer = setTimeout(() => fn.apply(this, args), delay);
10 };
11 }
12 const safeJSONParse = (str, fallback = {}) => {
13 try {
14 return JSON.parse(str) || fallback;
15 } catch (e) {
16 console.log("Failed to parse JSON:", str, e);
17 return fallback;
18 }
19 };
20 const loadQueryData = (state) => {
21 return fetch(sp_smart_search_block_localize.searchRestUrl, {
22 method: "POST",
23 headers: { "Content-Type": "application/json" },
24 body: JSON.stringify(state),
25 }).then((res) => res.json());
26 };
27 function highlightSearchTerm(container, term) {
28 if (!term) {
29 return;
30 }
31 const regex = new RegExp(
32 "(" + term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + ")",
33 "gi"
34 );
35
36 const walker = document.createTreeWalker(
37 container,
38 NodeFilter.SHOW_TEXT,
39 null,
40 false
41 );
42 const nodes = [];
43 while (walker.nextNode()) {
44 nodes.push(walker.currentNode);
45 }
46
47 nodes?.forEach(function (node) {
48 const parent = node.parentNode;
49 if (parent && parent.nodeName !== "MARK") {
50 const newHTML = node.nodeValue.replace(
51 regex,
52 '<mark class="sp-smart-post-search-highlight">$1</mark>'
53 );
54 if (newHTML !== node.nodeValue) {
55 const span = document.createElement("span");
56 span.innerHTML = newHTML;
57 parent.replaceChild(span, node);
58 }
59 }
60 });
61 }
62 const eventHandler = async (e, container, query) => {
63 e.preventDefault();
64 const target = e.target;
65 const searchResults = container.querySelector(
66 ".sp-smart-post-search-results"
67 );
68 const searchInput = target.closest(".sp-smart-post-search-input");
69 const taxonomyField = container.querySelector(
70 ".sp-smart-post-search-cate-list"
71 );
72
73 if (taxonomyField) {
74 const termId = taxonomyField.value;
75 query.term = termId;
76 }
77 if (searchInput) {
78 const searchTerm = searchInput.value.trim();
79 if (searchTerm.length > 0) {
80 searchResults.classList.add(
81 "sp-smart-post-search-results--show"
82 );
83 query.s = searchTerm;
84 } else {
85 searchResults.classList.remove(
86 "sp-smart-post-search-results--show"
87 );
88 return;
89 }
90 }
91
92 loadQueryData(query).then((data) => {
93 const resultList = container.querySelector(
94 ".sp-smart-post-search-list"
95 );
96 if (resultList) {
97 resultList.innerHTML = data.html;
98 if (query.s && query?.hightLightSearchTerm) {
99 highlightSearchTerm(resultList, query.s.trim());
100 }
101 }
102 const loadMore = container.querySelector(
103 ".sp-smart-post-search-load-more"
104 );
105 if (loadMore) {
106 const showCount = query?.showResultsCount || true;
107 const viewMoreText =
108 loadMore.dataset.view_more_text ?? "View More";
109 if (data.remaining_posts > 0) {
110 if (query?.moreResultClickAction === "expanded") {
111 loadMore.innerHTML = `<button type="button" class="sp-smart-post-search-load-more-button"> ${viewMoreText}${ showCount ? (" " + data.remaining_posts + " + Posts") : ""}</button>`;
112 const loadMoreBtn = loadMore.querySelector(
113 ".sp-smart-post-search-load-more-button"
114 );
115 loadMoreBtn?.addEventListener("click", () => {
116 query.loadMore = true;
117 eventHandler(e, container, query);
118 });
119 } else {
120 loadMore.innerHTML = `<a href="/?s=${query.s}" ${query?.moreResultClickAction === "new_tab" ? `target="_blank"` : ""} class="sp-smart-post-search-load-more-button">
121 ${viewMoreText}${ showCount ? (" " + data.remaining_posts + " + Posts") : ""} </a>`;
122 }
123 } else {
124 loadMore.innerHTML = "";
125 }
126 }
127 });
128 };
129 searchContainers?.forEach((container) => {
130 const searchForm = container.querySelector(
131 ".sp-smart-post-search-form"
132 );
133
134 let searchSettings = searchForm?.dataset?.search_settings ?? "{}";
135 searchSettings = safeJSONParse(searchSettings, {});
136 const blockWrapper = container.closest(".sp-smart-post-wrapper");
137 if (blockWrapper) {
138 searchSettings.block = blockWrapper.id;
139 if (searchSettings?.displayType === "popup") {
140 const searchTrigger = blockWrapper.querySelector(
141 "button.sp-smart-post-search-trigger-button"
142 );
143
144 if (searchTrigger) {
145 searchTrigger.addEventListener("click", () => {
146 container.classList.toggle(
147 "sp-smart-post-search-popup--active"
148 );
149 const searchOverlay = blockWrapper.querySelector(
150 ".sp-smart-post-search-overlay"
151 );
152 if(searchOverlay) {
153 searchOverlay.classList.toggle("sp-d-block");
154
155 searchOverlay.addEventListener("click", () => {
156 container.classList.remove(
157 "sp-smart-post-search-popup--active"
158 );
159 searchOverlay.classList.remove("sp-d-block");
160 });
161 }
162 // Close button functionality
163 const popupCloseBtn = container.querySelector(
164 ".sp-smart-post-popup-close-icon"
165 );
166 if ( popupCloseBtn ) {
167 popupCloseBtn.addEventListener( "click", () => {
168 container.classList.remove(
169 "sp-smart-post-search-popup--active"
170 );
171 if(searchOverlay) {
172 searchOverlay.classList.remove("sp-d-block");
173 }
174 });
175 }
176
177 });
178 }
179
180 }
181 }
182
183 if (searchSettings?.isAjaxSearch) {
184 const filter_fields =
185 container?.querySelectorAll(
186 ".sp-smart-post-search-form select[name], .sp-smart-post-search-form input[name]"
187 ) || [];
188 filter_fields?.forEach((field) => {
189 const isInput = field.tagName === "INPUT";
190 const eventName = isInput ? "input" : "change";
191
192 // Add event listener.
193 field?.addEventListener(
194 eventName,
195 isInput
196 ? debounce(
197 (e) =>
198 eventHandler(e, container, searchSettings),
199 400
200 )
201 : (e) => eventHandler(e, container, searchSettings)
202 );
203 });
204 } else {
205 searchForm?.addEventListener("submit", (event) => {
206 event.preventDefault();
207 });
208 }
209 const resultDiv = container.querySelector(".sp-smart-post-search-results");
210 container.addEventListener(
211 "blur",
212 function (event) {
213 // If focus moved *outside* the wrapper
214 if (!container.contains(event.relatedTarget) && resultDiv ) {
215 resultDiv.classList.remove("sp-smart-post-search-results--show"); // or your hide logic
216 }
217 },
218 true // IMPORTANT! useCapture to detect blur properly
219 );
220 });
221 });
222
223 document.addEventListener( "DOMContentLoaded", () => {
224 // const popupSearchIcon = document.querySelectorAll(".sp-smart-post-wrapper .sp-smart-post-search-trigger");
225 const elements = document.querySelectorAll(".sp-smart-post-search-wrapper.sp-smart-post-search-popup");
226 if ( elements.length < 1 ) return;
227
228 function adjustPosition() {
229 elements.forEach( el => {
230 const rect = el.getBoundingClientRect();
231
232 if ( (rect.width + rect.left) < window.innerWidth) {
233 el.classList.remove("sp-popup-position-right");
234 el.classList.add("sp-popup-position-left");
235 }
236 // If element goes past the right edge
237 else if ( (rect.width + rect.left) > window.innerWidth) {
238 el.classList.remove("sp-popup-position-left");
239 el.classList.add("sp-popup-position-right");
240 }
241 })
242 }
243 // Call when you show/move the element
244 adjustPosition();
245
246 // Optional: also check on window resize
247 window.addEventListener("resize", adjustPosition);
248 })
249