#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. // 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 '@mdi/font/css/materialdesignicons.min.css';
@import 'flatpickr/dist/flatpickr.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; 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) { div#advanced-search-content div.card div.card-body div.col:not(:last-child) {
margin-right: 1rem; margin-right: 1rem;
} }
@ -362,6 +377,12 @@ div.content-container {
min-height: 100vh; min-height: 100vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow: hidden;
@include media-breakpoint-up(md) {
margin-left: $sidebar-width;
}
div.content { div.content {
flex: 1; flex: 1;
} }
@ -383,19 +404,27 @@ div.content-container {
z-index: 100; /* Behind the navbar */ z-index: 100; /* Behind the navbar */
border-right: 1px solid $border-color; border-right: 1px solid $border-color;
background-color: var(--nbx-sidebar-bg); 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')) { @media (max-width: map.get($grid-breakpoints, 'md')) {
top: 8.125rem; top: 8.125rem;
} }
div.accordion-item { div.accordion-item {
div.accordion-collapse { border: unset;
&.collapse.show,
&.collapsing { & > a.accordion-button {
background-color: $accordion-body-active-bg; &:not(.collapsed) {
box-shadow: unset;
} }
} &.nav-link {
& > a.accordion-button.nav-link { border-radius: $border-radius;
&:hover { &:hover {
color: $accordion-button-active-color; color: $accordion-button-active-color;
background-color: $accordion-button-active-bg; background-color: $accordion-button-active-bg;
@ -406,6 +435,7 @@ div.content-container {
} }
} }
} }
}
.accordion-body { .accordion-body {
max-height: calc(100vh - 24rem); max-height: calc(100vh - 24rem);

View File

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

View File

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

View File

@ -169,3 +169,14 @@ interface ObjectWithGroup extends APIObjectBase {
declare const messages: string[]; declare const messages: string[];
type FormControls = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement; 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 'babel-polyfill';
import '@popperjs/core'; import '@popperjs/core';
import 'bootstrap'; import 'bootstrap';
import 'simplebar';
import './netbox'; import './netbox';

View File

@ -9,6 +9,7 @@ import { initClipboard } from './clipboard';
import { initDateSelector } from './dateSelector'; import { initDateSelector } from './dateSelector';
import { initTableConfig } from './tableConfig'; import { initTableConfig } from './tableConfig';
import { initInterfaceTable } from './tables'; import { initInterfaceTable } from './tables';
import { initSideNav } from './sidenav';
function init() { function init() {
for (const init of [ for (const init of [
@ -23,6 +24,7 @@ function init() {
initClipboard, initClipboard,
initTableConfig, initTableConfig,
initInterfaceTable, initInterfaceTable,
initSideNav,
]) { ]) {
init(); 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, 'pink-900': $pink-900,
); );
$font-family-sans-serif: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', $font-family-sans-serif: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji',
'Segoe UI Symbol', 'Noto Color Emoji'; 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
$font-family-monospace: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', $font-family-monospace: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
'Courier New', monospace; 'Courier New', monospace;
$accordion-padding-y: 0.8125rem; $accordion-padding-y: 0.8125rem;
$accordion-padding-x: 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" resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c"
integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg== 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": "@mdi/font@^5.9.55":
version "5.9.55" version "5.9.55"
resolved "https://registry.yarnpkg.com/@mdi/font/-/font-5.9.55.tgz#41acd50b88073ded7095fc3029d8712b6e12f38e" 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" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== 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: caniuse-api@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" 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" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== 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: core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 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" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= 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: lodash.toarray@^4.4.0:
version "4.4.0" version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561"
@ -6957,6 +6977,18 @@ simple-swizzle@^0.2.2:
dependencies: dependencies:
is-arrayish "^0.3.1" 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: slash@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"

View File

@ -6,11 +6,9 @@
{% block layout %} {% block layout %}
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <main class="ms-sm-auto px-0">
<main class="col-md-9 ms-sm-auto col-lg-10 px-0">
{# Sidebar #} {# Sidebar #}
<nav id="sidebar-menu" class="col-md-3 col-lg-2 d-md-block sidebar collapse px-0"> <nav id="sidebar-menu" class="d-md-block sidebar collapse px-0" data-simplebar>
{# Sidebar content #} {# Sidebar content #}
<div class="position-sticky pt-3"> <div class="position-sticky pt-3">
@ -21,7 +19,7 @@
</a> </a>
{# Search bar #} {# Search bar #}
<ul class="nav flex-column"> <ul class="nav flex-column px-4">
<div class="d-block d-md-none mx-1 my-3 search-container"> <div class="d-block d-md-none mx-1 my-3 search-container">
{% search_options %} {% search_options %}
</div> </div>
@ -127,5 +125,4 @@
</main> </main>
</div> </div>
</div>
{% endblock layout %} {% endblock layout %}

View File

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

View File

@ -11,6 +11,7 @@
data-bs-target="#{{ menu.label|lower }}" data-bs-target="#{{ menu.label|lower }}"
class="d-flex justify-content-between align-items-center accordion-button nav-link collapsed"> class="d-flex justify-content-between align-items-center accordion-button nav-link collapsed">
<span class="fw-bold sidebar-nav-link"> <span class="fw-bold sidebar-nav-link">
<i class="mdi mdi-{{ menu.icon }} me-1"></i>
{{ menu.label }} {{ menu.label }}
</span> </span>
</a> </a>

View File

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