diff --git a/core/src/client/ProductNavigation/app.tsx b/core/src/client/ProductNavigation/app.tsx index 4f1cd00b110..056d953982b 100644 --- a/core/src/client/ProductNavigation/app.tsx +++ b/core/src/client/ProductNavigation/app.tsx @@ -4,11 +4,14 @@ * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 */ import React from 'react'; -import { createRoot } from 'react-dom/client'; +import { createRoot, Root } from 'react-dom/client'; import { App } from '@labkey/api'; import { AppContext, ProductNavigation } from './ProductNavigation'; +let root: Root; + App.registerApp('productNavigation', (target, ctx) => { - createRoot(document.getElementById(target)).render(); + if (!root) root = createRoot(document.getElementById(target)); + root.render(); }); diff --git a/core/src/org/labkey/core/view/template/bootstrap/header.jsp b/core/src/org/labkey/core/view/template/bootstrap/header.jsp index c43d52c28f5..a3c2fd3bd0c 100644 --- a/core/src/org/labkey/core/view/template/bootstrap/header.jsp +++ b/core/src/org/labkey/core/view/template/bootstrap/header.jsp @@ -214,7 +214,9 @@ <% diff --git a/core/webapp/core/ProductNavigationHeader.js b/core/webapp/core/ProductNavigationHeader.js index 06f67b458e8..f9e4b326f2c 100644 --- a/core/webapp/core/ProductNavigationHeader.js +++ b/core/webapp/core/ProductNavigationHeader.js @@ -5,38 +5,44 @@ */ (function ($) { - var HEADER_ID_PREFIX = 'headerProductDropdown'; - var HEADER_CONTENT_ID = HEADER_ID_PREFIX + '-content'; + const HEADER_ID_PREFIX = 'headerProductDropdown'; + const HEADER_CONTENT_ID = HEADER_ID_PREFIX + '-content'; + let scriptRequested = false; + let scriptLoaded = false; + let menuOpen = false; - // wait to load the product navigation dependencies until the hover over the header icon - var productNavLoaded = false; - $(document).on('mouseenter', '#' + HEADER_ID_PREFIX + ' .dropdown-toggle', function(e) { - if (!productNavLoaded) { - $("#" + HEADER_CONTENT_ID).html("
Loading...
"); + function showProductMenu(show) { + LABKEY.App.loadApp('productNavigation', HEADER_CONTENT_ID, { show }); + } - LABKEY.requiresScript('gen/productNavigation', loadProductNav); - // LABKEY.requiresScript('http://localhost:3001/productNavigation.js', loadProductNav); - } else { - loadProductNav(); - } + function onLoad() { + scriptLoaded = true; + if (menuOpen) showProductMenu(true); + } + + function requireProductNav() { + if (scriptRequested) return; + scriptRequested = true; + LABKEY.requiresScript('gen/productNavigation', onLoad); + // LABKEY.requiresScript('http://localhost:3001/productNavigation.js', onLoad); + } + + // preload on hover/focus so the menu is usually ready by the time it opens + $(document).on('mouseenter focusin', '#' + HEADER_ID_PREFIX + ' .dropdown-toggle', requireProductNav); + + $(document).on('show.bs.dropdown', '#' + HEADER_ID_PREFIX, function () { + menuOpen = true; + requireProductNav(); + if (scriptLoaded) showProductMenu(true); }); - var loadProductNav = function() { - LABKEY.App.loadApp('productNavigation', HEADER_CONTENT_ID, { show: true }); - $(document).on('click', addProductNavClickHandler); - productNavLoaded = true; - }; + $(document).on('hidden.bs.dropdown', '#' + HEADER_ID_PREFIX, function () { + menuOpen = false; + if (scriptLoaded) showProductMenu(false); + }); // stop the product navigation menu from closing when click within the menu div $(document).on('click', '#' + HEADER_CONTENT_ID, function (e) { e.stopPropagation(); }); - - // on click outside of the open menu, remove click handler and hide menu (which will force it to reset on next open) - var addProductNavClickHandler = function (e) { - if ($(e.target).closest(HEADER_CONTENT_ID).length === 0) { - LABKEY.App.loadApp('productNavigation', HEADER_CONTENT_ID, { show: false }); - $(document).off('click', addProductNavClickHandler); - } - }; })(jQuery);