Migrate to collapsed sidebar layout

This commit is contained in:
checktheroads 2021-07-26 14:46:05 -07:00
parent 51c1f4b214
commit 2d32aeb972
9 changed files with 353 additions and 142 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -359,6 +359,8 @@ div.title-container {
nav.search {
background-color: var(--nbx-body-bg);
// Don't overtake dropdowns
z-index: 999;
form button.dropdown-toggle {
border-color: $input-border-color;
font-weight: $input-group-addon-font-weight;
@ -374,6 +376,157 @@ nav.search {
}
}
main.layout {
display: flex;
flex-wrap: nowrap;
height: 100vh;
height: -webkit-fill-available;
max-height: 100vh;
overflow-x: auto;
overflow-y: hidden;
.sidenav {
width: 4.5rem;
background-color: var(--nbx-sidebar-bg);
border-right: 1px solid $border-color;
// TODO: Figure out how to make the menu vertically scroll properly.
// overflow-x: hidden;
// overflow-y: auto;
padding-bottom: 1.5rem;
z-index: 5000;
& {
-ms-overflow-style: none; // Internet Explorer 10+
scrollbar-width: none; // Firefox
}
&::-webkit-scrollbar {
display: none; // Safari and Chrome
}
.nav-link {
font-size: $font-size-lg;
border-radius: unset;
transition: color 0s;
@include media-breakpoint-up(sm) {
font-size: $font-size-sm;
}
@include media-breakpoint-up(md) {
font-size: $font-size-base;
}
@include media-breakpoint-up(lg) {
font-size: $font-size-lg;
}
@include media-breakpoint-up(xl) {
font-size: $h4-font-size;
}
&:hover:not(.active) {
background-color: $accordion-button-active-bg;
}
&:after {
display: none;
}
}
.nav-item {
position: relative;
.nav-label {
opacity: 0;
z-index: 0;
height: 100%;
display: flex;
padding: $spacer;
position: absolute;
align-items: center;
margin-left: 4.5rem;
pointer-events: none;
justify-content: flex-start;
font-weight: $font-weight-bold;
transition: opacity 0.1s ease-in-out, transform 0.12s ease-in-out, z-index 0.12s ease-in-out;
transform: translateX(-50px);
background-color: $accordion-button-active-bg;
color: $nav-link-color;
border-top-right-radius: $border-radius;
border-bottom-right-radius: $border-radius;
[data-netbox-color-mode='dark'] &[class] {
color: shade-color($primary, 75%);
}
}
&:hover .nav-label {
transform: translateX(-1px);
z-index: 99;
opacity: 1;
box-shadow: 1rem 0 2rem rgba($black, 0.15);
}
&:hover .nav-link {
color: $nav-link-color;
[data-netbox-color-mode='dark'] &[class] {
color: shade-color($primary, 50%);
}
}
}
.sidenav-logo {
position: relative;
& .sidenav-logo-reveal {
opacity: 0;
z-index: 0;
height: 100%;
width: max-content;
display: flex;
padding: $spacer;
position: absolute;
align-items: center;
justify-content: flex-start;
font-weight: $font-weight-bold;
transition: opacity 0.1s ease-in-out, transform 0.12s ease-in-out, z-index 0.12s ease-in-out;
transform: translateX(-100%);
background-color: var(--nbx-sidebar-bg);
border-bottom-right-radius: $border-radius;
}
&:hover .sidenav-logo-reveal {
transform: translateX(-1px);
z-index: 2000;
opacity: 1;
}
}
.dropdown {
.dropdown-header {
font-weight: $font-weight-bold;
text-transform: uppercase;
color: var(--nbx-sidebar-title-color);
font-size: $font-size-sm;
}
.dropdown-item-group {
display: inline-flex;
width: 100%;
justify-content: space-between;
align-items: center;
padding-right: map.get($spacers, 3);
&.disabled {
cursor: not-allowed;
}
}
.dropdown-item {
padding-left: map.get($spacers, 4);
border-top-right-radius: $border-radius;
border-bottom-right-radius: $border-radius;
}
}
}
}
main.login-container {
display: flex;
height: calc(100vh - 4rem);
@ -425,7 +578,6 @@ h3.accordion-item-title,
h4.accordion-item-title,
h5.accordion-item-title,
h6.accordion-item-title {
// padding: 0 0.5rem;
padding: 0.25rem 0.5rem;
font-weight: $font-weight-bold;
text-transform: uppercase;
@ -474,7 +626,7 @@ li.dropdown-item.dropdown-item-btns {
height: calc(100vh - 48px);
padding-top: 0.5rem;
overflow-x: hidden;
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
overflow-y: auto; // Scrollable contents if viewport is shorter than content.
}
.navbar-brand {
@ -501,11 +653,9 @@ div.content-container {
min-height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
@include media-breakpoint-up(md) {
margin-left: $sidebar-width;
}
width: calc(100% - 4.5rem);
overflow-x: hidden;
overflow-y: auto;
div.content {
flex: 1;
@ -527,7 +677,7 @@ div.content-container {
top: 0;
bottom: 0;
left: 0;
z-index: 100; /* Behind the navbar */
z-index: 100; // Behind the navbar
border-right: 1px solid $border-color;
background-color: var(--nbx-sidebar-bg);
max-height: 100%;
@ -962,13 +1112,17 @@ html {
// Shade the home page content background-color.
&[data-netbox-path='/'] {
.content-container,
.search {
background-color: $gray-100;
.search
// ,.sidenav-logo-reveal
{
background-color: $gray-100 !important;
}
&[data-netbox-color-mode='dark'] {
.content-container,
.search {
background-color: $darkest;
.search
// ,.sidenav-logo-reveal
{
background-color: $darkest !important;
}
}
}

View File

@ -175,13 +175,11 @@ $accordion-bg: transparent;
$accordion-border-color: $border-color;
$accordion-button-color: $accordion-color;
$accordion-button-bg: $accordion-bg;
$accordion-body-active-bg: rgba($blue-300, 0.2);
$accordion-button-active-bg: rgba($blue-300, 0.25);
$accordion-button-active-color: $gray-300;
$accordion-button-active-bg: shade-color($blue-300, 10%);
$accordion-button-active-color: shade-color($blue-500, 10%);
$accordion-button-focus-border-color: $input-focus-border-color;
$accordion-icon-color: $accordion-color;
$accordion-icon-active-color: $accordion-button-active-color;
$accordion-button-icon: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$accordion-icon-color}'><path fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/></svg>");
$accordion-button-active-icon: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$accordion-icon-active-color}'><path fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/></svg>");

View File

@ -6,84 +6,20 @@
{% load static %}
{% block layout %}
<div class="container-fluid px-0">
<main class="ms-sm-auto">
<main class="layout">
{# Sidebar #}
<nav id="sidebar-menu" class="d-md-block sidebar collapse px-0" data-simplebar>
{# Sidebar content #}
<div class="position-sticky">
{# Logo #}
<div class="py-2">
<a class="sidebar-logo d-none d-md-flex justify-content-center" href="{% url 'home' %}">
<img src="{% static 'netbox_logo.svg' %}" alt="NetBox logo" />
</a>
</div>
<ul class="nav flex-column">
{# Search bar for collapsed menu #}
<div class="d-block d-md-none mx-1 my-3 search-container">
{% search_options %}
</div>
<div class="d-flex d-md-none mx-1 my-3 justify-content-center justify-content-md-end order-last order-md-0">
{% include 'inc/profile_button.html' %}
</div>
{# Navigation menu #}
{% nav %}
</ul>
</div>
{# Sidebar footer #}
<div class="d-flex flex-column container-fluid mt-auto justify-content-end sidebar-bottom">
<nav class="nav">
{# Documentation #}
<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>
</a>
{# REST API #}
<a type="button" class="nav-link" href="{% url 'api-root' %}" target="_blank">
<i title="REST API" class="mdi mdi-code-braces text-primary" data-bs-placement="top" data-bs-toggle="tooltip"></i>
</a>
{# API docs #}
<a type="button" class="nav-link" href="{% url 'api_docs' %}" target="_blank">
<i title="REST API documentation" class="mdi mdi-book text-primary" data-bs-placement="top" data-bs-toggle="tooltip"></i>
</a>
{# GraphQL API #}
{% if settings.GRAPHQL_ENABLED %}
<a type="button" class="nav-link" href="{% url 'graphql' %}" target="_blank">
<i title="GraphQL API" class="mdi mdi-graphql text-primary" data-bs-placement="top" data-bs-toggle="tooltip"></i>
</a>
{% endif %}
{# GitHub #}
<a type="button" class="nav-link" href="https://github.com/netbox-community/netbox" target="_blank">
<i title="Source Code" class="mdi mdi-github text-primary" data-bs-placement="top" data-bs-toggle="tooltip"></i>
</a>
{# NetDev Slack #}
<a type="button" class="nav-link" href="https://netdev.chat/" target="_blank">
<i title="Community" class="mdi mdi-slack text-primary" data-bs-placement="top" data-bs-toggle="tooltip"></i>
</a>
</nav>
</div>
</nav>
{% include 'base/sidebar.html' %}
{# Body #}
<div class="content-container">
{# Top bar #}
<nav class="navbar navbar-light sticky-top flex-md-nowrap p-3 search container-fluid">
<div class="d-md-none w-100 d-flex justify-content-between align-items-center my-3">
<nav class="navbar navbar-light sticky-top flex-md-nowrap ps-6 p-3 search container-fluid">
{# Mobile Navigation #}
<div class="d-md-none w-100 d-flex justify-content-between align-items-center my-3">
<a class="p-2 sidebar-logo d-block d-md-none" href="{% url 'home' %}">
<img src="{% static 'netbox_logo.svg' %}" alt="NetBox logo" width="100%" />
</a>
@ -99,10 +35,25 @@
<span class="navbar-toggler-icon"></span>
</button>
</div>
<div class="d-none d-md-flex w-100 search-container">
{% search_options %}
{% include 'inc/profile_button.html' %}
{# Desktop Navigation #}
<div class="d-none d-md-flex w-100 row search-container">
{# Empty spacer column to ensure search is centered. #}
<div class="col-3 d-flex flex-grow-1 ps-0"></div>
{# Search bar #}
<div class="col-6 d-flex flex-grow-1 justify-content-center">
{% search_options %}
</div>
{# Proflie/login button #}
<div class="col-3 d-flex flex-grow-1 pe-0 justify-content-end">
{% include 'inc/profile_button.html' %}
</div>
</div>
</nav>
{% if settings.BANNER_TOP %}

View File

@ -0,0 +1,93 @@
<div class="dropdown border-top dropend">
<a href="#" class="nav-link dropdown-toggle py-3" data-bs-toggle="dropdown" aria-expanded="false">
<i class="mdi mdi-account"></i>
{% comment %} <span>{{ request.user|truncatechars:"30" }}</span> {% endcomment %}
</a>
<ul class="dropdown-menu shadow">
<li>
<button type="button" class="dropdown-item color-mode-toggle">
<i class="color-mode-icon mdi mdi-lightbulb"></i>&nbsp;
<span class="color-mode-text">Dark Mode</span>
</button>
</li>
<li>
{% if request.user.is_staff %}
<a class="dropdown-item" href="{% url 'admin:index' %}">
<i class="mdi mdi-cog"></i> Admin
</a>
{% endif %}
</li>
<li>
<a class="dropdown-item" href="{% url 'user:profile' %}">
<i class="mdi mdi-account"></i> Profile & Settings
</a>
</li>
<li><hr class="dropdown-divider" /></li>
<li>
<a class="dropdown-item text-danger" href="{% url 'logout' %}">
<i class="mdi mdi-logout-variant"></i> Log Out
</a>
</li>
</ul>
</div>
{% comment %} {% if request.user.is_authenticated %}
<span class="dropdown ms-0 ms-md-3 profile-button">
<button
type="button"
aria-expanded="false"
data-bs-toggle="dropdown"
class="btn btn-outline-secondary dropdown-toggle"
>
<i class="mdi mdi-account"></i>
<span id="navbar_user">{{ request.user|truncatechars:"30" }}</span>
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<button type="button" class="dropdown-item color-mode-toggle">
<i class="color-mode-icon mdi mdi-lightbulb"></i>&nbsp;
<span class="color-mode-text">Dark Mode</span>
</button>
</li>
<li>
{% if request.user.is_staff %}
<a class="dropdown-item" href="{% url 'admin:index' %}">
<i class="mdi mdi-cog"></i> Admin
</a>
{% endif %}
</li>
<li>
<a class="dropdown-item" href="{% url 'user:profile' %}">
<i class="mdi mdi-account"></i> Profile & Settings
</a>
</li>
<li><hr class="dropdown-divider" /></li>
<li>
<a class="dropdown-item text-danger" href="{% url 'logout' %}">
<i class="mdi mdi-logout-variant"></i> Log Out
</a>
</li>
</ul>
</span>
{% else %}
<div class="btn-group ms-0 ms-md-3">
<a
class="btn btn-primary ws-nowrap"
type="button"
href="{% url 'login' %}"
>
<i class="mdi mdi-login-variant"></i> Log In
</a>
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown">
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<button class="dropdown-item color-mode-toggle">
<i class="color-mode-icon mdi mdi-lightbulb"></i>&nbsp;
<span class="color-mode-text">Dark Mode</span>
</button>
</li>
</ul>
</div>
{% endif %} {% endcomment %}

View File

@ -0,0 +1,24 @@
{% load nav %}
{% load static %}
<div class="d-flex flex-column flex-shrink-0 sidenav">
{# Logo Container #}
<div class="sidenav-logo">
{# Full logo, hidden until icon is hovered. #}
<a class="sidenav-logo-reveal" href="{% url 'home' %}">
<img src="{% static 'netbox_logo.svg' %}" alt="NetBox Logo" height="39px" />
</a>
{# Logo Icon #}
<a href="/" class="sidenav-logo-icon d-block p-3 link-dark text-decoration-none">
<img src="{% static 'netbox_icon.svg' %}" />
</a>
</div>
{# Navigation Items #}
{% nav %}
</div>

View File

@ -1,43 +1,39 @@
{% load helpers %}
<div id="sidenav-accordion" class="accordion accordion-flush nav-item">
<ul class="nav nav-pills nav-flush flex-column mb-auto text-center">
{% comment %} <li class="nav-item">
<a href="/" class="nav-link active py-3" aria-current="page" title="Home" data-bs-toggle="tooltip" data-bs-placement="right">
<i class="mdi mdi-home"></i>
</a>
</li> {% endcomment %}
{% for menu in nav_items %}
<li class="nav-item sidenav-dropdown">
<div class="nav-label">
{{ menu.label }}
</div>
<div class="dropdown dropend" title="{{ menu.label }}">
<a href="#" class="nav-link py-3 dropdown-toggle" id="menu{{ menu.label }}" data-bs-toggle="dropdown" aria-expanded="false">
<i class="{{ menu.icon_class }}"></i>
</a>
{# Main Collapsible Menu #}
<div class="accordion-item">
<a
href="#"
role="button"
aria-expanded="true"
data-bs-toggle="collapse"
data-bs-target="#{{ menu.label|lower }}"
class="d-flex justify-content-between align-items-center accordion-button nav-link collapsed">
<span class="fw-bold sidebar-nav-link">
<i class="{{ menu.icon_class }} me-1 opacity-50"></i>
{{ menu.label }}
</span>
</a>
<div id="{{ menu.label|lower }}" class="accordion-collapse collapse" data-bs-parent="#sidenav-accordion">
<div class="multi-level accordion-body px-0">
<ul class="dropdown-menu shadow" aria-labelledby="menu{{ menu.label }}">
<li><h4 class="dropdown-header text-dark">{{ menu.label }}</h4>{{ menu.has_link }}</li>
<hr class="dropdown-divider" />
{% for group in menu.groups %}
{# Within each main menu, there are groups of menu items #}
<div class="flex-column nav">
<h6 class="accordion-item-title">{{ group.label }}</h6>
{% for item in group.items %}
{# Each Menu Item #}
<div class="nav-item d-flex justify-content-between align-items-center">
{# Menu Link with Text #}
{% if request.user|has_perms:item.permissions %}
<a class="nav-link flex-grow-1" href="{% url item.link %}">
{{ item.link_text }}
</a>
{% if group.label != menu.label %}
<li><h6 class="dropdown-header">{{ group.label }}</h6></li>
{% endif %}
{% for item in group.items %}
{# Each Menu Item #}
{% if request.user|has_perms:item.permissions %}
<li class="dropdown-item-group">
<a class="dropdown-item" href="{% url item.link %}">{{ item.link_text }}</a>
{# Menu item buttons (if any) #}
{% if item.buttons %}
<div class="btn-group ps-1">
@ -50,30 +46,25 @@
{% endfor %}
</div>
{% endif %}
{% else %}
{# Display a disabled link (no permission) #}
<a class="nav-link flex-grow-1 disabled">
</li>
{% else %}
{# Display a disabled link (no permission) #}
<li class="dropdown-item-group disabled">
<a class="dropdown-item disabled" href="#" aria-disabled="true" disabled>
{{ item.link_text }}
</a>
{% endif %}
</div>
{% endfor %}
</div>
</li>
{% endif %}
{% endfor %}
{# Show a divider if not the last group #}
{% if forloop.counter != menu.groups|length %}
<hr class="dropdown-divider my-2" />
{% endif %}
{% endfor %}
</ul>
</div>
</div>
</div>
</li>
{% endfor %}
</div>
</ul>

View File

@ -1,4 +1,4 @@
<form class="input-group w-100" action="{% url 'search' %}" method="get">
<form class="input-group" action="{% url 'search' %}" method="get">
<input
name="q"
type="text"