erpnext.ProductSearch = class { constructor(opts) { /* Options: search_box_id (for custom search box) */ $.extend(this, opts); this.MAX_RECENT_SEARCHES = 4; this.search_box_id = this.search_box_id || "#search-box"; this.searchBox = $(this.search_box_id); this.setupSearchDropDown(); this.bindSearchAction(); } setupSearchDropDown() { this.search_area = $("#dropdownMenuSearch"); this.setupSearchResultContainer(); this.populateRecentSearches(); } bindSearchAction() { let me = this; // Show Search dropdown this.searchBox.on("focus", () => { this.search_dropdown.removeClass("hidden"); }); // If click occurs outside search input/results, hide results. // Click can happen anywhere on the page $("body").on("click", (e) => { let searchEvent = $(e.target).closest(this.search_box_id).length; let resultsEvent = $(e.target).closest('#search-results-container').length; let isResultHidden = this.search_dropdown.hasClass("hidden"); if (!searchEvent && !resultsEvent && !isResultHidden) { this.search_dropdown.addClass("hidden"); } }); // Process search input this.searchBox.on("input", (e) => { let query = e.target.value; if (query.length == 0) { me.populateResults(null); me.populateCategoriesList(null); } if (query.length < 3 || !query.length) return; frappe.call({ method: "erpnext.templates.pages.product_search.search", args: { query: query }, callback: (data) => { let product_results = null, category_results = null; // Populate product results product_results = data.message ? data.message.product_results : null; me.populateResults(product_results); // Populate categories if (me.category_container) { category_results = data.message ? data.message.category_results : null; me.populateCategoriesList(category_results); } // Populate recent search chips only on successful queries if (!$.isEmptyObject(product_results) || !$.isEmptyObject(category_results)) { me.setRecentSearches(query); } } }); this.search_dropdown.removeClass("hidden"); }); } setupSearchResultContainer() { this.search_dropdown = this.search_area.append(` `).find("#search-results-container"); this.setupCategoryContainer(); this.setupProductsContainer(); this.setupRecentsContainer(); } setupProductsContainer() { this.products_container = this.search_dropdown.append(`
`).find("#product-scroll"); } setupCategoryContainer() { this.category_container = this.search_dropdown.append(`
`).find(".category-chips"); } setupRecentsContainer() { let $recents_section = this.search_dropdown.append(`
${ __("Recent") }
`).find(".recent-searches"); this.recents_container = $recents_section.append(`
`).find("#recents"); } getRecentSearches() { return JSON.parse(localStorage.getItem("recent_searches") || "[]"); } attachEventListenersToChips() { let me = this; const chips = $(".recent-search"); window.chips = chips; for (let chip of chips) { chip.addEventListener("click", () => { me.searchBox[0].value = chip.innerText.trim(); // Start search with `recent query` me.searchBox.trigger("input"); me.searchBox.focus(); }); } } setRecentSearches(query) { let recents = this.getRecentSearches(); if (recents.length >= this.MAX_RECENT_SEARCHES) { // Remove the `first` query recents.splice(0, 1); } if (recents.indexOf(query) >= 0) { return; } recents.push(query); localStorage.setItem("recent_searches", JSON.stringify(recents)); this.populateRecentSearches(); } populateRecentSearches() { let recents = this.getRecentSearches(); if (!recents.length) { this.recents_container.html(`No searches yet.`); return; } let html = ""; recents.forEach((key) => { html += ` `; }); this.recents_container.html(html); this.attachEventListenersToChips(); } populateResults(product_results) { if (!product_results || product_results.length === 0) { let empty_html = ``; this.products_container.html(empty_html); return; } let html = ""; product_results.forEach((res) => { let thumbnail = res.thumbnail || '/assets/erpnext/images/ui-states/cart-empty-state.png'; html += ` `; }); this.products_container.html(html); } populateCategoriesList(category_results) { if (!category_results || category_results.length === 0) { let empty_html = `
`; this.category_container.html(empty_html); return; } let html = `
${ __("Categories") }
`; category_results.forEach((category) => { html += ` ${ category.name } `; }); this.category_container.html(html); } };