#6828: Fix various mobile UI issues

This commit is contained in:
checktheroads 2021-07-30 00:35:38 -07:00
parent 37c81ecec1
commit 35ecd1494f
14 changed files with 132 additions and 60 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -32,9 +32,7 @@ function handleSearchDropdownClick(event: Event, button: HTMLButtonElement) {
* Initialize Search Bar Elements. * Initialize Search Bar Elements.
*/ */
function initSearchBar() { function initSearchBar() {
for (const dropdown of getElements<HTMLUListElement>( for (const dropdown of getElements<HTMLUListElement>('.search-obj-selector')) {
'div.search-container ul.search-obj-selector',
)) {
for (const button of dropdown.querySelectorAll<HTMLButtonElement>( for (const button of dropdown.querySelectorAll<HTMLButtonElement>(
'li > button.dropdown-item', 'li > button.dropdown-item',
)) { )) {

View File

@ -172,6 +172,34 @@ table td > .progress {
min-width: 6rem; min-width: 6rem;
} }
.nav-mobile {
display: none;
flex-direction: column;
align-items: center;
justify-content: space-between;
width: 100%;
@include media-breakpoint-down(lg) {
display: flex;
}
.nav-mobile-top {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
}
.search-container {
display: flex;
width: 100%;
@include media-breakpoint-down(lg) {
display: none;
}
}
.card > .table.table-flush { .card > .table.table-flush {
margin-bottom: 0; margin-bottom: 0;
overflow: hidden; overflow: hidden;
@ -414,6 +442,18 @@ main.login-container {
} }
} }
.footer {
padding-top: map.get($spacers, 4);
padding-right: 0;
padding-bottom: map.get($spacers, 3);
padding-left: 0;
@include media-breakpoint-down(md) {
// Pad the bottom of the footer on mobile devices to account for mobile browser controls.
margin-bottom: 8rem;
}
}
footer.login-footer { footer.login-footer {
height: 4rem; height: 4rem;
margin-top: auto; margin-top: auto;
@ -658,6 +698,11 @@ div.content-container {
} }
.search-obj-selector { .search-obj-selector {
@include media-breakpoint-down(lg) {
// Limit the height and enable scrolling on mobile devices.
max-height: 75vh;
overflow-y: auto;
}
.dropdown-item, .dropdown-item,
.dropdown-header { .dropdown-header {
font-size: $font-size-sm; font-size: $font-size-sm;
@ -833,7 +878,7 @@ div.field-group:not(:first-of-type) {
label.required { label.required {
font-weight: $font-weight-bold; font-weight: $font-weight-bold;
&::after { &:after {
font-family: 'Material Design Icons'; font-family: 'Material Design Icons';
content: '\f06C4'; content: '\f06C4';
font-weight: normal; font-weight: normal;
@ -853,29 +898,34 @@ div.bulk-buttons {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
margin: $spacer / 2 0; margin: $spacer / 2 0;
// Each group of buttons needs to be contained separately for alignment purposes. This way, you // Each group of buttons needs to be contained separately for alignment purposes. This way, you
// can put some buttons in a group that aligns left, and other buttons in a group that aligns // can put some buttons in a group that aligns left, and other buttons in a group that aligns
// right. // right.
& > div.bulk-button-group { > div.bulk-button-group {
display: flex; display: flex;
flex-wrap: wrap;
&:first-of-type:not(:last-of-type) { &:first-of-type:not(:last-of-type) {
// If there are multiple bulk button groups and this is the first, the first button in the // If there are multiple bulk button groups and this is the first, the first button in the
// group should *not* have left spacing applied, so the button group aligns with the rest // group should *not* have left spacing applied, so the button group aligns with the rest
// of the page elements. // of the page elements.
& > *:first-child { > *:first-child {
margin-left: 0; margin-left: 0;
} }
} }
&:last-of-type:not(:first-of-type) { &:last-of-type:not(:first-of-type) {
// If there are multiple bulk button groups and this is the last, the last button in the // If there are multiple bulk button groups and this is the last, the last button in the
// group should *not* have right spacing applied, so the button group aligns with the rest // group should *not* have right spacing applied, so the button group aligns with the rest
// of the page elements. // of the page elements.
& > *:last-child { > *:last-child {
margin-right: 0; margin-right: 0;
} }
} }
// However, the rest of the buttons should have spacing applied in all directions. // However, the rest of the buttons should have spacing applied in all directions.
& > * { > * {
margin: $spacer / 4; margin: $spacer / 4;
} }
} }

View File

@ -80,12 +80,22 @@ $transition-100ms-ease-in-out: all 0.1s ease-in-out;
border-right: 1px solid $border-color; border-right: 1px solid $border-color;
transition: $transition-100ms-ease-in-out; transition: $transition-100ms-ease-in-out;
// Media fixes for iPhone 5 like resolutions // Media fixes for mobile resolutions.
@include media-breakpoint-down(lg) { @include media-breakpoint-down(lg) {
transform: translateX(-$sidenav-width-closed); transform: translateX(-$sidenav-width-closed);
+ .content-container[class] { + .content-container[class] {
margin-left: 0; margin-left: 0;
} }
.profile-button-container[class] {
display: block;
}
}
.profile-button-container {
display: none;
padding: $sidenav-link-spacing-y $sidenav-link-spacing-x;
} }
+ .content-container { + .content-container {
@ -322,6 +332,7 @@ $transition-100ms-ease-in-out: all 0.1s ease-in-out;
position: absolute; position: absolute;
opacity: 0; opacity: 0;
} }
@include media-breakpoint-down(lg) { @include media-breakpoint-down(lg) {
transform: translateX(0); transform: translateX(0);
} }
@ -331,14 +342,17 @@ $transition-100ms-ease-in-out: all 0.1s ease-in-out;
.sidenav-header { .sidenav-header {
padding: $spacer * 0.5; padding: $spacer * 0.5;
} }
.sidenav-brand { .sidenav-brand {
position: absolute; position: absolute;
opacity: 0; opacity: 0;
transform: translateX(-150%); transform: translateX(-150%);
} }
.sidenav-brand-icon { .sidenav-brand-icon {
opacity: 1; opacity: 1;
} }
.navbar-nav > .nav-item { .navbar-nav > .nav-item {
> .nav-link { > .nav-link {
&:after { &:after {
@ -354,6 +368,7 @@ $transition-100ms-ease-in-out: all 0.1s ease-in-out;
.nav-link-text { .nav-link-text {
opacity: 0; opacity: 0;
} }
@include parent-link() { @include parent-link() {
&.active { &.active {
margin-right: 0; margin-right: 0;

View File

@ -15,7 +15,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta <meta
name="viewport" name="viewport"
content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width, viewport-fit=cover"
/> />
{# Page title #} {# Page title #}

View File

@ -19,17 +19,22 @@
<nav class="navbar navbar-light sticky-top flex-md-nowrap ps-6 p-3 search container-fluid"> <nav class="navbar navbar-light sticky-top flex-md-nowrap ps-6 p-3 search container-fluid">
{# Mobile Navigation #} {# Mobile Navigation #}
<div class="d-md-none w-100 d-flex justify-content-between align-items-center my-3"> <div class="nav-mobile">
<a class="p-2 sidebar-logo d-block d-md-none" href="{% url 'home' %}"> <div class="nav-mobile-top">
<img src="{% static 'netbox_logo.svg' %}" alt="NetBox logo" width="100%" /> <a class="sidebar-logo p-2 d-block" href="{% url 'home' %}">
</a> <img src="{% static 'netbox_logo.svg' %}" alt="NetBox logo" width="75%" />
<button type="button" aria-label="Toggle Navigation" class="navbar-toggler sidenav-toggle-mobile"> </a>
<span class="navbar-toggler-icon"></span> <button type="button" aria-label="Toggle Navigation" class="navbar-toggler sidenav-toggle-mobile">
</button> <span class="navbar-toggler-icon"></span>
</button>
</div>
<div class="d-flex my-1 flex-grow-1 justify-content-center">
{% search_options %}
</div>
</div> </div>
{# Desktop Navigation #} {# Desktop Navigation #}
<div class="d-none d-md-flex w-100 row search-container"> <div class="row search-container">
{# Empty spacer column to ensure search is centered. #} {# Empty spacer column to ensure search is centered. #}
<div class="col-3 d-flex flex-grow-1 ps-0"></div> <div class="col-3 d-flex flex-grow-1 ps-0"></div>
@ -95,12 +100,13 @@
{% endif %} {% endif %}
{# Page footer #} {# Page footer #}
<footer class="footer container-fluid pb-3 pt-4 px-0"> <footer class="footer container-fluid">
<div class="row align-items-center justify-content-between mx-0"> <div class="row align-items-center justify-content-between mx-0">
{# Docs & Community Links #} {# Docs & Community Links #}
<div class="col"> <div class="col-sm-12 col-md-auto">
<nav class="nav justify-content-start"> <nav class="nav justify-content-center justify-content-lg-start">
{# Documentation #} {# Documentation #}
<a type="button" class="nav-link" href="{% static 'docs/' %}" target="_blank"> <a type="button" class="nav-link" href="{% static 'docs/' %}" target="_blank">
<i title="Docs" class="mdi mdi-book-open-variant text-primary" data-bs-placement="top" data-bs-toggle="tooltip"></i> <i title="Docs" class="mdi mdi-book-open-variant text-primary" data-bs-placement="top" data-bs-toggle="tooltip"></i>
@ -136,7 +142,7 @@
</div> </div>
{# System Info #} {# System Info #}
<div class="col text-end small text-muted"> <div class="col-sm-12 col-md-auto text-center text-lg-end small text-muted">
<span class="fw-light d-block d-md-inline">{% annotated_now %} {% now 'T' %}</span> <span class="fw-light d-block d-md-inline">{% annotated_now %} {% now 'T' %}</span>
<span class="ms-md-3 d-block d-md-inline">{{ settings.HOSTNAME }} (v{{ settings.VERSION }})</span> <span class="ms-md-3 d-block d-md-inline">{{ settings.HOSTNAME }} (v{{ settings.VERSION }})</span>
</div> </div>

View File

@ -5,7 +5,7 @@
<div class="sidenav-header"> <div class="sidenav-header">
{# Brand #} {# Brand #}
{# Full Logo #} {# Full Logo #}
<a class="sidenav-brand" href="/"> <a class="sidenav-brand" href="/">
<img src="{% static 'netbox_logo.svg' %}" height="48" class="sidenav-brand-img" alt="NetBox Logo"> <img src="{% static 'netbox_logo.svg' %}" height="48" class="sidenav-brand-img" alt="NetBox Logo">
@ -26,13 +26,16 @@
</div> </div>
<div class="sidenav-inner h-100 mb-auto"> <div class="sidenav-inner h-100 mb-auto">
{# Collapse #} {# Collapse #}
<div class="collapse sidenav-collapse"> <div class="collapse sidenav-collapse">
{# Nav Items #} {# Nav Items #}
{% nav %} {% nav %}
</div> </div>
</div> </div>
<div class="profile-button-container">
{% include 'inc/profile_button.html' %}
</div>
</nav> </nav>

View File

@ -8,13 +8,13 @@
title="Configure Table" title="Configure Table"
data-bs-target="#{{ table_modal }}" data-bs-target="#{{ table_modal }}"
class="btn btn-sm btn-outline-dark" class="btn btn-sm btn-outline-dark"
> >
<i class="mdi mdi-cog"></i> Configure Table <i class="mdi mdi-cog"></i> Configure Table
</button> </button>
</div> </div>
{% endif %} {% endif %}
</div> </div>
<div class="col col-md-4 d-flex noprint table-controls"> <div class="col col-12 col-lg-4 my-3 my-lg-0 d-flex noprint table-controls">
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<input <input
type="text" type="text"

View File

@ -13,7 +13,7 @@
{% for obj_type in results %} {% for obj_type in results %}
<div class="card"> <div class="card">
<h5 class="card-header">{{ obj_type.name|bettertitle }}</h5> <h5 class="card-header">{{ obj_type.name|bettertitle }}</h5>
<div class="card-body"> <div class="card-body table-responsive">
{% render_table obj_type.table 'inc/table.html' %} {% render_table obj_type.table 'inc/table.html' %}
</div> </div>
<div class="card-footer text-end"> <div class="card-footer text-end">

View File

@ -44,6 +44,7 @@
{# Display a disabled link (no permission) #} {# Display a disabled link (no permission) #}
<li class="nav-item disabled"> <li class="nav-item disabled">
<a href="#" class="nav-link disabled" aria-disabled="true" disabled> <a href="#" class="nav-link disabled" aria-disabled="true" disabled>
<i class='mdi mdi-lock small'></i>
<span class="sidenav-normal">{{ item.link_text }}</span> <span class="sidenav-normal">{{ item.link_text }}</span>
</a> </a>
</li> </li>

View File

@ -7,45 +7,44 @@
class="form-control" class="form-control"
value="{{ request.GET.q }}" value="{{ request.GET.q }}"
/> />
<input name="obj_type" hidden type="text" class="search-obj-type" /> <input name="obj_type" hidden type="text" class="search-obj-type" />
<span class="input-group-text search-obj-selected">All Objects</span> <span class="input-group-text search-obj-selected">All Objects</span>
<button
type="button" <button type="button" aria-expanded="false" data-bs-toggle="dropdown" class="btn btn-outline-secondary dropdown-toggle">
aria-expanded="false"
data-bs-toggle="dropdown"
class="btn btn-outline-secondary dropdown-toggle"
>
<i class="mdi mdi-filter-variant"></i> <i class="mdi mdi-filter-variant"></i>
</button> </button>
<ul class="dropdown-menu dropdown-menu-end search-obj-selector"> <ul class="dropdown-menu dropdown-menu-end search-obj-selector">
{% for option in options %} {% if option.items|length == 0 %} {% for option in options %}
<li> {% if option.items|length == 0 %}
<button <li>
class="dropdown-item" <button class="dropdown-item" type="button" data-search-value="{{ option.value }}">
type="button" {{ option.label }}
data-search-value="{{ option.value }}" </button>
> </li>
{{ option.label }} {% else %}
</button> <li><h6 class="dropdown-header">{{ option.label }}</h6></li>
</li> {% endif %}
{% else %}
<li><h6 class="dropdown-header">{{ option.label }}</h6></li> {% for item in option.items %}
{% endif %} {% for item in option.items %} <li>
<li> <button class="dropdown-item" type="button" data-search-value="{{ item.value }}">
<button {{ item.label }}
class="dropdown-item" </button>
type="button" </li>
data-search-value="{{ item.value }}" {% endfor %}
>
{{ item.label }} {% if forloop.counter != options|length %}
</button> <li><hr class="dropdown-divider" /></li>
</li> {% endif %}
{% endfor %} {% if forloop.counter != options|length %} {% endfor %}
<li><hr class="dropdown-divider" /></li>
{% endif %} {% endfor %}
</ul> </ul>
<button class="btn btn-primary" type="submit"> <button class="btn btn-primary" type="submit">
<i class="mdi mdi-magnify"></i> <i class="mdi mdi-magnify"></i>
</button> </button>
</form> </form>