From 74b1b04973ec1e4520b5377bacde62da5ec19264 Mon Sep 17 00:00:00 2001 From: Jared Khan Date: Wed, 13 Nov 2024 11:44:27 +0000 Subject: [PATCH 1/4] Add recent places to search dropdown --- OZprivate/rawJS/OZTreeModule/src/OZui.js | 4 +- OZprivate/rawJS/OZTreeModule/src/ui/search.js | 174 ++++++++++++++++-- OZprivate/scss/search_ui.scss | 8 + languages/en-us.py | 2 + languages/fr.py | 4 +- languages/sv.py | 2 + languages/zh-cn.py | 4 +- views/layout.html | 1 + views/treeviewer/UI_layer.load | 2 + views/treeviewer/js_strings.json | 2 + views/treeviewer/layout.html | 9 +- views/uikit_layout.html | 3 + 12 files changed, 191 insertions(+), 24 deletions(-) diff --git a/OZprivate/rawJS/OZTreeModule/src/OZui.js b/OZprivate/rawJS/OZTreeModule/src/OZui.js index dbb29ddd..5781e0ba 100644 --- a/OZprivate/rawJS/OZTreeModule/src/OZui.js +++ b/OZprivate/rawJS/OZTreeModule/src/OZui.js @@ -1,9 +1,9 @@ import search_manager from './ui/search_manager'; -import { searchPopulate, setup_location_list } from './ui/search.js'; +import { searchPopulate, setup_location_list, setup_recents_list, add_element_to_recents_list } from './ui/search.js'; import { fullLeafBase, fullLeaf, natural_theme } from './ui/leaf_draw.js'; import { sortList, teaseTour } from './ui/tours_list.js'; -export { search_manager, searchPopulate, setup_location_list }; +export { search_manager, searchPopulate, setup_location_list, setup_recents_list, add_element_to_recents_list }; export const leaf_draw = { fullLeafBase: fullLeafBase, diff --git a/OZprivate/rawJS/OZTreeModule/src/ui/search.js b/OZprivate/rawJS/OZTreeModule/src/ui/search.js index 6c22098a..1a33c617 100644 --- a/OZprivate/rawJS/OZTreeModule/src/ui/search.js +++ b/OZprivate/rawJS/OZTreeModule/src/ui/search.js @@ -62,6 +62,7 @@ function searchPopulate(searchbox, original_search, search_result) { var dropdown = $(".search_dropdown", searchbox); if (window.is_testing) { console.log("Hiding popular species section of dropdown"); } $('.popular_species', dropdown).hide(); + $('.recents', dropdown).hide(); $('.search_hits', dropdown).empty(); if (search_result.tree.length + search_result.tour.length == 0) { $('.no_results', dropdown).show(); @@ -131,26 +132,161 @@ function setup_location_list(target, locations_json) { if (typeof taxon === "string") { return $('
').text(OZstrings.hasOwnProperty(taxon) ? OZstrings[taxon] : taxon); } - return $('
') - // Attach compile_searchbox_data()-esque results to reconstitute on advanced_search_box click - .attr("data-vernacular", taxon.vernacular) - .attr("data-sciname", taxon.sciname) - .attr("data-pinpoint", '@=' + taxon.ott) - .html($('

').html($('') - .attr('href', taxon.href) - .attr("draggable","true") - .on('dragstart', function(event) { - // Recreate a compile_searchbox_data() format - event.originalEvent.dataTransfer.setData('result', JSON.stringify({ - 0: taxon.vernacular, - 1: taxon.sciname, - 2: taxon.ozid, - "pinpoint": '@=' + taxon.ott, - })); - }) - .html(taxon.vernacular ? $('').text(taxon.vernacular) : $('').text(taxon.sciname) ))); + return create_location_dd_element(taxon.vernacular, taxon.sciname, '@=' + taxon.ott, taxon.href, taxon.ozid); })); }); } -export { searchPopulate, setup_location_list }; +function create_location_dd_element(vernacular, sciname, pinpoint, href, ozid) { + return $('

') + // Attach compile_searchbox_data()-esque results to reconstitute on advanced_search_box click + .attr("data-vernacular", vernacular) + .attr("data-sciname", sciname) + .attr("data-pinpoint", pinpoint) + .html( + $('

').html( + $('') + .attr('href', href) + .attr("draggable","true") + .on('dragstart', function(event) { + // Recreate a compile_searchbox_data() format + event.originalEvent.dataTransfer.setData('result', JSON.stringify({ + 0: vernacular, + 1: sciname, + 2: ozid, + "pinpoint": pinpoint, + })); + }) + .html(vernacular ? $('').text(vernacular) : $('').text(sciname) ) + ) + ); +} + + + + +const MAX_RECENT_PLACES = 5; + +/** + * @typedef {Object} SearchResult + * @property {string|undefined} vernacular + * @property {string|undefined} sciname + * @property {string} pinpoint + * @property {string} href + */ + +/** + * @returns {SearchResult|null} + */ +function get_search_result_from_element(element) { + if ($(element).attr('data-pinpoint')) { + return { + // Recreate a compile_searchbox_data() format + "vernacular": $(element).attr('data-vernacular'), + "sciname": $(element).attr('data-sciname'), + "pinpoint": $(element).attr('data-pinpoint'), + "href": $(element).find('a').attr('href'), + }; + } + return null; +} + +/** + * Recently used places from the search menu. + * @type {SearchResult[]} + * */ +let recent_places = (() => { + try { + const stored = JSON.parse(localStorage.getItem('recent_places') || '[]'); + if (!Array.isArray(stored)) return []; + + const valid = stored.every(item => + item && + typeof item === 'object' && + typeof item.pinpoint === 'string' && + typeof item.href === 'string' && + (item.vernacular == undefined || typeof item.vernacular === 'string') && + (item.sciname == undefined || typeof item.sciname === 'string') + ); + + return valid ? stored : []; + } catch { + return []; + } +})(); + +/** + * Push a recent place to the front of the list, removing duplicates and limiting the size. + * @param {SearchResult} recent_search_result + * */ +function push_recent_place(recent_search_result) { + recent_places = recent_places.filter(function (place) { + return place.pinpoint !== recent_search_result.pinpoint; + }); + const newLength = recent_places.unshift(recent_search_result); + if (newLength > MAX_RECENT_PLACES) { + recent_places.pop(); + } + localStorage.setItem('recent_places', JSON.stringify(recent_places)); +} + +/** + * Push a recent search result to the front of the list using its containing dd element. + * */ +function add_element_to_recents_list(element) { + const search_result = get_search_result_from_element(element); + if (search_result) { + push_recent_place(search_result); + } +} + +function make_clear_recents_button() { + return $('