Improve mobile my2dos navigation

This commit is contained in:
rucki
2026-08-24 10:56:00 +02:00
parent c7f7bc801e
commit a7567d5d0b
4 changed files with 71 additions and 13 deletions
+5 -4
View File
@@ -25,13 +25,14 @@
{% endif %}
</div>
<div class="item-actions btn-group btn-group-sm">
{% if item.kind == 'todo' %}<button class="btn btn-outline-primary" hx-post="{% url 'toggle_done' item.id %}{% if request.GET.urlencode %}?{{ request.GET.urlencode }}{% endif %}" hx-target="#item-{{ item.id }}" hx-swap="outerHTML">✓</button>{% endif %}
{% if item.kind == 'todo' %}<button class="btn btn-outline-primary item-action-btn" title="{% trans 'Done' %}" aria-label="{% trans 'Done' %}" hx-post="{% url 'toggle_done' item.id %}{% if request.GET.urlencode %}?{{ request.GET.urlencode }}{% endif %}" hx-target="#item-{{ item.id }}" hx-swap="outerHTML">✓ <span class="d-md-none">{% trans "Done" %}</span></button>{% endif %}
{% if request.resolver_match.url_name == 'item_detail' %}
<a class="btn btn-outline-secondary" href="{% url 'item_editor' item.id %}?next={{ request.GET.next|default:'/'|urlencode }}">✎</a>
<a class="btn btn-outline-secondary item-action-btn" title="{% trans 'Edit' %}" aria-label="{% trans 'Edit' %}" href="{% url 'item_editor' item.id %}?next={{ request.GET.next|default:'/'|urlencode }}">✎ <span class="d-md-none">{% trans "Edit" %}</span></a>
{% else %}
<button class="btn btn-outline-secondary" hx-get="{% url 'edit_item' item.id %}{% if request.GET.urlencode %}?{{ request.GET.urlencode }}{% endif %}" hx-target="#item-{{ item.id }}" hx-swap="outerHTML">✎</button>
<a class="btn btn-outline-secondary item-action-btn d-md-none" title="{% trans 'Edit' %}" aria-label="{% trans 'Edit' %}" href="{% url 'item_editor' item.id %}?next={{ request.get_full_path|urlencode }}">✎ <span>{% trans "Edit" %}</span></a>
<button class="btn btn-outline-secondary item-action-btn d-none d-md-inline-block" title="{% trans 'Edit' %}" aria-label="{% trans 'Edit' %}" hx-get="{% url 'edit_item' item.id %}{% if request.GET.urlencode %}?{{ request.GET.urlencode }}{% endif %}" hx-target="#item-{{ item.id }}" hx-swap="outerHTML">✎</button>
{% endif %}
<button class="btn btn-outline-danger" hx-post="{% url 'delete_item' item.id %}" hx-target="#item-{{ item.id }}" hx-swap="delete">×</button>
<button class="btn btn-outline-danger item-action-btn" title="{% trans 'Delete' %}" aria-label="{% trans 'Delete' %}" hx-post="{% url 'delete_item' item.id %}" hx-target="#item-{{ item.id }}" hx-swap="delete">× <span class="d-md-none">{% trans "Delete" %}</span></button>
</div>
</div>
</div>
+3 -2
View File
@@ -17,12 +17,13 @@
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
<style>
body{background:linear-gradient(180deg,#f8fafc 0,#f7f7f4 18rem);color:#1f2937}.shell{max-width:960px}.navbar{box-shadow:0 .35rem 1.25rem rgba(15,23,42,.06)}.navbar a{text-decoration:none}.nav-soft{color:#475569!important;font-weight:500;padding:.2rem .35rem;border-radius:.5rem}.nav-soft:hover{color:#0d6efd!important;background:#eff6ff}.user-menu-btn{min-width:9rem}.card{border:0}.item-card{border:0;border-radius:1.1rem;box-shadow:0 .45rem 1.4rem rgba(15,23,42,.07)!important}.filter-panel{padding:.25rem .15rem}.border{border-color:rgba(15,23,42,.10)!important}.text-bg-light{background:#f1f5f9!important;color:#475569!important}.btn{border-radius:.7rem}.form-control,.form-select{border-color:#e2e8f0;border-radius:.75rem}.content p:last-child{margin-bottom:0}.tag{font-size:.85rem}.slash-menu{position:absolute;z-index:20;top:100%;left:0;width:18rem}.done{opacity:.6}.done .content{text-decoration:line-through}.content img{max-width:100%;max-height:420px;border-radius:.5rem;border:1px solid #ddd;padding:.25rem;background:white}.overview-content{max-height:calc(var(--overview-lines) * 1.55em);overflow:hidden}.overview-content.expanded{max-height:none}.content{min-width:0;max-width:100%;overflow-wrap:anywhere}.content pre{position:relative;display:block;width:100%;max-width:100%;box-sizing:border-box;background:#1f2937;color:#f9fafb;border-radius:.6rem;padding:1rem;overflow-x:auto;overflow-y:auto;white-space:pre}.content pre code{display:block;color:inherit;white-space:pre;min-width:0}.copy-code{position:absolute;top:.4rem;right:.4rem;line-height:1}.mobile-create-buttons,.mobile-create-nav,.mobile-tags,.mobile-media,.mobile-tag-picker{display:none}.mobile-tag-popup{position:fixed;inset:auto .75rem 1rem .75rem;z-index:1050;max-height:55vh;overflow:auto}.mobile-tag-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.25);z-index:1040}.camera-input{max-width:13rem}@media (max-width: 767.98px){.shell{max-width:100%}main.container{padding-left:.75rem;padding-right:.75rem}.navbar .container{gap:.5rem}.navbar-brand{font-size:1rem}.navbar .d-flex{gap:.5rem!important}.item-card{border-radius:.75rem}.mobile-create-buttons,.mobile-create-nav,.mobile-tags,.mobile-media{display:flex!important}.mobile-tag-picker{display:block!important}.desktop-quick-create{display:none!important}.mobile-create-nav{position:sticky;top:57px;z-index:10;background:#f7f7f4;padding:.35rem 0}.desktop-file{display:none!important}.mobile-create-buttons{position:sticky;top:57px;z-index:10;background:#f7f7f4;padding:.35rem 0}.quick-actions{flex-wrap:wrap}.quick-actions .form-control{min-width:100%}.slash-menu{width:calc(100vw - 1.5rem)}textarea.form-control-lg{font-size:1rem}.btn{touch-action:manipulation}.item-row{flex-direction:column}.item-actions{align-self:flex-end}.item-main{width:100%;min-width:0}}
body{background:linear-gradient(180deg,#f8fafc 0,#f7f7f4 18rem);color:#1f2937}.shell{max-width:960px}.navbar{box-shadow:0 .35rem 1.25rem rgba(15,23,42,.06)}.navbar a{text-decoration:none}.nav-soft{color:#475569!important;font-weight:500;padding:.2rem .35rem;border-radius:.5rem}.nav-soft:hover{color:#0d6efd!important;background:#eff6ff}.user-menu-btn{min-width:9rem}.card{border:0}.item-card{border:0;border-radius:1.1rem;box-shadow:0 .45rem 1.4rem rgba(15,23,42,.07)!important}.filter-panel{padding:.25rem .15rem}.border{border-color:rgba(15,23,42,.10)!important}.text-bg-light{background:#f1f5f9!important;color:#475569!important}.btn{border-radius:.7rem}.form-control,.form-select{border-color:#e2e8f0;border-radius:.75rem}.content p:last-child{margin-bottom:0}.tag{font-size:.85rem}.slash-menu{position:absolute;z-index:20;top:100%;left:0;width:18rem}.done{opacity:.6}.done .content{text-decoration:line-through}.content img{max-width:100%;max-height:420px;border-radius:.5rem;border:1px solid #ddd;padding:.25rem;background:white}.overview-content{max-height:calc(var(--overview-lines) * 1.55em);overflow:hidden}.overview-content.expanded{max-height:none}.content{min-width:0;max-width:100%;overflow-wrap:anywhere}.content pre{position:relative;display:block;width:100%;max-width:100%;box-sizing:border-box;background:#1f2937;color:#f9fafb;border-radius:.6rem;padding:1rem;overflow-x:auto;overflow-y:auto;white-space:pre}.content pre code{display:block;color:inherit;white-space:pre;min-width:0}.copy-code{position:absolute;top:.4rem;right:.4rem;line-height:1}.mobile-create-buttons,.mobile-create-nav,.mobile-tags,.mobile-media,.mobile-tag-picker,.mobile-bottom-nav{display:none}.mobile-tag-popup{position:fixed;inset:auto .75rem 1rem .75rem;z-index:1050;max-height:55vh;overflow:auto}.mobile-tag-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.25);z-index:1040}.camera-input{max-width:13rem}@media (max-width: 767.98px){body{padding-bottom:4.25rem}.shell{max-width:100%}main.container{padding:.25rem .75rem 1rem}.navbar .container{gap:.5rem}.navbar-brand{font-size:1rem}.navbar .d-flex{gap:.5rem!important}.user-menu-btn{min-width:auto;max-width:9rem}.item-card{border-radius:.75rem}.item-actions{width:100%;justify-content:stretch!important}.item-action-btn{flex:1 1 0;min-width:0}.mobile-create-buttons,.mobile-tags,.mobile-media{display:flex!important}.mobile-bottom-nav{display:flex!important;position:fixed;left:.75rem;right:.75rem;bottom:.75rem;z-index:1060;background:white;border-radius:1rem;box-shadow:0 .5rem 1.5rem rgba(15,23,42,.18);padding:.35rem}.mobile-bottom-nav .btn{font-size:.8rem;padding:.45rem .25rem}.mobile-tag-picker{display:block!important}.desktop-quick-create{display:none!important}.mobile-create-nav{display:none!important}.desktop-file{display:none!important}.mobile-create-buttons{position:sticky;top:57px;z-index:10;background:#f7f7f4;padding:.35rem 0}.toolbar-filter{display:block;background:#f7f7f4;border-top:4px solid #fff;padding:.35rem .75rem .5rem;box-shadow:0 .4rem .9rem rgba(15,23,42,.05)}.mobile-filter-sidebar{display:none}.filter-panel{background:#f7f7f4;padding:0}.filter-panel-title{display:block;margin:0 0 .3rem!important;font-size:.82rem;color:#64748b;line-height:1}.filter-row{display:grid;grid-template-columns:repeat(5,minmax(0,1fr)) 2.25rem;gap:.25rem;width:100%}.filter-row .btn,.filter-row summary{width:100%;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding-left:.1rem;padding-right:.1rem}.mobile-filter-toggle{position:absolute;opacity:0;pointer-events:none}.mobile-filter-more{grid-column:6;grid-row:1;position:relative;border:0;background:transparent;color:#475569;font-size:1.2rem;line-height:1;text-align:center;cursor:pointer}.mobile-filter-more .badge{position:absolute;top:-.15rem;right:.15rem;font-size:.55rem;line-height:1;padding:.12rem .25rem}.mobile-filter-details{display:none;grid-column:1/-1;grid-row:2;margin-top:.45rem;padding:.75rem;background:white;border-radius:1rem;box-shadow:0 .35rem 1rem rgba(15,23,42,.12)}.mobile-filter-toggle:checked ~ .mobile-filter-details{display:block}.quick-actions{flex-wrap:wrap}.quick-actions .form-control{min-width:100%}.slash-menu{width:calc(100vw - 1.5rem)}textarea.form-control-lg{font-size:1rem}.btn{touch-action:manipulation}.item-row{flex-direction:column}.item-actions{align-self:flex-end}.item-main{width:100%;min-width:0}}
</style>
</head>
<body>
<nav class="navbar bg-white sticky-top"><div class="container shell"><a class="navbar-brand fw-bold" href="/">my2dos</a><div class="d-flex align-items-center gap-3"><span class="small d-none d-sm-inline"><a class="nav-soft" href="/">{% for label in nav_overview_labels %}{{ label }}{% if not forloop.last %} · {% endif %}{% endfor %}</a>{% for link in nav_hidden_links %}<a class="nav-soft ms-2" href="{{ link.url }}">{{ link.label }}</a>{% endfor %}</span>{% if user.is_authenticated %}<a class="small nav-soft" href="{% url 'journal' %}">{% trans "Journal" %}</a><a class="small nav-soft" href="{% url 'kanban_list' %}">{% trans "Kanban" %}</a><a class="small nav-soft" href="{% url 'team_list' %}">{% trans "Teams" %}</a><div class="dropdown"><button class="btn btn-sm btn-outline-secondary dropdown-toggle user-menu-btn" data-bs-toggle="dropdown">{{ user.get_full_name|default:user.username }}</button><ul class="dropdown-menu dropdown-menu-end"><li><a class="dropdown-item" href="{% url 'settings' %}">{% trans "Settings" %}</a></li><li><button id="pwa-install" type="button" class="dropdown-item d-none">{% trans "Install app" %}</button></li>{% if user.is_staff %}<li><a class="dropdown-item" href="/admin/">Admin</a></li>{% endif %}<li><hr class="dropdown-divider"></li><li><form method="post" action="{% url 'logout' %}">{% csrf_token %}<button class="dropdown-item">{% trans "Logout" %}</button></form></li></ul></div>{% else %}<a class="btn btn-sm btn-dark" href="{% url 'login' %}">{% trans "Login" %}</a>{% endif %}</div></div></nav>
<div class="top-toolbar sticky-top"><nav class="navbar bg-white"><div class="container shell"><div class="d-flex align-items-center gap-2 w-100"><a class="small nav-soft fw-semibold" href="/">Start</a>{% if user.is_authenticated %}<a class="small nav-soft" href="{% url 'journal' %}">{% trans "Journal" %}</a><a class="small nav-soft" href="{% url 'team_list' %}">{% trans "Teams" %}</a><div class="dropdown ms-auto"><button class="btn btn-sm btn-outline-secondary dropdown-toggle user-menu-btn" data-bs-toggle="dropdown">{{ user.get_full_name|default:user.username }}</button><ul class="dropdown-menu dropdown-menu-end"><li><a class="dropdown-item" href="{% url 'settings' %}">{% trans "Settings" %}</a></li><li><hr class="dropdown-divider"></li><li><form method="post" action="{% url 'logout' %}">{% csrf_token %}<button class="dropdown-item">{% trans "Logout" %}</button></form></li></ul></div>{% else %}<a class="btn btn-sm btn-dark ms-auto" href="{% url 'login' %}">{% trans "Login" %}</a>{% endif %}</div></div></nav>{% block toolbar_filter %}{% endblock %}</div>
<main class="container shell py-4">{% for message in messages %}<div class="alert alert-{{ message.tags|default:'info' }}">{{ message }}</div>{% endfor %}{% block content %}{% endblock %}</main>
{% if user.is_authenticated %}<nav class="mobile-bottom-nav gap-1 d-md-none" aria-label="Mobile navigation"><a class="btn btn-primary flex-fill" href="{% url 'new_item' %}?kind=todo">Aufgabe</a><a class="btn btn-secondary flex-fill" href="{% url 'new_item' %}?kind=note">Notiz</a><a class="btn btn-success flex-fill" href="{% url 'new_item' %}?kind=link">Link</a><a class="btn btn-info flex-fill" href="{% url 'new_item' %}?kind=journal">Journal</a></nav>{% endif %}
<script>
document.body.addEventListener('htmx:configRequest', e => {e.detail.headers['X-CSRFToken']='{{ csrf_token }}'});
window.quickComposer = window.quickComposer || function(tags, itemId=null){
+58 -7
View File
@@ -1,5 +1,41 @@
{% extends 'core/base.html' %}
{% load querystring i18n %}
{% block toolbar_filter %}
<div class="toolbar-filter d-md-none">
<div class="filter-panel">
<div class="fw-semibold filter-panel-title">{% trans "Filter" %}</div>
<div class="filter-row">
<a class="btn btn-sm {% if active_kind == 'todo' %}btn-primary{% else %}btn-outline-primary{% endif %}" href="{% if active_kind == 'todo' %}{% query_replace kind=None %}{% else %}{% query_replace kind='todo' %}{% endif %}">Todos{% if active_kind == 'todo' %} ×{% endif %}</a>
<a class="btn btn-sm {% if active_kind == 'note' %}btn-secondary{% else %}btn-outline-secondary{% endif %}" href="{% if active_kind == 'note' %}{% query_replace kind=None %}{% else %}{% query_replace kind='note' %}{% endif %}">{% trans "Notes" %}{% if active_kind == 'note' %} ×{% endif %}</a>
<a class="btn btn-sm {% if active_kind == 'link' %}btn-success{% else %}btn-outline-success{% endif %}" href="{% if active_kind == 'link' %}{% query_replace kind=None %}{% else %}{% query_replace kind='link' %}{% endif %}">Links{% if active_kind == 'link' %} ×{% endif %}</a>
<a class="btn btn-sm {% if active_status == 'due' %}btn-danger{% else %}btn-outline-danger{% endif %}" href="{% if active_status == 'due' %}{% query_replace status=None %}{% else %}{% query_replace status='due' %}{% endif %}">{% trans "Due" %} <span class="badge {% if active_status == 'due' %}text-bg-light{% else %}text-bg-danger{% endif %}">{{ due_count }}</span>{% if active_status == 'due' %} ×{% endif %}</a>
<a class="btn btn-sm {% if active_status == 'archive' %}btn-dark{% else %}btn-outline-dark{% endif %}" href="{% if active_status == 'archive' %}{% query_replace status=None %}{% else %}{% query_replace status='archive' %}{% endif %}">{% trans "Archive" %}{% if active_status == 'archive' %} ×{% endif %}</a>
<input id="mobile-filter-toggle" class="mobile-filter-toggle" type="checkbox">
<label for="mobile-filter-toggle" class="mobile-filter-more" title="Filter bearbeiten">☰{% if q or active_tags %}<span class="badge text-bg-secondary ms-1">!</span>{% endif %}</label>
<div class="mobile-filter-details">
{% if q or active_tags %}<div class="small text-muted mb-2">{% if q %}„{{ q }}”{% endif %}{% for tag_name in active_tags %} #{{ tag_name }}{% endfor %}</div>{% endif %}
<form class="mb-3 position-relative" method="get" x-data="searchBox([{% for tag in tags %}'{{ tag.name|escapejs }}'{% if not forloop.last %},{% endif %}{% endfor %}])">
{% for tag_name in active_tags %}<input type="hidden" name="tag" value="{{ tag_name }}">{% endfor %}
{% if active_tag_mode == 'all' %}<input type="hidden" name="tag_mode" value="all">{% endif %}
{% if active_kind %}<input type="hidden" name="kind" value="{{ active_kind }}">{% endif %}
{% if active_status %}<input type="hidden" name="status" value="{{ active_status }}">{% endif %}
<div class="input-group input-group-sm">
<input x-ref="search" class="form-control" name="q" value="{{ q }}" placeholder="{% trans 'Search (Ctrl+F, # for tags)' %}" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" @input="onInput($event)" @keydown="onKeydown($event)">
{% if q %}<a class="btn btn-outline-secondary" href="{% query_replace q=None %}" title="{% trans 'Clear search' %}">×</a>{% endif %}
</div>
<div class="slash-menu list-group shadow" x-show="open" @click.outside="open=false" x-cloak>
<template x-for="(tag, idx) in filtered" :key="tag"><button type="button" class="list-group-item list-group-item-action" :class="{'active': idx === active}" @mouseenter="active=idx" @mousedown.prevent="select(tag)"><strong x-text="'#'+tag"></strong></button></template>
</div>
</form>
{% if active_tags|length > 1 %}<div class="btn-group btn-group-sm mb-2"><a class="btn {% if active_tag_mode == 'any' %}btn-dark{% else %}btn-outline-secondary{% endif %}" href="{% query_replace tag_mode=None %}">{% trans "any" %}</a><a class="btn {% if active_tag_mode == 'all' %}btn-dark{% else %}btn-outline-secondary{% endif %}" href="{% query_replace tag_mode='all' %}">{% trans "all" %}</a></div>{% endif %}
<div class="d-flex flex-wrap gap-2">
{% for tag in tags %}{% if tag.name in active_tags %}<a class="badge rounded-pill text-bg-dark text-decoration-none tag tag-filter" data-tag="{{ tag.name }}" href="{% query_replace tag=None %}">#{{ tag.name }} ×</a>{% else %}<a class="badge rounded-pill text-bg-light text-decoration-none tag tag-filter" data-tag="{{ tag.name }}" href="{% query_replace tag=tag.name %}">#{{ tag.name }}</a>{% endif %}{% empty %}<span class="text-muted small">{% trans "No tags yet" %}</span>{% endfor %}
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block content %}
<div class="mobile-create-nav gap-2 mb-3">
<a class="btn btn-sm btn-primary flex-fill" href="{% url 'new_item' %}?kind=todo&next={{ request.get_full_path|urlencode }}">{% trans "Task" %}</a>
@@ -8,18 +44,17 @@
<a class="btn btn-sm btn-info flex-fill" href="{% url 'new_item' %}?kind=journal&next={{ request.get_full_path|urlencode }}">Journal</a>
</div>
<div class="row g-4">
<aside class="col-lg-3 order-lg-2">
<aside class="col-lg-3 order-lg-2 mobile-filter-sidebar">
<div class="filter-panel">
<div class="fw-semibold mb-2">{% trans "Filter" %}</div>
<div class="d-flex flex-wrap gap-2 mb-3">
<div class="fw-semibold mb-2 filter-panel-title">{% trans "Filter" %}</div>
<div class="d-flex flex-wrap gap-2 mb-3 filter-buttons">
<a class="btn btn-sm {% if active_kind == 'todo' %}btn-primary{% else %}btn-outline-primary{% endif %}" href="{% if active_kind == 'todo' %}{% query_replace kind=None %}{% else %}{% query_replace kind='todo' %}{% endif %}">Todos{% if active_kind == 'todo' %} ×{% endif %}</a>
<a class="btn btn-sm {% if active_kind == 'note' %}btn-secondary{% else %}btn-outline-secondary{% endif %}" href="{% if active_kind == 'note' %}{% query_replace kind=None %}{% else %}{% query_replace kind='note' %}{% endif %}">{% trans "Notes" %}{% if active_kind == 'note' %} ×{% endif %}</a>
<a class="btn btn-sm {% if active_kind == 'link' %}btn-success{% else %}btn-outline-success{% endif %}" href="{% if active_kind == 'link' %}{% query_replace kind=None %}{% else %}{% query_replace kind='link' %}{% endif %}">Links{% if active_kind == 'link' %} ×{% endif %}</a>
<a class="btn btn-sm {% if active_status == 'open' %}btn-warning{% else %}btn-outline-warning{% endif %}" href="{% if active_status == 'open' %}{% query_replace status=None %}{% else %}{% query_replace status='open' %}{% endif %}">{% trans "Open" %}{% if active_status == 'open' %} ×{% endif %}</a>
<a class="btn btn-sm {% if active_status == 'due' %}btn-danger{% else %}btn-outline-danger{% endif %}" href="{% if active_status == 'due' %}{% query_replace status=None %}{% else %}{% query_replace status='due' %}{% endif %}">{% trans "Due" %} <span class="badge {% if active_status == 'due' %}text-bg-light{% else %}text-bg-danger{% endif %}">{{ due_count }}</span>{% if active_status == 'due' %} ×{% endif %}</a>
<a class="btn btn-sm {% if active_status == 'archive' %}btn-dark{% else %}btn-outline-dark{% endif %}" href="{% if active_status == 'archive' %}{% query_replace status=None %}{% else %}{% query_replace status='archive' %}{% endif %}">{% trans "Archive" %}{% if active_status == 'archive' %} ×{% endif %}</a>
</div>
<form class="mb-3 position-relative" method="get" x-data="searchBox([{% for tag in tags %}'{{ tag.name|escapejs }}'{% if not forloop.last %},{% endif %}{% endfor %}])" x-on:keydown.window="if(($event.ctrlKey||$event.metaKey)&&$event.key==='f'){ $event.preventDefault(); $refs.search.focus(); $refs.search.select(); }">
<form class="mb-3 position-relative d-none d-md-block" method="get" x-data="searchBox([{% for tag in tags %}'{{ tag.name|escapejs }}'{% if not forloop.last %},{% endif %}{% endfor %}])" x-on:keydown.window="if(($event.ctrlKey||$event.metaKey)&&$event.key==='f'){ $event.preventDefault(); $refs.search.focus(); $refs.search.select(); }">
{% for tag_name in active_tags %}<input type="hidden" name="tag" value="{{ tag_name }}">{% endfor %}
{% if active_tag_mode == 'all' %}<input type="hidden" name="tag_mode" value="all">{% endif %}
{% if active_kind %}<input type="hidden" name="kind" value="{{ active_kind }}">{% endif %}
@@ -46,14 +81,30 @@
{% include 'core/_tags.html' %}
</div>
</div>
<details class="d-md-none">
<summary class="btn btn-sm btn-outline-secondary mb-2">{% trans "Show tags" %}</summary>
<details class="d-md-none mobile-filter-more">
<summary class="btn btn-sm btn-outline-secondary" title="Filter bearbeiten">☰{% if q or active_tags %}<span class="badge text-bg-secondary ms-1">!</span>{% endif %}</summary>
<div class="mobile-filter-details">
{% if q or active_tags %}<div class="small text-muted mb-2">{% if q %}„{{ q }}”{% endif %}{% for tag_name in active_tags %} #{{ tag_name }}{% endfor %}</div>{% endif %}
<form class="mb-3 position-relative" method="get" x-data="searchBox([{% for tag in tags %}'{{ tag.name|escapejs }}'{% if not forloop.last %},{% endif %}{% endfor %}])">
{% for tag_name in active_tags %}<input type="hidden" name="tag" value="{{ tag_name }}">{% endfor %}
{% if active_tag_mode == 'all' %}<input type="hidden" name="tag_mode" value="all">{% endif %}
{% if active_kind %}<input type="hidden" name="kind" value="{{ active_kind }}">{% endif %}
{% if active_status %}<input type="hidden" name="status" value="{{ active_status }}">{% endif %}
<div class="input-group input-group-sm">
<input x-ref="search" class="form-control" name="q" value="{{ q }}" placeholder="{% trans 'Search (Ctrl+F, # for tags)' %}" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" @input="onInput($event)" @keydown="onKeydown($event)">
{% if q %}<a class="btn btn-outline-secondary" href="{% query_replace q=None %}" title="{% trans 'Clear search' %}">×</a>{% endif %}
</div>
<div class="slash-menu list-group shadow" x-show="open" @click.outside="open=false" x-cloak>
<template x-for="(tag, idx) in filtered" :key="tag"><button type="button" class="list-group-item list-group-item-action" :class="{'active': idx === active}" @mouseenter="active=idx" @mousedown.prevent="select(tag)"><strong x-text="'#'+tag"></strong></button></template>
</div>
</form>
{% if active_tags|length > 1 %}<div class="btn-group btn-group-sm mb-2"><a class="btn {% if active_tag_mode == 'any' %}btn-dark{% else %}btn-outline-secondary{% endif %}" href="{% query_replace tag_mode=None %}">{% trans "any" %}</a><a class="btn {% if active_tag_mode == 'all' %}btn-dark{% else %}btn-outline-secondary{% endif %}" href="{% query_replace tag_mode='all' %}">{% trans "all" %}</a></div>{% endif %}
<div class="d-flex flex-wrap gap-2">
{% for tag in tags %}
{% if tag.name in active_tags %}<a class="badge rounded-pill text-bg-dark text-decoration-none tag tag-filter" data-tag="{{ tag.name }}" href="{% query_replace tag=None %}">#{{ tag.name }} ×</a>{% else %}<a class="badge rounded-pill text-bg-light text-decoration-none tag tag-filter" data-tag="{{ tag.name }}" href="{% query_replace tag=tag.name %}">#{{ tag.name }}</a>{% endif %}
{% empty %}<span class="text-muted small">{% trans "No tags yet" %}</span>{% endfor %}
</div>
</div>
</details>
</div>
</aside>