Procházet zdrojové kódy

Migrate to collapsed sidebar layout

checktheroads před 4 roky
rodič
revize
2d32aeb972

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
netbox/project-static/dist/netbox-dark.css


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
netbox/project-static/dist/netbox-light.css


+ 166 - 12
netbox/project-static/styles/netbox.scss

@@ -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;
       }
     }
   }

+ 2 - 4
netbox/project-static/styles/theme-dark.scss

@@ -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>");
 

+ 25 - 74
netbox/templates/base/layout.html

@@ -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 %}

+ 93 - 0
netbox/templates/base/profile_button.html

@@ -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 %}

+ 24 - 0
netbox/templates/base/sidebar.html

@@ -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>

+ 40 - 49
netbox/utilities/templates/navigation/nav_items.html

@@ -1,43 +1,39 @@
 {% load helpers %}
 
-<div id="sidenav-accordion" class="accordion accordion-flush nav-item">
-  {% for menu in nav_items %}
+<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 %}
 
-    {# 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>
+  {% 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>
 
-      <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>

+ 1 - 1
netbox/utilities/templates/search/searchbar.html

@@ -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"

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů