#6797: Improve toast styles

This commit is contained in:
checktheroads 2021-07-24 17:08:18 -07:00
parent 189e733f81
commit 0479d5a02a
18 changed files with 124 additions and 44 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -34,16 +34,16 @@ export function createToast(
message: string,
extra?: string,
): Toast {
let iconName = 'bi-exclamation-triangle-fill';
let iconName = 'mdi-alert';
switch (level) {
case 'warning':
iconName = 'bi-exclamation-triangle-fill';
iconName = 'mdi-alert';
case 'success':
iconName = 'bi-check-circle-fill';
iconName = 'mdi-check-circle';
case 'info':
iconName = 'bi-info-circle-fill';
iconName = 'mdi-information';
case 'danger':
iconName = 'bi-exclamation-triangle-fill';
iconName = 'mdi-alert';
}
const container = document.createElement('div');
@ -59,7 +59,7 @@ export function createToast(
header.setAttribute('class', `toast-header bg-${level} text-body`);
const icon = document.createElement('i');
icon.setAttribute('class', `bi ${iconName}`);
icon.setAttribute('class', `mdi ${iconName}`);
const titleElement = document.createElement('strong');
titleElement.setAttribute('class', 'me-auto ms-1');

View File

@ -94,12 +94,37 @@
margin-bottom: $spacer;
}
// Ensure elements with data-href set show the correct cursor.
// data-href is set on non non-anchor elements that need to redirect the user to a URL when
// clicked, but where an anchor element does not suffice or is not supported.
*[data-href] {
cursor: pointer;
}
// Use proper contrasting color foreground color for special components.
@each $color, $value in $theme-colors {
// Override CSS values on each theme color.
// Use Bootstrap's method of coloring alert links to appropriately color close buttons within
// another colored element.
// See: https://github.com/twbs/bootstrap/blob/2bdbb42dcf6bfb99b5e9e5444d9e64589eb8c08f/scss/_alert.scss#L50-L52
// See: https://github.com/twbs/bootstrap/blob/2bdbb42dcf6bfb99b5e9e5444d9e64589eb8c08f/scss/_close.scss#L12
$shaded-color: shade-color(mix($value, color-contrast($value), abs($alert-color-scale)), 5%);
$btn-close-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$shaded-color}'><path d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/></svg>");
.bg-#{$color} button.btn-close {
background: transparent escape-svg($btn-close-bg) center / $btn-close-width auto no-repeat;
}
// Use Bootstrap's method of coloring the .alert-link class automatically.
// See: https://github.com/twbs/bootstrap/blob/2bdbb42dcf6bfb99b5e9e5444d9e64589eb8c08f/scss/_alert.scss#L50-L52
.toast.bg-#{$color},
.alert.alert-#{$color} {
a {
color: $shaded-color;
font-weight: $font-weight-bold;
}
}
// Use proper contrasting color foreground color for special components.
.badge,
.toast,
.progress-bar {
@ -107,11 +132,11 @@
color: color-contrast($value);
}
}
// Use proper foreground color in the alert body. Note: this is applied to a, p, & small because
// Use proper foreground color in the alert body. Note: this is applied to p, & small because
// we *don't* want to override the h1-h6 colors for alerts, since those are set to a color
// similar to the alert color.
.alert.alert-#{$color} {
a,
p,
small {
color: color-contrast($value);
@ -926,8 +951,9 @@ div.card > div.card-header > div.table-controls {
border-bottom: 1px solid $nav-tabs-border-color;
}
// Shade the home page content background-color.
body {
// Page-specific styles.
html {
// Shade the home page content background-color.
&[data-netbox-path='/'] {
.content-container,
.search {
@ -940,4 +966,11 @@ body {
}
}
}
// Don't show the django-messages toasts on the login screen in favor of the alert component.
&[data-netbox-path*='/login'] {
#django-messages {
display: none;
}
}
}

View File

@ -128,6 +128,10 @@ $font-family-sans-serif: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMa
$font-family-monospace: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
'Courier New', monospace;
// This is the same value as the default from Bootstrap, but it needs to be in scope prior to
// importing _variables.scss from Bootstrap.
$btn-close-width: 1em;
$accordion-padding-y: 0.8125rem;
$accordion-padding-x: 0.8125rem;

View File

@ -46,6 +46,7 @@ $link-hover-color: $blue-100;
// Alerts
$alert-bg-scale: -5%;
$alert-border-scale: -20%;
$alert-color-scale: 20%;
// Tables
$table-color: $gray-100;
@ -261,15 +262,8 @@ $carousel-dark-control-icon-filter: invert(1) grayscale(100);
// Close
$btn-close-color: $white;
$btn-close-white-filter: invert(1) grayscale(100%) brightness(200%);
$btn-close-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$btn-close-color}'><path d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/></svg>");
@each $color, $value in $theme-colors {
.bg-#{$color} button.btn-close {
background: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{color-contrast($value)}'><path d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/></svg>");
}
}
// Code
$code-color: $gray-200;
$kbd-color: $white;

