#6372: Implement layout improvements

This commit is contained in:
checktheroads 2021-06-22 17:20:17 -07:00
parent a8a9e061a1
commit eb0d5c996e
24 changed files with 244 additions and 140 deletions

View File

@ -1,4 +1,4 @@
// Entry for all 3rd party library imports that do not rely on Bootstrap or NetBox styles.
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
@import '@mdi/font/css/materialdesignicons.min.css';
@import 'flatpickr/dist/flatpickr.css';

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -98,6 +98,21 @@ table td > .progress {
min-width: 6rem;
}
span.profile-button .dropdown-menu {
transition: opacity 0.2s ease-in-out;
display: block !important;
right: 0;
left: auto;
margin-top: 0.5rem;
box-shadow: $box-shadow;
&:not(.show) {
opacity: 0;
}
&.show {
opacity: 1;
}
}
div#advanced-search-content div.card div.card-body div.col:not(:last-child) {
margin-right: 1rem;
}
@ -362,6 +377,12 @@ div.content-container {
min-height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
@include media-breakpoint-up(md) {
margin-left: $sidebar-width;
}
div.content {
flex: 1;
}
@ -383,27 +404,36 @@ div.content-container {
z-index: 100; /* Behind the navbar */
border-right: 1px solid $border-color;
background-color: var(--nbx-sidebar-bg);
max-height: 100%;
width: 100%;
@include media-breakpoint-up(md) {
width: 100%;
max-width: $sidebar-width;
}
@media (max-width: map.get($grid-breakpoints, 'md')) {
top: 8.125rem;
}
div.accordion-item {
div.accordion-collapse {
&.collapse.show,
&.collapsing {
background-color: $accordion-body-active-bg;
}
}
& > a.accordion-button.nav-link {
&:hover {
color: $accordion-button-active-color;
background-color: $accordion-button-active-bg;
}
&:focus {
border-color: unset;
border: unset;
& > a.accordion-button {
&:not(.collapsed) {
box-shadow: unset;
}
&.nav-link {
border-radius: $border-radius;
&:hover {
color: $accordion-button-active-color;
background-color: $accordion-button-active-bg;
}
&:focus {
border-color: unset;
box-shadow: unset;
}
}
}
}

View File

@ -20,6 +20,7 @@
"parcel-bundler": "1.12.3",
"query-string": "^6.14.1",
"sass": "^1.32.8",
"simplebar": "^5.3.4",
"slim-select": "^1.27.0"
},
"devDependencies": {

View File

@ -1,8 +1,5 @@
import { getElements, isTruthy } from './util';
type ColorMode = 'light' | 'dark';
type ColorModePreference = ColorMode | 'none';
const COLOR_MODE_KEY = 'netbox-color-mode';
const TEXT_WHEN_DARK = 'Light Mode';
const TEXT_WHEN_LIGHT = 'Dark Mode';

View File

@ -169,3 +169,14 @@ interface ObjectWithGroup extends APIObjectBase {
declare const messages: string[];
type FormControls = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
type ColorMode = 'light' | 'dark';
type ColorModePreference = ColorMode | 'none';
type ConfigContextFormat = 'json' | 'yaml';
type UserPreferences = {
ui: {
colorMode: ColorMode;
showRackImages: boolean;
};
};

View File

@ -1,4 +1,5 @@
import 'babel-polyfill';
import '@popperjs/core';
import 'bootstrap';
import 'simplebar';
import './netbox';

View File

@ -9,6 +9,7 @@ import { initClipboard } from './clipboard';
import { initDateSelector } from './dateSelector';
import { initTableConfig } from './tableConfig';
import { initInterfaceTable } from './tables';
import { initSideNav } from './sidenav';
function init() {
for (const init of [
@ -23,6 +24,7 @@ function init() {
initClipboard,
initTableConfig,
initInterfaceTable,
initSideNav,
]) {
init();
}

View File

@ -0,0 +1,22 @@
import { getElement, getElements } from './util';
const breakpoints = {
sm: 540,
md: 720,
lg: 960,
xl: 1140,
};
function toggleBodyPosition(position: HTMLBodyElement['style']['position']): void {
for (const element of getElements('body')) {
element.style.position = position;
}
}
export function initSideNav() {
const element = getElement<HTMLAnchorElement>('sidebarMenu');
if (element !== null && document.body.clientWidth < breakpoints.lg) {
element.addEventListener('shown.bs.collapse', () => toggleBodyPosition('fixed'));
element.addEventListener('hidden.bs.collapse', () => toggleBodyPosition('relative'));
}
}

View File

@ -235,11 +235,13 @@ $theme-color-addons: (
'pink-900': $pink-900,
);
$font-family-sans-serif: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
'Segoe UI Symbol', 'Noto Color Emoji';
$font-family-sans-serif: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji',
'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
$font-family-monospace: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
'Courier New', monospace;
$accordion-padding-y: 0.8125rem;
$accordion-padding-x: 0.8125rem;
$sidebar-width: 280px;

View File

@ -1070,6 +1070,11 @@
resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c"
integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==
"@juggle/resize-observer@^3.3.1":
version "3.3.1"
resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.3.1.tgz#b50a781709c81e10701004214340f25475a171a0"
integrity sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw==
"@mdi/font@^5.9.55":
version "5.9.55"
resolved "https://registry.yarnpkg.com/@mdi/font/-/font-5.9.55.tgz#41acd50b88073ded7095fc3029d8712b6e12f38e"
@ -2022,6 +2027,11 @@ camelcase@^5.0.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
can-use-dom@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/can-use-dom/-/can-use-dom-0.1.0.tgz#22cc4a34a0abc43950f42c6411024a3f6366b45a"
integrity sha1-IsxKNKCrxDlQ9CxkEQJKP2NmtFo=
caniuse-api@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0"
@ -2388,6 +2398,11 @@ core-js@^2.4.0, core-js@^2.5.0:
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
core-js@^3.0.1:
version "3.15.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.15.1.tgz#6c08ab88abdf56545045ccf5fd81f47f407e7f1a"
integrity sha512-h8VbZYnc9pDzueiS2610IULDkpFFPunHwIpl8yRwFahAEEdSpHlTy3h3z3rKq5h11CaUdBEeRViu9AYvbxiMeg==
core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@ -4820,6 +4835,11 @@ lodash.sortby@^4.7.0:
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
lodash.throttle@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4"
integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=
lodash.toarray@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561"
@ -6957,6 +6977,18 @@ simple-swizzle@^0.2.2:
dependencies:
is-arrayish "^0.3.1"
simplebar@^5.3.4:
version "5.3.4"
resolved "https://registry.yarnpkg.com/simplebar/-/simplebar-5.3.4.tgz#7de8d4a07ed3c6612644f4dbc04a8427fdf038ef"
integrity sha512-2mCaVdiroCKmXuD+Qfy+QSE32m5BMuZ4ssHvRD1QEPYH95Re/kox7j/Wy0Hje8Uo7LY7O6JK3XSNJmesGlsP8Q==
dependencies:
"@juggle/resize-observer" "^3.3.1"
can-use-dom "^0.1.0"
core-js "^3.0.1"
lodash.debounce "^4.0.8"
lodash.memoize "^4.1.2"
lodash.throttle "^4.1.1"
slash@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"

View File

@ -6,126 +6,123 @@
{% block layout %}
<div class="container-fluid">
<div class="row">
<main class="col-md-9 ms-sm-auto col-lg-10 px-0">
<main class="ms-sm-auto px-0">
{# Sidebar #}
<nav id="sidebar-menu" class="d-md-block sidebar collapse px-0" data-simplebar>
{# Sidebar #}
<nav id="sidebar-menu" class="col-md-3 col-lg-2 d-md-block sidebar collapse px-0">
{# Sidebar content #}
<div class="position-sticky pt-3">
{# Sidebar content #}
<div class="position-sticky pt-3">
{# Logo #}
<a class="p-1 sidebar-logo d-none d-md-flex justify-content-center" href="{% url 'home' %}">
<img src="{% static 'netbox_logo.svg' %}" alt="NetBox logo" />
</a>
{# Search bar #}
<ul class="nav flex-column">
<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-end">
{% include 'inc/profile_button.html' %}
</div>
{% nav %}
</ul>
</div>
{# Sidebar footer #}
<div class="d-flex flex-column container-fluid mt-auto justify-content-end sidebar-bottom">
<nav class="nav justify-content-between mb-2 mt-4 px-2">
{# Documentation #}
<a type="button" target="_blank" class="nav-link" href="https://netbox.readthedocs.io/">
<i title="Docs" class="mdi mdi-book-open-variant text-primary" data-bs-placement="top" data-bs-toggle="tooltip"></i>
</a>
{# API docs #}
<a class="nav-link" href="{% url 'api_docs' %}" target="_blank">
<i title="API" data-bs-placement="top" data-bs-toggle="tooltip" class="mdi mdi-code-braces text-primary"></i>
</a>
{# GitHub #}
<a class="nav-link" href="https://github.com/netbox-community/netbox" target="_blank">
<i title="Source Code" data-bs-placement="top" data-bs-toggle="tooltip" class="mdi mdi-code-tags text-primary"></i>
</a>
{# GitHub wiki #}
<a target="_blank" class="nav-link" href="https://github.com/netbox-community/netbox/wiki">
<i title="Get Help" data-bs-placement="top" data-bs-toggle="tooltip" class="mdi mdi-lifebuoy text-primary"></i>
</a>
</nav>
</div>
</nav>
{# 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">
<a class="px-2 sidebar-logo d-block d-md-none" href="{% url 'home' %}">
<img src="{% static 'netbox_logo.svg' %}" alt="NetBox logo" />
</a>
<button
type="button"
aria-expanded="false"
data-bs-toggle="collapse"
aria-controls="sidebar-menu"
data-bs-target="#sidebar-menu"
aria-label="Toggle Navigation"
class="navbar-toggler position-relative collapsed"
>
<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' %}
</div>
</nav>
{# Page header #}
{% block header %}
<div class="title-container px-3 py-3">
{# Title #}
<div id="content-title">
{# Center-align title in object-edit views #}
<h1 class="h2 w-100{% if form or vc_form %} text-center{% endif %}">{% block title %}{% endblock title %}</h1>
</div>
{# Controls #}
{% block controls %}{% endblock controls %}
{# Logo #}
<a class="p-1 sidebar-logo d-none d-md-flex justify-content-center" href="{% url 'home' %}">
<img src="{% static 'netbox_logo.svg' %}" alt="NetBox logo" />
</a>
{# Search bar #}
<ul class="nav flex-column px-4">
<div class="d-block d-md-none mx-1 my-3 search-container">
{% search_options %}
</div>
{% endblock header %}
{# Page content #}
<div id="content" class="container-fluid content px-0 m-0">
{% block tabs %}{% endblock %}
<div class="px-3">
{% block content %}{% endblock %}
<div class="d-flex d-md-none mx-1 my-3 justify-content-end">
{% include 'inc/profile_button.html' %}
</div>
</div>
{# Page footer #}
<footer class="footer container-fluid pb-3 pt-4 px-0">
<div class="row align-items-center justify-content-end mx-0">
<div class="col-auto d-none d-md-block"></div>
<div class="col text-center small text-muted">
<span class="fw-light d-block d-md-inline">{% now 'Y-m-d H:i:s T' %}</span>
<span class="ms-md-3 d-block d-md-inline">{{ settings.HOSTNAME }} (v{{ settings.VERSION }})</span>
</div>
</div>
</footer>
{% nav %}
</ul>
</div>
</main>
</div>
{# Sidebar footer #}
<div class="d-flex flex-column container-fluid mt-auto justify-content-end sidebar-bottom">
<nav class="nav justify-content-between mb-2 mt-4 px-2">
{# Documentation #}
<a type="button" target="_blank" class="nav-link" href="https://netbox.readthedocs.io/">
<i title="Docs" class="mdi mdi-book-open-variant text-primary" data-bs-placement="top" data-bs-toggle="tooltip"></i>
</a>
{# API docs #}
<a class="nav-link" href="{% url 'api_docs' %}" target="_blank">
<i title="API" data-bs-placement="top" data-bs-toggle="tooltip" class="mdi mdi-code-braces text-primary"></i>
</a>
{# GitHub #}
<a class="nav-link" href="https://github.com/netbox-community/netbox" target="_blank">
<i title="Source Code" data-bs-placement="top" data-bs-toggle="tooltip" class="mdi mdi-code-tags text-primary"></i>
</a>
{# GitHub wiki #}
<a target="_blank" class="nav-link" href="https://github.com/netbox-community/netbox/wiki">
<i title="Get Help" data-bs-placement="top" data-bs-toggle="tooltip" class="mdi mdi-lifebuoy text-primary"></i>
</a>
</nav>
</div>
</nav>
{# 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">
<a class="px-2 sidebar-logo d-block d-md-none" href="{% url 'home' %}">
<img src="{% static 'netbox_logo.svg' %}" alt="NetBox logo" />
</a>
<button
type="button"
aria-expanded="false"
data-bs-toggle="collapse"
aria-controls="sidebar-menu"
data-bs-target="#sidebar-menu"
aria-label="Toggle Navigation"
class="navbar-toggler position-relative collapsed"
>
<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' %}
</div>
</nav>
{# Page header #}
{% block header %}
<div class="title-container px-3 py-3">
{# Title #}
<div id="content-title">
{# Center-align title in object-edit views #}
<h1 class="h2 w-100{% if form or vc_form %} text-center{% endif %}">{% block title %}{% endblock title %}</h1>
</div>
{# Controls #}
{% block controls %}{% endblock controls %}
</div>
{% endblock header %}
{# Page content #}
<div id="content" class="container-fluid content px-0 m-0">
{% block tabs %}{% endblock %}
<div class="px-3">
{% block content %}{% endblock %}
</div>
</div>
{# Page footer #}
<footer class="footer container-fluid pb-3 pt-4 px-0">
<div class="row align-items-center justify-content-end mx-0">
<div class="col-auto d-none d-md-block"></div>
<div class="col text-center small text-muted">
<span class="fw-light d-block d-md-inline">{% now 'Y-m-d H:i:s T' %}</span>
<span class="ms-md-3 d-block d-md-inline">{{ settings.HOSTNAME }} (v{{ settings.VERSION }})</span>
</div>
</div>
</footer>
</div>
</main>
</div>
{% endblock layout %}

View File

@ -1,5 +1,5 @@
{% if request.user.is_authenticated %}
<span class="dropdown ms-0 ms-md-3">
<span class="dropdown ms-0 ms-md-3 profile-button">
<button
type="button"
aria-expanded="false"
@ -25,12 +25,12 @@
</li>
<li>
<a class="dropdown-item" href="{% url 'user:profile' %}">
<i class="mdi mdi-account"></i> Profile
<i class="mdi mdi-account"></i> Profile & Settings
</a>
</li>
<li><hr class="dropdown-divider" /></li>
<li>
<a class="dropdown-item" href="{% url 'logout' %}">
<a class="dropdown-item text-danger" href="{% url 'logout' %}">
<i class="mdi mdi-logout-variant"></i> Log Out
</a>
</li>

View File

@ -11,6 +11,7 @@
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="mdi mdi-{{ menu.icon }} me-1"></i>
{{ menu.label }}
</span>
</a>

View File

@ -33,11 +33,13 @@ class Menu:
"""A top level menu group. Example: Organization, Devices, IPAM."""
label: str
icon: str
groups: Sequence[MenuGroup]
ORGANIZATION_MENU = Menu(
label="Organization",
icon="domain",
groups=(
MenuGroup(
label="Sites",
@ -83,6 +85,7 @@ ORGANIZATION_MENU = Menu(
DEVICES_MENU = Menu(
label="Devices",
icon="server",
groups=(
MenuGroup(
label="Devices",
@ -150,6 +153,7 @@ DEVICES_MENU = Menu(
IPAM_MENU = Menu(
label="IPAM",
icon="counter",
groups=(
MenuGroup(
label="IP Addresses",
@ -204,6 +208,7 @@ IPAM_MENU = Menu(
VIRTUALIZATION_MENU = Menu(
label="Virtualization",
icon="monitor",
groups=(
MenuGroup(
label="Virtual Machines",
@ -231,6 +236,7 @@ VIRTUALIZATION_MENU = Menu(
CIRCUITS_MENU = Menu(
label="Circuits",
icon="transit-connection-variant",
groups=(
MenuGroup(
label="Circuits",
@ -256,6 +262,7 @@ CIRCUITS_MENU = Menu(
POWER_MENU = Menu(
label="Power",
icon="flash",
groups=(
MenuGroup(
label="Power",
@ -271,6 +278,7 @@ POWER_MENU = Menu(
OTHER_MENU = Menu(
label="Other",
icon="notification-clear-all",
groups=(
MenuGroup(
label="Logging",