started fresh, much better now

This commit is contained in:
EthanHealy01 2025-07-11 01:34:30 +01:00
parent d17d10b240
commit 8bcdb4cf9e
5 changed files with 228 additions and 48 deletions

View File

@ -290,6 +290,21 @@ span.icon-text::after {
color: var(--md-sys-color-on-surface-variant);
}
.nav-link {
display: flex;
align-items: center;
max-width: 95vw;
}
.chevron-icon {
margin-left: auto;
transition: transform 0.3s ease;
}
[aria-expanded="true"] > .chevron-icon {
transform: rotate(180deg);
}
.nav-item {
position: relative;
}
@ -484,27 +499,88 @@ html[dir="rtl"] .dropdown-menu {
display: inline-flex;
}
@media (min-width:992px) {
.dropdown:hover .dropdown-menu {
display: block;
margin-top: 0;
@media (max-width:1199.98px) {
.navbar-collapse .dropdown-menu {
width: 100%;
}
/* .icon-hide {
display: none;
} */
}
@media (max-width:1199px) {
.icon-hide {
display: inline-flex;
.navbar-collapse .dropdown-menu-wrapper {
width: 100%;
box-sizing: border-box;
}
.navbar-collapse .dropdown-mw-28 {
min-width: 0;
}
}
@media (min-width:1200px) {
/* This CSS-based hover is disabled because it conflicts with Bootstrap's JavaScript.
Hover functionality is now handled in navbar.js and search.js */
/*
.dropdown:hover .dropdown-menu {
display: block;
margin-top: 0;
}
*/
.icon-hide {
display: none;
}
.chevron-icon {
display: none !important;
}
}
@media (max-width: 1199.98px) {
.navbar-collapse .dropdown-menu {
width: 100vw !important;
max-width: 100vw !important;
left: 0 !important;
}
.navbar-collapse .dropdown-mega .dropdown-menu {
max-height: 60vh;
overflow-y: auto;
}
.navbar-collapse .dropdown-mega .dropdown-menu-wrapper {
border-radius: 0;
}
#favoritesDropdown,
#languageDropdown + .dropdown-menu .dropdown-menu-wrapper,
#searchDropdown + .dropdown-menu .dropdown-menu-wrapper {
padding: 1.5rem 1rem;
}
#favoritesDropdown, #languageSelection, #searchResults {
width: 100%;
box-sizing: border-box;
}
#languageSelection {
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
.navbar-collapse .dropdown-menu-wrapper {
width: 95vw;
box-sizing: border-box;
margin-left: 0 !important;
padding: 0 !important;
}
.navbar-collapse .dropdown-mw-28 {
min-width: 0;
}
.icon-hide {
display: inline-flex;
}
.navbar-collapse .dropdown-item {
margin-left: 0 !important;
}
.container {
margin-left: 0px !important;
padding-left: 4px !important;
}
}
.go-pro-link {
@ -558,6 +634,12 @@ html[dir="rtl"] .dropdown-menu {
box-sizing: border-box;
}
@media (max-width: 768px) {
.feature-group {
min-width: 10rem;
}
}
.feature-rows {
display: flex;
flex-wrap: wrap;

View File

@ -42,6 +42,58 @@ function toolsManager() {
});
}
function setupDropdownHovers() {
const dropdowns = document.querySelectorAll('.navbar-nav > .nav-item.dropdown');
dropdowns.forEach(dropdown => {
const toggle = dropdown.querySelector('[data-bs-toggle="dropdown"]');
if (!toggle) return;
// Skip search dropdown, it has its own logic
if (toggle.id === 'searchDropdown') {
return;
}
let timeout;
const instance = bootstrap.Dropdown.getOrCreateInstance(toggle);
dropdown.addEventListener('mouseenter', () => {
if (window.innerWidth >= 1200) {
clearTimeout(timeout);
if (!instance._isShown()) {
instance.show();
}
}
});
dropdown.addEventListener('mouseleave', () => {
if (window.innerWidth >= 1200) {
timeout = setTimeout(() => {
if (instance._isShown()) {
instance.hide();
}
}, 200);
}
});
toggle.addEventListener('click', (e) => {
if (window.innerWidth >= 1200) {
// On desktop, prevent Bootstrap's default click toggle
e.preventDefault();
e.stopPropagation();
// Still allow navigation if it's a link
const href = toggle.getAttribute('href');
if (href && href !== '#') {
window.location.href = href;
}
}
// On mobile (< 1200px), this listener does nothing, allowing default click behavior.
});
});
}
window.tooltipSetup = () => {
const tooltipElements = document.querySelectorAll('[title]');
@ -56,23 +108,27 @@ window.tooltipSetup = () => {
document.body.appendChild(customTooltip);
element.addEventListener('mouseenter', (event) => {
if (window.innerWidth >= 1200) {
customTooltip.style.display = 'block';
customTooltip.style.left = `${event.pageX + 10}px`; // Position tooltip slightly away from the cursor
customTooltip.style.top = `${event.pageY + 10}px`;
});
// Update the position of the tooltip as the user moves the mouse
element.addEventListener('mousemove', (event) => {
customTooltip.style.left = `${event.pageX + 10}px`;
customTooltip.style.top = `${event.pageY + 10}px`;
}
});
element.addEventListener('mousemove', (event) => {
if (window.innerWidth >= 1200) {
customTooltip.style.left = `${event.pageX + 10}px`;
customTooltip.style.top = `${event.pageY + 10}px`;
}
});
// Hide the tooltip when the mouse leaves
element.addEventListener('mouseleave', () => {
customTooltip.style.display = 'none';
});
});
};
document.addEventListener('DOMContentLoaded', () => {
tooltipSetup();
setupDropdownHovers();
});

