diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json index aae72ab3a..5cadb805f 100644 --- a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json @@ -586,6 +586,7 @@ "editor": { "title": "Manage palette", "palette": "Palette", + "allCatalogs": "All Catalogs", "times": { "seconds": "seconds ago", "minutes": "minutes ago", diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js index 34d3ba160..24f9c1909 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js @@ -16,15 +16,17 @@ RED.palette.editor = (function() { var disabled = false; - + let catalogues = [] + const loadedCatalogs = [] var editorTabs; - var filterInput; - var searchInput; - var nodeList; - var packageList; - var loadedList = []; - var filteredList = []; - var loadedIndex = {}; + let filterInput; + let searchInput; + let nodeList; + let packageList; + let fullList = [] + let loadedList = []; + let filteredList = []; + let loadedIndex = {}; var typesInUse = {}; var nodeEntries = {}; @@ -162,7 +164,6 @@ RED.palette.editor = (function() { } } - function getContrastingBorder(rgbColor){ var parts = /^rgba?\(\s*(\d+),\s*(\d+),\s*(\d+)[,)]/.exec(rgbColor); if (parts) { @@ -369,10 +370,10 @@ RED.palette.editor = (function() { var activeSort = sortModulesRelevance; function handleCatalogResponse(err,catalog,index,v) { + const url = catalog.url catalogueLoadStatus.push(err||v); if (!err) { if (v.modules) { - var a = false; v.modules = v.modules.filter(function(m) { if (RED.utils.checkModuleAllowed(m.id,m.version,installAllowList,installDenyList)) { loadedIndex[m.id] = m; @@ -389,13 +390,14 @@ RED.palette.editor = (function() { m.timestamp = 0; } m.index = m.index.join(",").toLowerCase(); + m.catalog = catalog; + m.catalogIndex = index; return true; } return false; }) loadedList = loadedList.concat(v.modules); } - searchInput.searchBox('count',loadedList.length); } else { catalogueLoadErrors = true; } @@ -404,7 +406,7 @@ RED.palette.editor = (function() { } if (catalogueLoadStatus.length === catalogueCount) { if (catalogueLoadErrors) { - RED.notify(RED._('palette.editor.errors.catalogLoadFailed',{url: catalog}),"error",false,8000); + RED.notify(RED._('palette.editor.errors.catalogLoadFailed',{url: url}),"error",false,8000); } var delta = 250-(Date.now() - catalogueLoadStart); setTimeout(function() { @@ -416,12 +418,13 @@ RED.palette.editor = (function() { function initInstallTab() { if (loadedList.length === 0) { + fullList = []; loadedList = []; loadedIndex = {}; packageList.editableList('empty'); $(".red-ui-palette-module-shade-status").text(RED._('palette.editor.loading')); - var catalogues = RED.settings.theme('palette.catalogues')||['https://catalogue.nodered.org/catalogue.json']; + catalogueLoadStatus = []; catalogueLoadErrors = false; catalogueCount = catalogues.length; @@ -431,23 +434,92 @@ RED.palette.editor = (function() { $("#red-ui-palette-module-install-shade").show(); catalogueLoadStart = Date.now(); var handled = 0; - catalogues.forEach(function(catalog,index) { - $.getJSON(catalog, {_: new Date().getTime()},function(v) { - handleCatalogResponse(null,catalog,index,v); + loadedCatalogs.length = 0; // clear the loadedCatalogs array + for (let index = 0; index < catalogues.length; index++) { + const url = catalogues[index]; + $.getJSON(url, {_: new Date().getTime()},function(v) { + loadedCatalogs.push({ index: index, url: url, name: v.name, updated_at: v.updated_at, modules_count: (v.modules || []).length }) + handleCatalogResponse(null,{ url: url, name: v.name},index,v); refreshNodeModuleList(); }).fail(function(jqxhr, textStatus, error) { - console.warn("Error loading catalog",catalog,":",error); - handleCatalogResponse(jqxhr,catalog,index); + console.warn("Error loading catalog",url,":",error); + handleCatalogResponse(jqxhr,url,index); }).always(function() { handled++; if (handled === catalogueCount) { - searchInput.searchBox('change'); + //sort loadedCatalogs by e.index ascending + loadedCatalogs.sort((a, b) => a.index - b.index) + updateCatalogFilter(loadedCatalogs) } }) - }); + } } } + /** + * Refreshes the catalog filter dropdown and updates local variables + * @param {[{url:String, name:String, updated_at:String, modules_count:Number}]} catalogEntries + */ + function updateCatalogFilter(catalogEntries, maxRetry = 3) { + // clean up existing filters + const catalogSelection = $('#red-catalogue-filter-select') + if (catalogSelection.length === 0) { + // sidebar not yet loaded (red-catalogue-filter-select is not in dom) + if (maxRetry > 0) { + // console.log("updateCatalogFilter: sidebar not yet loaded, retrying in 100ms") + // try again in 100ms + setTimeout(() => { + updateCatalogFilter(catalogEntries, maxRetry - 1) + }, 100); + return; + } + return; // give up + } + catalogSelection.off("change") // remove any existing event handlers + catalogSelection.attr('disabled', 'disabled') + catalogSelection.empty() + catalogSelection.append($('`) + } + // select the 1st option in the select list + catalogSelection.val(catalogSelection.find('option:first').val()) + + // if there is only 1 catalog, hide the select + if (catalogEntries.length > 1) { + catalogSelection.prepend(``) + catalogSelection.removeAttr('disabled') // permit the user to select a catalog + } + // refresh the searchInput counter and trigger a change + filterByCatalog(catalogSelection.val()) + searchInput.searchBox('change'); + + // hook up the change event handler + catalogSelection.on("change", function() { + const selectedCatalog = $(this).val(); + filterByCatalog(selectedCatalog); + searchInput.searchBox('change'); + }) + } + + function filterByCatalog(selectedCatalog) { + if (loadedCatalogs.length <= 1 || selectedCatalog === "all") { + loadedList = fullList.slice(); + } else { + loadedList = fullList.filter(function(m) { + return (m.catalog.name === selectedCatalog); + }) + } + refreshFilteredItems(); + searchInput.searchBox('count',filteredList.length+" / "+loadedList.length); + } + function refreshFilteredItems() { packageList.editableList('empty'); var currentFilter = searchInput.searchBox('value').trim(); @@ -462,7 +534,6 @@ RED.palette.editor = (function() { if (filteredList.length === 0) { packageList.editableList('addItem',{}); } - if (filteredList.length > 10) { packageList.editableList('addItem',{start:10,more:filteredList.length-10}) } @@ -492,6 +563,7 @@ RED.palette.editor = (function() { var updateDenyList = []; function init() { + catalogues = RED.settings.theme('palette.catalogues')||['https://catalogue.nodered.org/catalogue.json'] if (RED.settings.get('externalModules.palette.allowInstall', true) === false) { return; } @@ -669,7 +741,8 @@ RED.palette.editor = (function() { }); - nodeList = $('