1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Merge pull request #6960 from candlerb/candlerb/6879-v3

Display device names in front of device front/rear images
This commit is contained in:
Jeremy Stretch
2021-10-07 12:35:17 -04:00
committed by GitHub
7 changed files with 95 additions and 84 deletions

View File

@ -112,6 +112,9 @@ class RackElevationSVG:
) )
image.fit(scale='slice') image.fit(scale='slice')
link.add(image) link.add(image)
link.add(drawing.text(str(name), insert=text, stroke='black',
stroke_width='0.2em', stroke_linejoin='round', class_='device-image-label'))
link.add(drawing.text(str(name), insert=text, fill='white', class_='device-image-label'))
def _draw_device_rear(self, drawing, device, start, end, text): def _draw_device_rear(self, drawing, device, start, end, text):
rect = drawing.rect(start, end, class_="slot blocked") rect = drawing.rect(start, end, class_="slot blocked")
@ -129,6 +132,9 @@ class RackElevationSVG:
) )
image.fit(scale='slice') image.fit(scale='slice')
drawing.add(image) drawing.add(image)
drawing.add(drawing.text(str(device), insert=text, stroke='black',
stroke_width='0.2em', stroke_linejoin='round', class_='device-image-label'))
drawing.add(drawing.text(str(device), insert=text, fill='white', class_='device-image-label'))
@staticmethod @staticmethod
def _draw_empty(drawing, rack, start, end, text, id_, face_id, class_, reservation): def _draw_empty(drawing, rack, start, end, text, id_, face_id, class_, reservation):

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,92 +1,90 @@
import { rackImagesState } from './stores'; import { rackImagesState, RackViewSelection } from './stores';
import { getElements } from './util'; import { getElements } from './util';
import type { StateManager } from './state'; import type { StateManager } from './state';
type RackToggleState = { hidden: boolean }; export type RackViewState = { view: RackViewSelection };
/** /**
* Toggle the Rack Image button to reflect the current state. If the current state is hidden and * Show or hide images and labels to build the desired rack view.
* the images are therefore hidden, the button should say "Show Images". Likewise, if the current
* state is *not* hidden, and therefore the images are shown, the button should say "Hide Images".
*
* @param hidden Current State - `true` if images are hidden, `false` otherwise.
* @param button Button element.
*/ */
function toggleRackImagesButton(hidden: boolean, button: HTMLButtonElement): void { function setRackView(
const text = hidden ? 'Show Images' : 'Hide Images'; view: RackViewSelection,
const selected = hidden ? '' : 'selected'; elevation: HTMLObjectElement,
button.setAttribute('selected', selected);
button.innerHTML = `<i class="mdi mdi-file-image-outline"></i>&nbsp;${text}`;
}
/**
* Show all rack images.
*/
function showRackImages(): void {
for (const elevation of getElements<HTMLObjectElement>('.rack_elevation')) {
const images = elevation.contentDocument?.querySelectorAll('image.device-image') ?? [];
for (const image of images) {
image.classList.remove('hidden');
}
}
}
/**
* Hide all rack images.
*/
function hideRackImages(): void {
for (const elevation of getElements<HTMLObjectElement>('.rack_elevation')) {
const images = elevation.contentDocument?.querySelectorAll('image.device-image') ?? [];
for (const image of images) {
image.classList.add('hidden');
}
}
}
/**
* Toggle the visibility of device images and update the toggle button style.
*/
function handleRackImageToggle(
target: HTMLButtonElement,
state: StateManager<RackToggleState>,
): void { ): void {
const initiallyHidden = state.get('hidden'); switch(view) {
state.set('hidden', !initiallyHidden); case 'images-and-labels': {
const hidden = state.get('hidden'); showRackElements('image.device-image', elevation);
showRackElements('text.device-image-label', elevation);
if (hidden) { break;
hideRackImages(); }
} else { case 'images-only': {
showRackImages(); showRackElements('image.device-image', elevation);
hideRackElements('text.device-image-label', elevation);
break;
}
case 'labels-only': {
hideRackElements('image.device-image', elevation);
hideRackElements('text.device-image-label', elevation);
break;
}
}
}
function showRackElements(
selector: string,
elevation: HTMLObjectElement,
): void {
const elements = elevation.contentDocument?.querySelectorAll(selector) ?? [];
for (const element of elements) {
element.classList.remove('hidden');
}
}
function hideRackElements(
selector: string,
elevation: HTMLObjectElement,
): void {
const elements = elevation.contentDocument?.querySelectorAll(selector) ?? [];
for (const element of elements) {
element.classList.add('hidden');
} }
toggleRackImagesButton(hidden, target);
} }
/** /**
* Add onClick callback for toggling rack elevation images. Synchronize the image toggle button * Change the visibility of all racks in response to selection.
* text and display state of images with the local state. */
function handleRackViewSelect(
newView: RackViewSelection,
state: StateManager<RackViewState>,
): void {
state.set('view', newView);
for (const elevation of getElements<HTMLObjectElement>('.rack_elevation')) {
setRackView(newView, elevation);
}
}
/**
* Add change callback for selecting rack elevation images, and set
* initial state of select and the images themselves
*/ */
export function initRackElevation(): void { export function initRackElevation(): void {
const initiallyHidden = rackImagesState.get('hidden'); const initialView = rackImagesState.get('view');
for (const button of getElements<HTMLButtonElement>('button.toggle-images')) {
toggleRackImagesButton(initiallyHidden, button);
button.addEventListener( for (const control of getElements<HTMLSelectElement>('select.rack-view')) {
'click', control.selectedIndex = [...control.options].findIndex(o => o.value == initialView);
control.addEventListener(
'change',
event => { event => {
handleRackImageToggle(event.currentTarget as HTMLButtonElement, rackImagesState); handleRackViewSelect((event.currentTarget as any).value as RackViewSelection, rackImagesState);
}, },
false, false,
); );
} }
for (const element of getElements<HTMLObjectElement>('.rack_elevation')) { for (const element of getElements<HTMLObjectElement>('.rack_elevation')) {
element.addEventListener('load', () => { element.addEventListener('load', () => {
if (initiallyHidden) { setRackView(initialView, element);
hideRackImages();
} else if (!initiallyHidden) {
showRackImages();
}
}); });
} }
} }

