From 48e1933b714f8a22ed0308f8a4c2ce9db15df488 Mon Sep 17 00:00:00 2001 From: Stephan de Wit Date: Wed, 21 Aug 2024 11:39:32 +0200 Subject: [PATCH] dashboard: compact Services widget (https://github.com/opnsense/core/issues/7766) --- .../www/js/widgets/BaseTableWidget.js | 50 +++++++++++-------- src/opnsense/www/js/widgets/Services.js | 27 +++++----- 2 files changed, 41 insertions(+), 36 deletions(-) diff --git a/src/opnsense/www/js/widgets/BaseTableWidget.js b/src/opnsense/www/js/widgets/BaseTableWidget.js index f46638599..4d903081e 100644 --- a/src/opnsense/www/js/widgets/BaseTableWidget.js +++ b/src/opnsense/www/js/widgets/BaseTableWidget.js @@ -30,24 +30,6 @@ class BaseTableWidget extends BaseWidget { this.tables = {}; this.curSize = null; - this.sizeStates = { - 0: { - '.flextable-row': {'padding': ''}, - '.flextable-header .flex-cell': {'border-bottom': 'solid 1px'}, - '.flex-cell': {'width': '100%'}, - '.column': {'width': '100%'}, - '.flex-subcell': {'width': '100%'}, - }, - 450: { - '.flextable-row': {'padding': '0.5em 0.5em'}, - '.flextable-header .flex-cell': {'border-bottom': ''}, - '.flex-cell': {'width': this._calculateColumnWidth.bind(this)}, - '.column .flex-cell': {'width': '100%'}, - '.column': {'width': ''}, - '.flex-subcell': {'width': ''}, - } - } - this.widths = Object.keys(this.sizeStates).sort(); } _calculateColumnWidth() { @@ -92,6 +74,7 @@ class BaseTableWidget extends BaseWidget { * headers: list of headers to display. Only applicable for headerPosition: top. * sortIndex: index of the column to sort on. Only applicable for headerPosition: top. * sortOrder: 'asc' or 'desc'. Only applicable for headerPosition: top. + * headerBreakpoint: width in pixels beforing switching to a column layout. Only applicable for headerPosition: left. * */ if (this.options === null) { @@ -104,11 +87,32 @@ class BaseTableWidget extends BaseWidget { rotation: false, sortIndex: null, sortOrder: 'desc', + headerBreakpoint: 450, ...options } let $table = null; let $headerContainer = null; + this.headerBreakpoint = mergedOpts.headerBreakpoint; + + this.sizeStates = { + 0: { + '.flextable-row': {'padding': ''}, + '.flextable-header .flex-cell': {'border-bottom': 'solid 1px'}, + '.flex-cell': {'width': '100%'}, + '.column': {'width': '100%'}, + '.flex-subcell': {'width': '100%'}, + }, + [this.headerBreakpoint]: { + '.flextable-row': {'padding': '0.5em 0.5em'}, + '.flextable-header .flex-cell': {'border-bottom': ''}, + '.flex-cell': {'width': this._calculateColumnWidth.bind(this)}, + '.column .flex-cell': {'width': '100%'}, + '.column': {'width': ''}, + '.flex-subcell': {'width': ''}, + } + } + this.widths = Object.keys(this.sizeStates).sort(); if (mergedOpts.headerPosition === 'top') { /* CSS grid implementation */ @@ -163,9 +167,9 @@ class BaseTableWidget extends BaseWidget { let newElement = true; if (rowIdentifier !== null) { - let $existingRow = $(`#id_${rowIdentifier}`); + let $existingRow = $(`#${this.id}_${rowIdentifier}`); if ($existingRow.length === 0) { - $gridRow.attr('id', `id_${rowIdentifier}`); + $gridRow.attr('id', `${this.id}_${rowIdentifier}`); } else { $gridRow = $existingRow.empty(); newElement = false; @@ -181,7 +185,7 @@ class BaseTableWidget extends BaseWidget { $table.append($gridRow); } } else { - $(`#id_${rowIdentifier}`).replaceWith($gridRow); + $(`#${this.id}_${rowIdentifier}`).replaceWith($gridRow); } if (options.headerPosition === 'top' && options.sortIndex !== null) { @@ -278,6 +282,10 @@ class BaseTableWidget extends BaseWidget { } onWidgetResize(elem, width, height) { + if (this.widths == null || this.sizeStates == null) { + return false; + } + let lowIndex = 0; for (let i = 0; i < this.widths.length; i++) { if (this.widths[i] <= width) { diff --git a/src/opnsense/www/js/widgets/Services.js b/src/opnsense/www/js/widgets/Services.js index c41a76d00..00254ac26 100644 --- a/src/opnsense/www/js/widgets/Services.js +++ b/src/opnsense/www/js/widgets/Services.js @@ -27,7 +27,6 @@ export default class Services extends BaseTableWidget { constructor() { super(); - this.locked = false; } @@ -40,7 +39,8 @@ export default class Services extends BaseTableWidget { getMarkup() { let $table = this.createTable('services-table', { - headerPosition: 'left' + headerPosition: 'left', + headerBreakpoint: 270 }); return $(`
`).append($table); } @@ -48,7 +48,7 @@ export default class Services extends BaseTableWidget { serviceControl(actions) { return actions.map(({ action, id, title, icon }) => ` `).join(''); @@ -62,16 +62,12 @@ export default class Services extends BaseTableWidget { return; } - if (!this.dataChanged('services', data)) { - return; - } - $('.service-status').tooltip('hide'); $('.srv_status_act2').tooltip('hide'); for (const service of data.rows) { let name = service.name; - let description = service.description; + let $description = $(`
${service.description}
`); let actions = []; if (service.locked) { @@ -83,11 +79,12 @@ export default class Services extends BaseTableWidget { actions.push({ action: 'start', id: service.id, title: this.translations.start, icon: 'play' }); } - let $buttonContainer = $(`
+ let $buttonContainer = $(`
+ data-toggle="tooltip" title="${service.running ? this.translations.running : this.translations.stopped}" + style="font-size: 10px;">
@@ -95,7 +92,7 @@ export default class Services extends BaseTableWidget { $buttonContainer.append(this.serviceControl(actions)); - super.updateTable('services-table', [[description, $buttonContainer.prop('outerHTML')]], service.id); + super.updateTable('services-table', [[$description.prop('outerHTML'), $buttonContainer.prop('outerHTML')]], service.id); } $('.service-status').tooltip({container: 'body'}); @@ -105,10 +102,10 @@ export default class Services extends BaseTableWidget { this.locked = true; event.preventDefault(); let $elem = $(event.currentTarget); - const icon = $elem.find('i').clone(); - $elem.remove('i').html(''); - await $.post(`/api/core/service/${$elem.data('service_action')}/${$elem.data('service')}`); - $elem.remove('i').html(icon); + let $icon = $elem.children(0); + this.startCommandTransition($elem.data('service'), $icon); + const result = await this.ajaxCall(`/api/core/service/${$elem.data('service_action')}/${$elem.data('service')}`, {}, 'POST'); + await this.endCommandTransition($elem.data('service'), $icon, true, false); await this.updateServices(); this.locked = false; });