Single menu on mobile

This commit is contained in:
Mira Weller
2026-09-25 23:38:04 +02:00
parent 67a2be4820
commit 7a4c05ffe5
13 changed files with 276 additions and 131 deletions
@@ -97,6 +97,17 @@
{% if request.organizer %}data-organizer="{{ request.organizer.slug }}"{% endif %}
{% if request.event %}data-event="{{ request.event.slug }}"{% endif %}
data-select2-locale="{{ select2locale }}" data-longdateformat="{{ js_long_date_format }}" class="nojs">
{% if request.event %}
{% if has_domain and not request.event.live %}
<form action="{% eventurl request.event "presale:event.auth" %}" method="post"
target="_blank" id="gotoshopform">
<input type="hidden" value="{{ new_session }}" name="session">
</form>
{% else %}
<form action="{% eventurl request.event "presale:event.index" %}" method="get"
target="_blank" id="gotoshopform"></form>
{% endif %}
{% endif %}
<div id="wrapper">
<nav class="navbar navbar-inverse navbar-static-top" role="navigation">
<div class="navbar-header">
@@ -108,24 +119,20 @@
</a>
{% if request.organizer %}
<a class="navbar-context-breadcrumb" href="{% url "control:organizer" organizer=request.organizer.slug %}">
{{ request.organizer.slug }}
{{ request.organizer.slug }} {#<span class="fa fa-users"></span>#}
</a>
{% endif %}
{% if request.event %}
<a class="navbar-context-breadcrumb" href="{% url "control:event.index" organizer=request.organizer.slug event=request.event.slug %}">
{{ request.event.slug }}
{{ request.event.slug }} {#<span class="fa fa-calendar"></span>#}
</a>
{% endif %}
{% if request.event %}
{% if has_domain and not request.event.live %}
<form action="{% eventurl request.event "presale:event.auth" %}" method="post"
target="_blank" class="mobile-navbar-view-form visible-xs-block">
<input type="hidden" value="{{ new_session }}" name="session">
<button type="submit" class="btn btn-link navbar-toggle">
<i class="fa fa-eye"></i>
</button>
</form>
<button type="submit" class="btn btn-link navbar-toggle" form="gotoshopform">
<i class="fa fa-eye"></i>
</button>
{% else %}
<a href="{% eventurl request.event "presale:event.index" %}" title="{% trans "Go to shop" %}"
target="_blank" class="navbar-toggle mobile-navbar-view-link">
@@ -140,7 +147,7 @@
{% endif %}
<button type="button" class="navbar-toggle"
data-toggle="collapse" data-target=".navbar-nav-collapse">
data-toggle="dropdown" data-target="#main_sidebar">
<span class="sr-only">{% trans "Toggle navigation" %}</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
@@ -149,15 +156,13 @@
</div>
<ul class="nav navbar-nav navbar-top-links navbar-left flip hidden-xs">
<!--
{% if request.event %}
<li>
{% if has_domain and not request.event.live %}
<form action="{% eventurl request.event "presale:event.auth" %}" method="post" target="_blank">
<input type="hidden" value="{{ new_session }}" name="session">
<button type="submit" class="btn btn-link" id="button-shop">
<i class="fa fa-eye"></i> {% trans "Go to shop" %}
</button>
</form>
<button type="submit" class="btn btn-link" id="button-shop" form="gotoshopform">
<i class="fa fa-eye"></i> {% trans "Go to shop" %}
</button>
{% else %}
<a href="{% eventurl request.event "presale:event.index" %}" title="{% trans "Go to shop" %}" target="_blank">
<i class="fa fa-eye"></i> {% trans "Go to shop" %}
@@ -171,8 +176,9 @@
</a>
</li>
{% endif %}
-->
</ul>
<ul class="nav navbar-nav navbar-top-links navbar-right flip">
<ul class="nav navbar-nav navbar-top-links navbar-right flip hidden-xs">
{% for nav in nav_topbar %}
<li {% if nav.children %}class="dropdown"{% endif %}>
<a href="{{ nav.url }}" title="{{ nav.title }}" {% if nav.active %}class="active"{% endif %}
@@ -267,8 +273,110 @@
</ul>
</li>
</ul>
<div class="navbar-default sidebar" role="navigation">
<div class="sidebar-nav navbar-nav-collapse navbar-collapse">
<div class="navbar-default sidebar" id="main_sidebar" role="navigation" data-dropdown-keep-on-click-inside="true">
<div class="sidebar-nav">
<ul class="nav visible-xs">
{% for nav in nav_topbar %}
<li {% if nav.children %}{% endif %}>
{% if nav.children %}<details>
<summary>{% endif %}
<a {% if not nav.children %}href="{{ nav.url }}"{% endif %} title="{{ nav.title }}" {% if nav.active %}class="active"{% endif %}
{% if nav.external %}target="_blank"{% endif %}
>
{% if nav.icon %}
{% if "<svg" in nav.icon %}
{{ nav.icon|safe }}
{% else %}
<span class="fa fa-fw fa-{{ nav.icon }}"></span>
{% endif %}
<span class="visible-xs-inline">{{ nav.label|default:nav.title }}</span>
{% else %}
{{ nav.label }}
{% endif %}
{% if nav.children %}<span class="fa arrow pull-right"></span>{% endif %}
</a>
{% if nav.children %}
</summary>
<ul class="nav nav-second-level" role="menu">
{% for item in nav.children %}
{% if item.separator %}
<li role="separator" class="divider"></li>
{% else %}
<li>
<a {% if item.url %}href="{{ item.url }}"{% endif %}
{% if item.external %}target="_blank"{% endif %}
{% if item.active %}class="active"{% endif %}>
{% if item.icon %}
<span class="fa fa-fw fa-{{ item.icon }}"></span>
{% endif %}
{{ item.label|safe }}</a>
</li>
{% endif %}
{% endfor %}
</ul>
</details>
{% endif %}
</li>
{% endfor %}
{% if request.user.is_staff and not staff_session %}
<li>
<form action="{% url 'control:user.sudo' %}?next={{ request.path|add:"?"|add:request.GET.urlencode|urlencode }}" method="post">
{% csrf_token %}
<button type="submit" class="btn-block text-left" id="button-sudo">
<i class="fa fa-fw fa-id-card"></i> {% trans "Admin mode" %}
</button>
</form>
</li>
{% elif request.user.is_staff and staff_session %}
<li>
<a href="{% url 'control:user.sudo.stop' %}" class="danger admin-only">
<i class="fa fa-fw fa-id-card"></i> {% trans "End admin session" %}
</a>
</li>
{% endif %}
{% if warning_update_available %}
<li>
<a href="{% url 'control:global.update' %}" class="danger">
<i class="fa fa-fw fa-bell"></i>
</a>
</li>
{% endif %}
<li>
<details>
<summary><a>
<i class="fa fa-fw fa-user"></i> {{ request.user.get_full_name }}
<span class="fa arrow pull-right"></span>
</a></summary>
<ul class="nav nav-second-level" role="menu">
<li>
<a href="{% url 'control:organizers' %}">
<i class="fa fa-fw fa-group"></i> {% trans "My organizer accounts" %}
</a>
</li>
<li>
<a href="{% url 'control:user.settings' %}">
<i class="fa fa-fw fa-cog"></i> {% trans "Account settings" %}
</a>
</li>
<li role="separator" class="divider"></li>
<li>
<a href="{% url 'control:auth.logout' %}" title="{% trans "Log out" %}">
<i class="fa fa-fw fa-sign-out"></i> {% trans "Log out" %}
</a>
</li>
</ul>
</details>
</li>
</ul>
<div class="side-header">
{% if request.event or request.organizer %}
{% trans "Switch organizer or event" %}
{% else %}
{% trans "Go to organizer or event" %}
{% endif %}
</div>
<div class="dropdown context-selector">
{% if request.event %}
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
@@ -296,11 +404,11 @@
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-user fa-stack-1x fa-inverse"></i>
<i class="fa fa-search fa-stack-1x fa-inverse"></i>
</span>
<div class="context-indicator">
<span class="context-name">{{ request.user }}</span>
<span class="context-name">{% trans "Search..." %}</span>
</div>
<span class="caret"></span></a>
{% endif %}
@@ -320,11 +428,11 @@
</div>
<div class="side-header">
{% if request.event %}
{% trans "Global and user menu" %}
{% trans "Event menu" %}
{% elif request.organizer %}
{% trans "Organizer account menu" %}
{% else %}
{% trans "Event menu" %}
{% trans "Personal account menu" %}
{% endif %}
</div>
<ul class="nav" id="side-menu">
@@ -56,7 +56,7 @@
</p>
</div>
{% else %}
<span class="table-responsive">
<div class="table-responsive">
<table class="table table-hover table-quotas">
<thead>
<tr>
@@ -7,6 +7,15 @@
{% block title %}{{ request.event.name }}{% endblock %}
{% block content %}
<h1>
{% if has_domain and not request.event.live %}
<button type="submit" class="btn btn-lg btn-primary pull-right hidden-xs" id="button-shop" form="gotoshopform">
<i class="fa fa-eye"></i> {% trans "Go to shop" %}
</button>
{% else %}
<a href="{% eventurl request.event "presale:event.index" %}" class="btn btn-lg btn-primary pull-right hidden-xs" target="_blank">
<i class="fa fa-eye"></i> {% trans "Go to shop" %}
</a>
{% endif %}
{{ request.event.name }}
<small>
{% if request.event.has_subevents %}
@@ -17,6 +26,17 @@
<span id="warnings_loading" class="fa fa-cog fa-spin"></span>
</small>
</h1>
<p class="visible-xs">
{% if has_domain and not request.event.live %}
<button type="submit" class="btn btn-lg btn-primary" id="button-shop" form="gotoshopform">
<i class="fa fa-eye"></i> {% trans "Go to shop" %}
</button>
{% else %}
<a href="{% eventurl request.event "presale:event.index" %}" class="btn btn-lg btn-primary" target="_blank">
<i class="fa fa-eye"></i> {% trans "Go to shop" %}
</a>
{% endif %}
</p>
<div class="helper-space-below">
{% trans "Shop URL:" %}
<span id="shop_url" class="text-muted">{% abseventurl request.event "presale:event.index" %}</span>
@@ -21,10 +21,10 @@
</a>
</div>
{% else %}
<div class="panel panel-default">
<div class="panel-heading">
<details class="panel panel-default" data-stateful="events_index_filter">
<summary class="panel-heading">
<h3 class="panel-title">{% trans "Filter" %}</h3>
</div>
</summary>
<form class="panel-body filter-form" action="" method="get">
<div class="row">
<div class="col-md-3 col-sm-6 col-xs-12">
@@ -55,14 +55,14 @@
</button>
</div>
</form>
</div>
</details>
<p>
<a href="{% url "control:events.add" %}" class="btn btn-default">
<span class="fa fa-plus"></span>
{% trans "Create a new event" %}
</a>
</p>
<table class="table table-condensed table-hover">
<div class="table-responsive"><table class="table table-condensed table-hover">
<thead>
<tr>
<th>
@@ -186,7 +186,7 @@
</tr>
{% endfor %}
</tbody>
</table>
</table></div>
{% include "pretixcontrol/pagination.html" %}
{% endif %}
{% endblock %}
@@ -2,10 +2,19 @@
{% load i18n %}
{% load bootstrap3 %}
{% load static %}
{% load eventurl %}
{% block inner %}
<h1>
<a href="{% eventurl request.organizer "presale:organizer.index" %}" class="btn btn-lg btn-default pull-right hidden-xs" target="_blank">
<i class="fa fa-eye"></i> {% trans "Public profile" %}
</a>
{% blocktrans with name=request.organizer.name %}Organizer: {{ name }}{% endblocktrans %}
</h1>
<p class="visible-xs">
<a href="{% eventurl request.organizer "presale:organizer.index" %}" class="btn btn-lg btn-default" target="_blank">
<i class="fa fa-eye"></i> {% trans "Public profile" %}
</a>
</p>
{% if events|length == 0 and not filter_form.filtered %}
{% if "organizer.events:create" in request.orgapermset %}
<p>
@@ -19,10 +28,10 @@
<em>{% trans "You currently do not have access to any events." %}</em>
</p>
{% else %}
<div class="panel panel-default">
<div class="panel-heading">
<details class="panel panel-default" open data-stateful="organizer_detail_filter">
<summary class="panel-heading">
<h3 class="panel-title">{% trans "Filter" %}</h3>
</div>
</summary>
<form class="panel-body filter-form" action="" method="get">
<div class="row">
<div class="col-md-3 col-sm-6 col-xs-12">
@@ -50,7 +59,7 @@
</button>
</div>
</form>
</div>
</details>
{% if "organizer.events:create" in request.orgapermset %}
<p>
<a href="{% url "control:events.add" %}?organizer={{ request.organizer.slug }}" class="btn btn-primary">
@@ -59,7 +68,7 @@
</a>
</p>
{% endif %}
<table class="table table-condensed table-hover">
<div class="table-responsive"><table class="table table-condensed table-hover">
<thead>
<tr>
<th>{% trans "Event name" %}</th>
@@ -157,7 +166,7 @@
</tr>
{% endfor %}
</tbody>
</table>
</table></div>
{% include "pretixcontrol/pagination.html" %}
{% endif %}
{% endblock %}
@@ -5,14 +5,16 @@
{% load eventurl %}
{% block title %}{% trans "Organizers" %}{% endblock %}
{% block content %}
<h1>{% trans "Organizers" %}</h1>
<h1>
{% trans "Organizers" %}
</h1>
<p>{% trans "The list below shows all organizer accounts you have administrative access to." %}</p>
<div class="panel panel-default">
<div class="panel-heading">
<details class="panel panel-default" open data-stateful="organizers_index_filter">
<summary class="panel-heading">
<h3 class="panel-title">
{% trans "Filter" %}
</h3>
</div>
</summary>
<form class="panel-body filter-form" action="" method="get">
<div class="row">
<div class="col-md-12 col-sm-6 col-xs-12">
@@ -26,7 +28,7 @@
</button>
</div>
</form>
</div>
</details>
{% if staff_session %}
<p>
<a href="{% url "control:organizers.add" %}" class="btn btn-default admin-only">
@@ -35,9 +37,9 @@
</a>
</p>
{% endif %}
<table class="table table-condensed table-hover">
<thead>
<tr>
<div class="table-responsive"><table class="table table-condensed table-hover">
<thead>
<tr>
<th>
{% trans "Organizer name" %}
<a href="?{% url_replace request 'ordering' '-name' %}"><i class="fa fa-caret-down"></i></a>
@@ -48,18 +50,18 @@
<a href="?{% url_replace request 'ordering' '-slug' %}"><i class="fa fa-caret-down"></i></a>
<a href="?{% url_replace request 'ordering' 'slug' %}"><i class="fa fa-caret-up"></i></a>
</th>
</tr>
</thead>
<tbody>
{% for o in organizers %}
<tr>
<td><strong>
</tr>
</thead>
<tbody>
{% for o in organizers %}
<tr>
<td><strong>
<a href="{% url "control:organizer" organizer=o.slug %}">{{ o.name }}</a>
</strong></td>
<td>{{ o.slug }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</tr>
{% endfor %}
</tbody>
</table></div>
{% include "pretixcontrol/pagination.html" %}
{% endblock %}
+2 -12
View File
@@ -773,7 +773,6 @@ if (typeof jQuery === 'undefined') {
// DROPDOWN CLASS DEFINITION
// =========================
var backdrop = '.dropdown-backdrop'
var toggle = '[data-toggle="dropdown"]'
var Dropdown = function (element) {
$(element).on('click.bs.dropdown', this.toggle)
@@ -796,7 +795,6 @@ if (typeof jQuery === 'undefined') {
function clearMenus(e) {
if (e && e.which === 3) return
$(backdrop).remove()
$(toggle).each(function () {
var $this = $(this)
var $parent = getParent($this)
@@ -804,7 +802,7 @@ if (typeof jQuery === 'undefined') {
if (!$parent.hasClass('open')) return
if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return
if (e && e.type == 'click' && ($parent.attr('data-dropdown-keep-on-click-inside') || /input|textarea/i.test(e.target.tagName)) && $.contains($parent[0], e.target)) return
$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
@@ -823,17 +821,9 @@ if (typeof jQuery === 'undefined') {
var $parent = getParent($this)
var isActive = $parent.hasClass('open')
clearMenus()
clearMenus(e)
if (!isActive) {
if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
// if mobile we use a backdrop because click events don't delegate
$(document.createElement('div'))
.addClass('dropdown-backdrop')
.insertAfter($(this))
.on('click', clearMenus)
}
var relatedTarget = { relatedTarget: this }
$parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
@@ -233,13 +233,36 @@
line-height: $line-height-computed;
}
@media (max-width: $grid-float-breakpoint-max) {
// Dropdowns get custom display when collapsed
.open .dropdown-menu {
position: fixed;
display: flex;
margin: 0;
max-width: 100%;
> li {
min-width: 0;
> a {
padding-top: $navbar-padding-vertical;
padding-bottom: $navbar-padding-vertical;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
max-width: 100%;
flex: 0 1 10px;
min-width: 0;
}
}
}
@media (max-width: $grid-float-breakpoint-max) {
.navbar-default.sidebar > .sidebar-nav {
display: none;
}
// Dropdowns get custom display when collapsed
.navbar-nav .open, .navbar-default.sidebar.open {
& > .dropdown-menu, & > .sidebar-nav {
position: fixed; display: block;
float: none;
width: auto;
width: 80%; top: 0; left: inherit; right: 0; bottom: 0; background: white;
width: 80%; top: 0; left: inherit; right: 0; bottom: 0; background: white; z-index: 2;
margin-top: 0;
padding: 50px 0 10px 0;
overflow-y: auto; overflow-x: hidden;
@@ -265,25 +288,8 @@
}
}
}
}
display: flex;
//overflow: hidden;
margin: 0;
max-width: 100%;
> li {
//overflow: hidden;
min-width: 0;
> a {
padding-top: $navbar-padding-vertical;
padding-bottom: $navbar-padding-vertical;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
max-width: 100%;
flex: 0 1 10px;
min-width: 0;
&::before {
position: fixed; content: " "; background: rgba(0,0,0,0.2); top: 0; left: 0; bottom: 0; right: 0; z-index: -1;
}
}
}
@@ -16,7 +16,7 @@
position: relative;
display: block;
> a {
> a, > details > summary > a, > form > button {
position: relative;
display: block;
padding: $nav-link-padding;
+37 -34
View File
@@ -55,44 +55,17 @@ setup_collapsible_details = function (el) {
})
let isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]'
el.find('details summary').click(function (e) {
if (this.tagName !== 'A' && $(e.target).closest('a').length > 0) {
return true
}
let $details = $(this).closest('details')
let isOpen = $details.prop('open')
let $detailsNotSummary = $details.children(':not(summary)')
if ($detailsNotSummary.is(':animated')) {
e.preventDefault()
return false
}
if (isOpen) {
$details.removeClass('details-open')
$detailsNotSummary.stop().show().slideUp(500, function () {
$details.prop('open', false)
})
} else {
$detailsNotSummary.stop().hide()
$details.prop('open', true)
$details.addClass('details-open')
$detailsNotSummary.slideDown()
}
e.preventDefault()
return false
}).keyup(function (event) {
if (32 == event.keyCode || (13 == event.keyCode && !isOpera)) {
// Space or Enter is pressed — trigger the `click` event on the `summary` element
// Opera already seems to trigger the `click` event when Enter is pressed
event.preventDefault()
$(this).click()
}
})
$('details').each(function () {
let $details = $(this),
$detailsSummary = $('summary', $details).first(),
$detailsNotSummary = $details.children(':not(summary)')
$details.prop('open', typeof $details.attr('open') == 'string')
var open = typeof $details.attr('open') == 'string'
var stateKey = null
if ($details.attr('data-stateful')) {
stateKey = 'ui-details-state-' + $details.attr('data-stateful')
if (localStorage[stateKey]) open = localStorage[stateKey] === 'open'
}
$details.prop('open', open)
if (!$details.prop('open')) {
if ($details.find('.has-error, .alert-danger').length) {
$details.addClass('details-open')
@@ -108,6 +81,36 @@ setup_collapsible_details = function (el) {
'aria-controls': $details.attr('id')
}).prop('tabIndex', 0).bind('selectstart dragstart mousedown', function () {
return false
}).click(function (e) {
if (this.tagName !== 'A' && $(e.target).closest('a[href]').length > 0) {
return true
}
let isOpen = $details.prop('open')
if ($detailsNotSummary.is(':animated')) {
e.preventDefault()
return false
}
if (isOpen) {
$details.removeClass('details-open')
$detailsNotSummary.stop().show().slideUp(500, function () {
$details.prop('open', false)
})
} else {
$detailsNotSummary.stop().hide()
$details.prop('open', true)
$details.addClass('details-open')
$detailsNotSummary.slideDown()
}
if (stateKey) localStorage[stateKey] = isOpen ? 'closed' : 'open'
e.preventDefault()
return false
}).keyup(function (event) {
if (32 == event.keyCode || (13 == event.keyCode && !isOpera)) {
// Space or Enter is pressed — trigger the `click` event on the `summary` element
// Opera already seems to trigger the `click` event when Enter is pressed
event.preventDefault()
$(this).click()
}
})
})
@@ -12,10 +12,10 @@ $(window).bind('load resize', function () {
let topOffset = 50,
width = (this.window.innerWidth > 0) ? this.window.innerWidth : this.screen.width
if (width < 768) {
$('div.navbar-collapse').addClass('collapse')
//$('div.navbar-collapse').addClass('collapse')
topOffset = 100 // 2-row-menu
} else {
$('div.navbar-collapse').removeClass('collapse')
//$('div.navbar-collapse').removeClass('collapse')
}
let height = ((this.window.innerHeight > 0) ? this.window.innerHeight : this.screen.height) - 1
@@ -119,6 +119,7 @@ body {
a.has-children {
margin-right: 40px;
}
details[open] summary .fa.arrow { transform: rotate(-90deg); }
}
.sidebar .fa.arrow {
@@ -197,13 +198,18 @@ body {
}
.side-header {
padding: 25px 15px 5px;
border-bottom: 2px solid #eee;
color: #aaa;
font-weight: bold;
border-bottom: 2px solid #e7e7e7;
color: #adadad;
font-weight: bold; font-size: 90%;
text-transform: uppercase;
}
@media(min-width:768px) {
.side-header {
//color: #ddd; transition: color 0.5s ease-in-out;
}
.sidebar:hover .side-header {
//color: #aaa;
}
.sidebar {
z-index: 1;
position: absolute;
@@ -582,6 +582,7 @@ table td ul:last-child {
transparent 12px,
transparent 22px,
rgba(0, 0, 0, .1) 24px);
color: white;
}
.quotabox {