View File

@ -1,6 +1,8 @@
import { createState } from '../state'; import { createState } from '../state';
export const rackImagesState = createState<{ hidden: boolean }>( export type RackViewSelection = 'images-and-labels' | 'images-only' | 'labels-only';
{ hidden: false },
export const rackImagesState = createState<{ view: RackViewSelection }>(
{ view: 'images-and-labels' },
{ persist: true }, { persist: true },
); );

View File

@ -18,10 +18,11 @@
{% endblock %} {% endblock %}
{% block extra_controls %} {% block extra_controls %}
<button class="btn btn-sm btn-outline-primary toggle-images" selected="selected"> <select class="btn btn-sm btn-outline-dark rack-view">
<i class="mdi mdi-file-image-outline"></i> <option value="images-and-labels" selected="selected">Images and Labels</option>
Hide Images <option value="images-only">Images only</option>
</button> <option value="labels-only">Labels only</option>
</select>
<a {% if prev_rack %}href="{% url 'dcim:rack' pk=prev_rack.pk %}{% endif %}" class="btn btn-sm btn-primary{% if not prev_rack %} disabled{% endif %}"> <a {% if prev_rack %}href="{% url 'dcim:rack' pk=prev_rack.pk %}{% endif %}" class="btn btn-sm btn-primary{% if not prev_rack %} disabled{% endif %}">
<i class="mdi mdi-chevron-left" aria-hidden="true"></i> Previous <i class="mdi mdi-chevron-left" aria-hidden="true"></i> Previous
</a> </a>

View File

@ -7,9 +7,13 @@
{% block controls %} {% block controls %}
<div class="controls"> <div class="controls">
<div class="control-group"> <div class="control-group">
<button class="btn btn-sm btn-outline-dark toggle-images" selected="selected"> <div class="btn-group btn-group-sm" role="group">
<span class="mdi mdi mdi-checkbox-marked-circle-outline" aria-hidden="true"></span> Show Images <select class="btn btn-sm btn-outline-secondary active rack-view">
</button> <option value="images-and-labels" selected="selected">Images and Labels</option>
<option value="images-only">Images only</option>
<option value="labels-only">Labels only</option>
</select>
</div>
<div class="btn-group btn-group-sm" role="group"> <div class="btn-group btn-group-sm" role="group">
<a href="{% url 'dcim:rack_elevation_list' %}{% querystring request face='front' %}" class="btn btn-outline-secondary{% if rack_face == 'front' %} active{% endif %}">Front</a> <a href="{% url 'dcim:rack_elevation_list' %}{% querystring request face='front' %}" class="btn btn-outline-secondary{% if rack_face == 'front' %} active{% endif %}">Front</a>
<a href="{% url 'dcim:rack_elevation_list' %}{% querystring request face='rear' %}" class="btn btn-outline-secondary{% if rack_face == 'rear' %} active{% endif %}">Rear</a> <a href="{% url 'dcim:rack_elevation_list' %}{% querystring request face='rear' %}" class="btn btn-outline-secondary{% if rack_face == 'rear' %} active{% endif %}">Rear</a>