View File

@ -1,39 +1,59 @@
{% load helpers %}
<div id="django-messages" class="toast-container position-fixed bottom-0 end-0 m-3">
{# Django Messages #}
{% if messages %}
{% for message in messages %}
<div class="django-message toast align-items-center border-0 bg-{% if message.tags %}{{ message.tags }}{% else %}info{% endif %}" role="alert" aria-live="assertive" aria-atomic="true" data-bs-delay="10000">
<div class="d-flex">
<div class="toast-body">
{{ message }}
{% with message.level_tag|status_from_tag as status %}
{% with status|icon_from_status as icon %}
<div class="django-message toast align-items-center border-0 bg-{{ status }}" role="alert" aria-live="assertive" aria-atomic="true" data-bs-delay="10000">
<div class="d-flex">
<div class="toast-body">
<i class="mdi mdi-{{ icon }} me-1"></i>
{{ message }}
</div>
<button type="button" class="btn-close me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
</div>
<button type="button" class="btn-close me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
</div>
{% endwith %}
{% endwith %}
{% endfor %}
{% elif form and form.non_field_errors %}
{# Non-Field Form Errors #}
{% for error in form.non_field_errors.get_json_data %}
<div class="django-message toast align-items-center border-0 bg-danger" role="alert" aria-live="assertive" aria-atomic="true" data-bs-delay="10000">
<div class="d-flex">
<div class="toast-body">
{{ error.message }}
</div>
<button type="button" class="btn-close me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
</div>
{% endfor %}
{% elif form and form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="django-message toast align-items-center border-0 bg-danger" role="alert" aria-live="assertive" aria-atomic="true" data-bs-delay="60000">
<div class="toast-header bg-danger">
<strong class="me-auto">{{ field.label }}</strong>
<div class="django-message toast align-items-center border-0 bg-danger" role="alert" aria-live="assertive" aria-atomic="true" data-bs-delay="10000">
<div class="d-flex">
<div class="toast-body">
<i class="mdi mdi-{{ "danger"|icon_from_status }} me-1"></i>
{{ error.message }}
</div>
<button type="button" class="btn-close me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
{{ error|escape }}
</div>
</div>
{% endfor %}
{% elif form and form.errors %}
{# Form Field Errors #}
{% for field in form %}
{% for error in field.errors %}
<div class="django-message toast align-items-center border-0 bg-danger" role="alert" aria-live="assertive" aria-atomic="true" data-bs-delay="60000">
<div class="toast-header bg-danger">
<strong class="me-auto">
<i class="mdi mdi-{{ "danger"|icon_from_status }} me-1"></i>
{{ field.label }}
</strong>
<button type="button" class="btn-close me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
{{ error|escape }}
</div>
</div>
{% endfor %}
{% endfor %}
{% endif %}
</div>

View File

@ -326,6 +326,35 @@ def querystring(request, **kwargs):
return ''
@register.filter
def status_from_tag(tag: str = "info") -> str:
"""
Determine Bootstrap theme status/level from Django's Message.level_tag.
"""
status_map = {
'warning': 'warning',
'success': 'success',
'error': 'danger',
'debug': 'info',
'info': 'info',
}
return status_map.get(tag.lower(), 'info')
@register.filter
def icon_from_status(status: str = "info") -> str:
"""
Determine icon class name from Bootstrap theme status/level.
"""
icon_map = {
'warning': 'alert',
'success': 'check-circle',
'danger': 'alert',
'info': 'information',
}
return icon_map.get(status.lower(), 'information')
@register.inclusion_tag('utilities/templatetags/utilization_graph.html')
def utilization_graph(utilization, warning_threshold=75, danger_threshold=90):
"""
@ -338,7 +367,7 @@ def utilization_graph(utilization, warning_threshold=75, danger_threshold=90):
elif warning_threshold or danger_threshold:
bar_class = 'bg-success'
else:
bar_class = 'bg-default'
bar_class = 'bg-gray'
return {
'utilization': utilization,
'bar_class': bar_class,