View File

@ -79,33 +79,67 @@ const searchDropdown = document.getElementById('searchDropdown');
const searchInput = document.getElementById('navbarSearchInput');
const dropdownMenu = searchDropdown.querySelector('.dropdown-menu');
// Handle dropdown shown event
searchDropdown.addEventListener('shown.bs.dropdown', function () {
searchInput.focus();
});
// Create a single dropdown instance
const dropdownInstance = new bootstrap.Dropdown(searchDropdown);
// Handle hover opening
searchDropdown.addEventListener('mouseenter', function () {
const dropdownInstance = new bootstrap.Dropdown(searchDropdown);
// Function to handle showing the dropdown
function showSearchDropdown() {
if (!dropdownInstance._isShown()) {
dropdownInstance.show();
}
setTimeout(() => searchInput.focus(), 150); // Focus after animation
}
setTimeout(() => {
searchInput.focus();
}, 100);
// Handle click for mobile
searchDropdown.addEventListener('click', function (e) {
if (window.innerWidth < 1200) {
// Let Bootstrap's default toggling handle it, but ensure focus
if (!dropdownInstance._isShown()) {
// Use a small delay to allow the dropdown to open before focusing
setTimeout(() => searchInput.focus(), 150);
}
} else {
// On desktop, hover opens the dropdown, so a click shouldn't toggle it.
e.preventDefault();
}
});
// Handle mouse leave
searchDropdown.addEventListener('mouseleave', function () {
// Check if current value is empty (including if user typed and then deleted)
if (searchInput.value.trim().length === 0) {
searchInput.blur();
const dropdownInstance = new bootstrap.Dropdown(searchDropdown);
// Handle hover for desktop
searchDropdown.addEventListener('mouseenter', function () {
if (window.innerWidth >= 1200) {
showSearchDropdown();
}
});
// Handle mouse leave for desktop
searchDropdown.addEventListener('mouseleave', function (e) {
if (window.innerWidth >= 1200) {
// A short delay to allow moving mouse from button to menu
setTimeout(() => {
const dropdownMenu = searchDropdown.querySelector('.dropdown-menu');
if (!dropdownMenu) return;
// Check if either the button or the menu is still hovered
const isHoveringButton = searchDropdown.matches(':hover');
const isHoveringMenu = dropdownMenu.matches(':hover');
if (!isHoveringButton && !isHoveringMenu && searchInput.value.trim().length === 0) {
dropdownInstance.hide();
}
});
searchDropdown.addEventListener('hidden.bs.dropdown', function () {
if (searchInput.value.trim().length === 0) {
searchInput.blur();
}, 200);
}
});
// Hide dropdown if it's open and user clicks outside
document.addEventListener('click', function(e) {
if (!searchDropdown.contains(e.target) && dropdownInstance._isShown()) {
if (searchInput.value.trim().length === 0) {
dropdownInstance.hide();
}
}
});
// Keep dropdown open if search input is clicked
searchInput.addEventListener('click', function (e) {
e.stopPropagation();
});

View File

@ -31,6 +31,10 @@
const isHighDPI = systemDPR > 1.4;
function scaleNav() {
if (window.innerWidth < 1200) {
// Don't scale nav on mobile
return;
}
const currentDPR = window.devicePixelRatio || 1;
const browserZoom = currentDPR / systemDPR;

View File

@ -45,9 +45,10 @@
apps
</span>
<span class="icon-text" th:data-text="#{navbar.allTools}" th:text="#{navbar.allTools}"></span>
<span class="material-symbols-rounded chevron-icon">expand_more</span>
</a>
<div class="dropdown-menu dropdown-menu-tp" aria-labelledby="navbarDropdown-1">
<div class="dropdown-menu-wrapper" style="justify-content: center; display:flex">
<div class="dropdown-menu-wrapper" style="max-width: 95vw !important;">
<div class="feature-rows">
<th:block th:insert="~{fragments/navElements.html :: navElements}"></th:block>
@ -117,9 +118,10 @@
star
</span>
<span class="icon-text icon-hide" th:data-text="#{navbar.favorite}" th:text="#{navbar.favorite}"></span>
<span class="material-symbols-rounded chevron-icon">expand_more</span>
</a>
<div class="dropdown-menu dropdown-menu-tp dropdown-mw-28" aria-labelledby="navbarDropdown-5">
<div class="dropdown-menu-wrapper px-xl-2 px-2" id="favoritesDropdown">
<div class="dropdown-menu dropdown-menu-tp dropdown-mw-28" role="menu" aria-labelledby="navbarDropdown-5">
<div class="dropdown-menu-wrapper px-xl-2 px-2" id="favoritesDropdown" style="max-width: 95vw !important; ">
<!-- Dropdown items will be added here by JavaScript -->
</div>
</div>
@ -140,9 +142,10 @@
language
</span>
<span class="icon-text icon-hide" th:data-text="#{navbar.language}" th:text="#{navbar.language}"></span>
<span class="material-symbols-rounded chevron-icon">expand_more</span>
</a>
<div class="dropdown-menu dropdown-menu-tp" aria-labelledby="languageDropdown">
<div class="dropdown-menu-wrapper px-xl-2 px-2">
<div class="dropdown-menu-wrapper px-xl-2 px-2" style="max-width: 95vw !important;">
<div id="languageSelection" class="scrollable-y lang_dropdown-mw scalable-languages-container">
<th:block th:insert="~{fragments/languages :: langs}"></th:block>
</div>
@ -157,9 +160,10 @@
search
</span>
<span class="icon-text icon-hide">Search</span>
<span class="material-symbols-rounded chevron-icon">expand_more</span>
</a>
<div class="dropdown-menu dropdown-menu-tp" aria-labelledby="searchDropdown">
<div class="dropdown-menu-wrapper px-xl-2 px-2">
<div class="dropdown-menu-wrapper px-xl-2 px-2" style="max-width: 95vw !important;">
<form th:action="@{''}" class="d-flex p-2 search-form" id="searchForm">
<input class="form-control search-input" type="search" th:placeholder="#{navbar.search}"
aria-label="Search" id="navbarSearchInput">
@ -172,7 +176,7 @@
<li class="nav-item" th:if="${!@runningProOrHigher}">
<a href="https://stirlingpdf.com/pricing" class="nav-link go-pro-link" target="_blank"
rel="noopener noreferrer">
rel="noopener noreferrer" aria-label="Upgrade to PRO" title="Upgrade to PRO">
<span class="go-pro-badge" th:text="#{enterpriseEdition.button}"></span>
</a>
</li>