mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-28 03:16:25 -06:00
#6828: Fix various mobile UI issues
This commit is contained in:
parent
37c81ecec1
commit
35ecd1494f
BIN
netbox/project-static/dist/netbox-dark.css
vendored
BIN
netbox/project-static/dist/netbox-dark.css
vendored
Binary file not shown.
BIN
netbox/project-static/dist/netbox-light.css
vendored
BIN
netbox/project-static/dist/netbox-light.css
vendored
Binary file not shown.
BIN
netbox/project-static/dist/netbox.js
vendored
BIN
netbox/project-static/dist/netbox.js
vendored
Binary file not shown.
BIN
netbox/project-static/dist/netbox.js.map
vendored
BIN
netbox/project-static/dist/netbox.js.map
vendored
Binary file not shown.
@ -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',
|
||||||
)) {
|
)) {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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 #}
|
||||||
|
@ -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>
|
||||||
|
@ -35,4 +35,7 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="profile-button-container">
|
||||||
|
{% include 'inc/profile_button.html' %}
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
@ -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"
|
||||||
|
@ -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">
|
||||||
|
@ -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>
|
||||||
|
@ -7,43 +7,42 @@
|
|||||||
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>
|
||||||
|
Loading…
Reference in New Issue
Block a user