mirror of
https://github.com/kevinveenbirkenbach/homepage.veen.world.git
synced 2025-06-28 23:55:31 +02:00
Compare commits
5 Commits
0809272458
...
00e0096f8a
Author | SHA1 | Date | |
---|---|---|---|
00e0096f8a | |||
f017cacebe | |||
7c51ac6bbc | |||
573a3be360 | |||
1eb673454c |
@ -2,7 +2,7 @@
|
||||
|
||||
## Access
|
||||
### Locale
|
||||
http://127.0.0.1:5000
|
||||
[http://127.0.0.1:5000](http://127.0.0.1:5000)
|
||||
|
||||
|
||||
## Administrate Docker
|
||||
|
@ -17,16 +17,27 @@ accounts:
|
||||
url: https://microblog.veen.world/@kevinveenbirkenbach
|
||||
|
||||
- name: Pictures
|
||||
description: View my photo gallery
|
||||
icon:
|
||||
class: fa-solid fa-camera
|
||||
url: https://picture.veen.world/kevinveenbirkenbach
|
||||
class: fa-solid fa-images
|
||||
subitems:
|
||||
- name: Pixelfed
|
||||
description: View my photo gallery
|
||||
icon:
|
||||
class: fa-solid fa-camera
|
||||
url: https://s.veen.world/pictures
|
||||
- name: Instagram
|
||||
description: Follow me on Instagram
|
||||
icon:
|
||||
class: fa-brands fa-instagram
|
||||
url: https://www.instagram.com/kevinveenbirkenbach/
|
||||
identifier: kevinveenbirkenbach
|
||||
warning: Using software and platforms from the Meta corporation (e.g., Facebook, Instagram, WhatsApp) may compromise your data privacy and digital freedom due to centralized control, extensive data collection practices, and inconsistent moderation policies. These platforms often fail to adequately address harmful content, misinformation, and abuse.
|
||||
|
||||
- name: Videos
|
||||
description: Watch my videos
|
||||
icon:
|
||||
class: fa-solid fa-video
|
||||
url: https://video.veen.world/a/kevinveenbirkenbach
|
||||
url: https://s.veen.world/videos
|
||||
|
||||
- name: Blog
|
||||
description: Read my blog
|
||||
@ -56,13 +67,6 @@ accounts:
|
||||
class: fa-brands fa-meta
|
||||
url:
|
||||
subitems:
|
||||
- name: Instagram
|
||||
description: Follow me on Instagram
|
||||
icon:
|
||||
class: fa-brands fa-instagram
|
||||
url: https://www.instagram.com/kevinveenbirkenbach/
|
||||
identifier: kevinveenbirkenbach
|
||||
warning: Using software and platforms from the Meta corporation (e.g., Facebook, Instagram, WhatsApp) may compromise your data privacy and digital freedom due to centralized control, extensive data collection practices, and inconsistent moderation policies. These platforms often fail to adequately address harmful content, misinformation, and abuse.
|
||||
- name: Facebook
|
||||
description: Like my Facebook page
|
||||
icon:
|
||||
@ -502,6 +506,7 @@ navigation:
|
||||
- link: accounts
|
||||
|
||||
- name: Imprint
|
||||
description: Check out the imprint information
|
||||
icon:
|
||||
class: fa-solid fa-scale-balanced
|
||||
url: https://s.veen.world/imprint
|
||||
|
@ -1,3 +1,5 @@
|
||||
@import url("navigation.css");
|
||||
|
||||
/* General link styles */
|
||||
a {
|
||||
text-decoration: none;
|
||||
@ -77,67 +79,4 @@ h3.card-title {
|
||||
|
||||
h3.footer-title {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
/* Dropdown menu styles */
|
||||
.dropdown-menu {
|
||||
position: absolute !important;
|
||||
}
|
||||
|
||||
.dropdown-menu-footer {
|
||||
position: absolute !important;
|
||||
top: auto !important;
|
||||
bottom: 100%; /* Positions the menu above the trigger */
|
||||
transform: translateY(-10px); /* Optional spacing for smoother appearance */
|
||||
}
|
||||
|
||||
/* Dropdown submenu styles */
|
||||
.dropdown-submenu {
|
||||
position: relative;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.dropdown-submenu > .dropdown-menu {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 100%; /* Default position: open to the right */
|
||||
margin-top: -1px;
|
||||
z-index: 1050;
|
||||
transition: opacity 0.3s ease-in-out; /* Smooth opacity transition */
|
||||
}
|
||||
|
||||
/* Handle collapse behavior for dropdowns */
|
||||
.dropdown-menu.collapse {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dropdown-menu.collapse.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Ensure submenus are hidden by default */
|
||||
.dropdown-submenu .dropdown-menu {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
position: absolute;
|
||||
left: 100%;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
/* Show submenu on hover */
|
||||
.dropdown-submenu:hover > .dropdown-menu {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Ensure submenu remains visible when hovered over */
|
||||
.dropdown-submenu:hover > .dropdown-menu:hover {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Handle dynamic submenu positioning */
|
||||
.dropdown-submenu > .dropdown-menu[style*="right: 100%"] {
|
||||
left: auto; /* Override left position for leftward opening */
|
||||
}
|
||||
}
|
36
app/static/css/navigation.css
Normal file
36
app/static/css/navigation.css
Normal file
@ -0,0 +1,36 @@
|
||||
/* Dropdown-Menüs verstecken */
|
||||
.dropdown-menu {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.3s ease, visibility 0.3s ease;
|
||||
}
|
||||
|
||||
/* Dropdown-Menü beim Hover anzeigen */
|
||||
.nav-item.dropdown:hover > .dropdown-menu,
|
||||
.dropdown-submenu:hover > .dropdown-menu {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* Dropdown-Menü bei der Klasse "open" anzeigen */
|
||||
.nav-item.dropdown.open > .dropdown-menu,
|
||||
.dropdown-submenu.open > .dropdown-menu {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* Positionierung von Submenüs */
|
||||
.dropdown-submenu > .dropdown-menu {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 100%; /* Rechts ausklappen */
|
||||
}
|
||||
|
||||
.dropdown-submenu.open > .dropdown-menu {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
119
app/static/js/navigation.js
Normal file
119
app/static/js/navigation.js
Normal file
@ -0,0 +1,119 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const menuItems = document.querySelectorAll('.nav-item.dropdown');
|
||||
const subMenuItems = document.querySelectorAll('.dropdown-submenu');
|
||||
|
||||
menuItems.forEach(item => {
|
||||
let timeout;
|
||||
|
||||
// Öffnen beim Hovern
|
||||
item.addEventListener('mouseenter', () => {
|
||||
clearTimeout(timeout);
|
||||
openMenu(item, true);
|
||||
});
|
||||
|
||||
// Verzögertes Schließen beim Verlassen
|
||||
item.addEventListener('mouseleave', () => {
|
||||
timeout = setTimeout(() => closeMenu(item), 500);
|
||||
});
|
||||
|
||||
// Öffnen und Position anpassen beim Klicken
|
||||
item.addEventListener('click', (e) => {
|
||||
e.preventDefault(); // Verhindert die Standardaktion
|
||||
e.stopPropagation(); // Verhindert das Schließen von Menüs bei Klick
|
||||
if (item.classList.contains('open')) {
|
||||
closeMenu(item);
|
||||
} else {
|
||||
openMenu(item, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
subMenuItems.forEach(item => {
|
||||
let timeout;
|
||||
|
||||
// Öffnen beim Hovern
|
||||
item.addEventListener('mouseenter', () => {
|
||||
clearTimeout(timeout);
|
||||
openMenu(item, false);
|
||||
});
|
||||
|
||||
// Verzögertes Schließen beim Verlassen
|
||||
item.addEventListener('mouseleave', () => {
|
||||
timeout = setTimeout(() => closeMenu(item), 500);
|
||||
});
|
||||
|
||||
// Öffnen und Position anpassen beim Klicken
|
||||
item.addEventListener('click', (e) => {
|
||||
e.preventDefault(); // Verhindert die Standardaktion
|
||||
e.stopPropagation(); // Verhindert das Schließen von Menüs bei Klick
|
||||
if (item.classList.contains('open')) {
|
||||
closeMenu(item);
|
||||
} else {
|
||||
openMenu(item, false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Globale Klick-Listener, um Menüs zu schließen, wenn außerhalb geklickt wird
|
||||
document.addEventListener('click', () => {
|
||||
[...menuItems, ...subMenuItems].forEach(item => closeMenu(item));
|
||||
});
|
||||
|
||||
function openMenu(item, isTopLevel) {
|
||||
item.classList.add('open');
|
||||
const submenu = item.querySelector('.dropdown-menu');
|
||||
if (submenu) {
|
||||
adjustMenuPosition(submenu, item, isTopLevel);
|
||||
submenu.style.display = 'block';
|
||||
submenu.style.opacity = '1';
|
||||
submenu.style.visibility = 'visible';
|
||||
}
|
||||
}
|
||||
|
||||
function closeMenu(item) {
|
||||
item.classList.remove('open');
|
||||
const submenu = item.querySelector('.dropdown-menu');
|
||||
if (submenu) {
|
||||
submenu.style.display = 'none';
|
||||
submenu.style.opacity = '0';
|
||||
submenu.style.visibility = 'hidden';
|
||||
}
|
||||
}
|
||||
|
||||
function adjustMenuPosition(submenu, parent, isTopLevel) {
|
||||
const rect = submenu.getBoundingClientRect();
|
||||
const parentRect = parent.getBoundingClientRect();
|
||||
|
||||
// Platzberechnung
|
||||
const spaceAbove = parentRect.top;
|
||||
const spaceBelow = window.innerHeight - parentRect.bottom;
|
||||
const spaceLeft = parentRect.left;
|
||||
const spaceRight = window.innerWidth - parentRect.right;
|
||||
|
||||
// Standardpositionierung
|
||||
submenu.style.top = '';
|
||||
submenu.style.bottom = '';
|
||||
submenu.style.left = '';
|
||||
submenu.style.right = '';
|
||||
|
||||
if (isTopLevel) {
|
||||
// Top-Level-Menüs öffnen nur nach oben oder unten
|
||||
if (spaceBelow < rect.height && spaceAbove > rect.height) {
|
||||
submenu.style.bottom = '100%';
|
||||
submenu.style.top = 'auto';
|
||||
} else {
|
||||
submenu.style.top = `${parentRect.height}px`;
|
||||
submenu.style.bottom = 'auto';
|
||||
}
|
||||
} else {
|
||||
// Submenüs öffnen in die Richtung mit mehr Platz
|
||||
const prefersRight = spaceRight >= spaceLeft;
|
||||
submenu.style.left = prefersRight ? '100%' : 'auto';
|
||||
submenu.style.right = prefersRight ? 'auto' : '100%';
|
||||
|
||||
const prefersBelow = spaceBelow >= spaceAbove;
|
||||
submenu.style.top = prefersBelow ? '0' : 'auto';
|
||||
submenu.style.bottom = prefersBelow ? 'auto' : '100%';
|
||||
}
|
||||
}
|
||||
});
|
@ -1,41 +0,0 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const dropdownSubmenus = document.querySelectorAll('.dropdown-submenu');
|
||||
|
||||
dropdownSubmenus.forEach(submenu => {
|
||||
let timeout;
|
||||
|
||||
// Zeige das Submenü beim Hover
|
||||
submenu.addEventListener('mouseenter', () => {
|
||||
clearTimeout(timeout);
|
||||
const menu = submenu.querySelector('.dropdown-menu');
|
||||
if (menu) {
|
||||
// Dynamische Positionierung
|
||||
const rect = menu.getBoundingClientRect();
|
||||
const viewportWidth = window.innerWidth;
|
||||
|
||||
// Überprüfen, ob Platz nach rechts ist, sonst nach links öffnen
|
||||
if (rect.right > viewportWidth) {
|
||||
menu.style.left = 'auto';
|
||||
menu.style.right = '100%';
|
||||
} else {
|
||||
menu.style.left = '100%';
|
||||
menu.style.right = 'auto';
|
||||
}
|
||||
|
||||
menu.style.display = 'block';
|
||||
menu.style.opacity = '1';
|
||||
}
|
||||
});
|
||||
|
||||
// Verstecke das Submenü nach 0.5 Sekunden
|
||||
submenu.addEventListener('mouseleave', () => {
|
||||
const menu = submenu.querySelector('.dropdown-menu');
|
||||
if (menu) {
|
||||
timeout = setTimeout(() => {
|
||||
menu.style.display = 'none';
|
||||
menu.style.opacity = '0';
|
||||
}, 500); // 0.5 Sekunden Verzögerung
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
@ -41,7 +41,7 @@
|
||||
<!-- Include modal -->
|
||||
{% include "moduls/modal.html.j2" %}
|
||||
<script src="{{ url_for('static', filename='js/modal.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/submenus.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/navigation.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/tooltip.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
@ -34,7 +34,7 @@
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
|
||||
<!-- Navigation Bar -->
|
||||
<!-- Navigation Bar -->
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<div class="container-fluid">
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav{{menu_type}}" aria-controls="navbarNav{{menu_type}}" aria-expanded="false" aria-label="Toggle navigation">
|
||||
@ -53,14 +53,14 @@
|
||||
{% else %}
|
||||
<!-- Dropdown Menu -->
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown{{ loop.index }}" role="button" data-bs-toggle="dropdown" data-bs-display="dynamic" data-popper-placement="top" title="{{ item.description }}" aria-expanded="false">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown{{ loop.index }}" role="button" data-bs-toggle="dropdown" data-bs-display="dynamic" aria-expanded="false">
|
||||
{% if item.icon is defined and item.icon.class is defined %}
|
||||
<i class="{{ item.icon.class }}" data-bs-toggle="tooltip"></i> {{ item.name }}
|
||||
<i class="{{ item.icon.class }}"></i> {{ item.name }}
|
||||
{% else %}
|
||||
<p>Missing icon in item: {{ item }}</p>
|
||||
{% endif %}
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-{{menu_type}}" aria-labelledby="navbarDropdown{{ loop.index }}">
|
||||
<ul class="dropdown-menu">
|
||||
{{ render_subitems(item.subitems) }}
|
||||
</ul>
|
||||
</li>
|
||||
@ -69,4 +69,4 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</nav>
|
||||
|
Loading…
x
Reference in New Issue
Block a user