diff --git a/component_catalog/templates/component_catalog/includes/hierarchy_instance_box.html b/component_catalog/templates/component_catalog/includes/hierarchy_instance_box.html index 984c5198..c5fce9dd 100644 --- a/component_catalog/templates/component_catalog/includes/hierarchy_instance_box.html +++ b/component_catalog/templates/component_catalog/includes/hierarchy_instance_box.html @@ -32,7 +32,7 @@ {% include 'component_catalog/includes/vulnerability_icon_link.html' with url=instance.get_absolute_url count=relation.vulnerability_count %} {% endif %} {% if relation.package_id and relation.package.declared_dependencies.all %} - {{ relation.package.declared_dependencies.all|length }} diff --git a/dejacode/static/css/dejacode_bootstrap.css b/dejacode/static/css/dejacode_bootstrap.css index f67badbb..b45e3019 100644 --- a/dejacode/static/css/dejacode_bootstrap.css +++ b/dejacode/static/css/dejacode_bootstrap.css @@ -141,6 +141,9 @@ table.text-break thead { background-color: var(--bs-djc-blue-bg); height: 54px; } +.nav { + --bs-nav-link-padding-x: 0.75rem; +} .navbar-nav .active>.nav-link, .navbar-nav .show>.nav-link .navbar-nav .nav-link.active, @@ -470,6 +473,11 @@ table.vulnerabilities-table .column-summary { font-size: 0.75rem; } +/* -- Licenses tab -- */ +#tab_licenses .column-usage_policy { + min-width: 175px; +} + /* -- Package Details -- */ textarea.licenseexpressionwidget { height: 62px; @@ -700,7 +708,17 @@ td.sub-header { .tab-content .table thead tr th { font-size: 0.875rem; } -th a.sort i {width: auto;} +th a.sort i { + width: auto; +} +tr.row-alert-danger > td:first-child { + box-shadow: inset 4px 0 0 var(--bs-danger); + padding-left: 1rem; +} +tr.row-alert-warning > td:first-child { + box-shadow: inset 4px 0 0 var(--bs-warning); + padding-left: 1rem; +} /* -- Better looks for the popover fake links -- */ .tag_popover, diff --git a/dejacode/static/js/dejacode_main.js b/dejacode/static/js/dejacode_main.js index 00cc51b2..71503214 100644 --- a/dejacode/static/js/dejacode_main.js +++ b/dejacode/static/js/dejacode_main.js @@ -239,6 +239,43 @@ function setupDismissibleAlerts() { }); } +function setupScrollToTargets() { + // Scroll to an in-page section when an element with data-scroll-to is clicked. + // The offset accounts for the sticky header via the body's padding-top. + document.addEventListener('click', (event) => { + const trigger = event.target.closest('[data-scroll-to]'); + if (!trigger) return; + const target = document.getElementById(trigger.dataset.scrollTo); + if (!target) return; + const offset = parseFloat(getComputedStyle(document.body).paddingTop) || 0; + const top = target.getBoundingClientRect().top + window.scrollY - offset; + window.scrollTo({ top, behavior: 'smooth' }); + }); +} + +function setupPaginationKeys() { + // Arrow key navigation for the page's own pagination. Pagination living inside + // a .tab-content is excluded: arrow keys there belong to the tab's own logic. + // Disabled links render as (no href), so querying skips them. + const isPageLevel = (link) => link && !link.closest('.tab-content'); + const previousLink = [...document.querySelectorAll('a.page-link[aria-label="Previous"]')].find(isPageLevel); + const nextLink = [...document.querySelectorAll('a.page-link[aria-label="Next"]')].find(isPageLevel); + if (!previousLink && !nextLink) return; + + const anyInputHasFocus = () => document.querySelector('input:focus, textarea:focus') !== null; + + document.addEventListener('keydown', (event) => { + if (anyInputHasFocus()) return; + if (event.key === 'ArrowLeft' && previousLink) { + event.preventDefault(); + window.location.href = previousLink.href; + } else if (event.key === 'ArrowRight' && nextLink) { + event.preventDefault(); + window.location.href = nextLink.href; + } + }); +} + document.addEventListener('DOMContentLoaded', () => { NEXB = {}; NEXB.client_data = JSON.parse(document.getElementById("client_data").textContent); @@ -277,4 +314,6 @@ document.addEventListener('DOMContentLoaded', () => { setupThemeSwitcher(); setupPlatformHints(); setupDismissibleAlerts(); + setupScrollToTargets(); + setupPaginationKeys(); }); diff --git a/dje/templates/dataspace_home.html b/dje/templates/dataspace_home.html index 465195d6..94025027 100644 --- a/dje/templates/dataspace_home.html +++ b/dje/templates/dataspace_home.html @@ -78,7 +78,7 @@

{{ card.title }}

{% if forloop.first %} {% endif %} diff --git a/dje/templates/hierarchy_base.js.html b/dje/templates/hierarchy_base.js.html index c74e979f..8f80be9b 100644 --- a/dje/templates/hierarchy_base.js.html +++ b/dje/templates/hierarchy_base.js.html @@ -43,10 +43,13 @@ // Draw if the hierarchy tab is active if (isTabActive(tabId)) jsPlumbHierarchy.setSuspendDrawing(false, true); - document.querySelector('button[data-bs-target="#tab_hierarchy"]').addEventListener('shown.bs.tab', function (e) { - // Second argument instructs jsPlumb to perform a full repaint. - jsPlumbHierarchy.setSuspendDrawing(false, true); - }); + const hierarchyTab = document.querySelector('button[data-bs-target="#tab_hierarchy"]'); + if (hierarchyTab) { + hierarchyTab.addEventListener('shown.bs.tab', function (e) { + // Second argument instructs jsPlumb to perform a full repaint. + jsPlumbHierarchy.setSuspendDrawing(false, true); + }); + } // Repaint on resizing the browser window if the related tab is active window.addEventListener('resize', function(){ diff --git a/dje/templates/object_details_base.html b/dje/templates/object_details_base.html index bf1fd9d7..ffaacff2 100644 --- a/dje/templates/object_details_base.html +++ b/dje/templates/object_details_base.html @@ -75,7 +75,7 @@

{% endblock %}