diff --git a/apps/core/context_processors.py b/apps/core/context_processors.py index 2b45915..8139b35 100644 --- a/apps/core/context_processors.py +++ b/apps/core/context_processors.py @@ -1,4 +1,4 @@ -from apps.products.models import MainProduct +from apps.products.models import MainProduct, SubProduct def navigation(request): @@ -7,4 +7,13 @@ def navigation(request): .prefetch_related("sub_products") .order_by("order", "name") ) - return {"nav_main_products": main_products} + all_sub_products = list( + SubProduct.objects.filter(is_active=True).order_by("order", "name")[:5] + ) + footer_sub_products = all_sub_products[:4] + footer_sub_products_has_more = len(all_sub_products) == 5 + return { + "nav_main_products": main_products, + "footer_sub_products": footer_sub_products, + "footer_sub_products_has_more": footer_sub_products_has_more, + } diff --git a/static/css/main.css b/static/css/main.css index 3e06581..a92cfb9 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -1263,7 +1263,7 @@ a.citation-count-badge:hover { .article-citation-item { display: flex; - align-items: baseline; + align-items: flex-start; gap: 0.5rem; font-size: 0.8rem; color: var(--text-secondary); @@ -1284,16 +1284,64 @@ a.citation-count-badge:hover { flex: 1; } -.citation-link { +.citation-actions { flex-shrink: 0; + display: flex; + flex-direction: column; + align-items: center; + gap: 0.65rem; +} + +.citation-link, +.citation-copy { color: var(--accent-blue-light); opacity: 0.75; transition: opacity 0.2s; display: flex; align-items: center; + justify-content: center; } -.citation-link:hover { +.citation-copy { + position: relative; + padding: 0; + border: none; + background: none; + cursor: pointer; + font: inherit; +} + +.citation-link:hover, +.citation-copy:hover { + opacity: 1; +} + +.citation-copy-feedback { + position: absolute; + left: 50%; + top: calc(100% + 0.2rem); + transform: translateX(-50%); + font-size: 0.62rem; + font-weight: 600; + letter-spacing: 0.02em; + color: var(--accent-blue-light); + white-space: nowrap; + pointer-events: none; +} + +.citation-copy-icon-done { + display: none; +} + +.citation-copy.is-copied .citation-copy-icon { + display: none; +} + +.citation-copy.is-copied .citation-copy-icon-done { + display: flex; +} + +.citation-copy.is-copied { opacity: 1; } diff --git a/static/js/main.js b/static/js/main.js index 4c75aaf..84427c7 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -9,6 +9,7 @@ const SELECTORS = { faqQuestion: '.faq-question', faqAnswer: '.faq-answer', fadeInElements: '.fade-in', + citationCopy: '.citation-copy', }; function initNavbarScroll() { @@ -190,6 +191,47 @@ function initReadMore() { }); } +function initCitationCopy() { + const copyButtons = document.querySelectorAll(SELECTORS.citationCopy); + if (!copyButtons.length) return; + + copyButtons.forEach((btn) => { + const url = btn.getAttribute('data-citation-url'); + const feedback = btn.querySelector('.citation-copy-feedback'); + if (!url || !feedback) return; + + let resetTimer = null; + + btn.addEventListener('click', async () => { + clearTimeout(resetTimer); + + try { + await navigator.clipboard.writeText(url); + } catch { + const textarea = document.createElement('textarea'); + textarea.value = url; + textarea.setAttribute('readonly', ''); + textarea.style.position = 'fixed'; + textarea.style.left = '-9999px'; + document.body.appendChild(textarea); + textarea.select(); + document.execCommand('copy'); + document.body.removeChild(textarea); + } + + feedback.hidden = false; + btn.classList.add('is-copied'); + btn.setAttribute('aria-label', 'Link copied'); + + resetTimer = setTimeout(() => { + feedback.hidden = true; + btn.classList.remove('is-copied'); + btn.setAttribute('aria-label', 'Copy citation link'); + }, 2000); + }); + }); +} + function init() { initNavbarScroll(); initMobileMenu(); @@ -197,6 +239,7 @@ function init() { initFAQAccordion(); initIntersectionObserver(); initReadMore(); + initCitationCopy(); } if (document.readyState === 'loading') { diff --git a/templates/partials/_footer.html b/templates/partials/_footer.html index 93ab2dc..816b7d0 100644 --- a/templates/partials/_footer.html +++ b/templates/partials/_footer.html @@ -34,13 +34,16 @@ - {% if nav_main_products %} + {% if footer_sub_products %} {% endif %} diff --git a/templates/products/sub_detail.html b/templates/products/sub_detail.html index 3b87c96..3918afe 100644 --- a/templates/products/sub_detail.html +++ b/templates/products/sub_detail.html @@ -177,11 +177,23 @@
  • {{ citation.text }} {% if citation.url %} - - - + + + + + + {% endif %}
  • {% endfor %}