diff --git a/application/src/main/data/json/system/widget_types/timeseries_table.json b/application/src/main/data/json/system/widget_types/timeseries_table.json
index 625f7a1e7d..24443cfecf 100644
--- a/application/src/main/data/json/system/widget_types/timeseries_table.json
+++ b/application/src/main/data/json/system/widget_types/timeseries_table.json
@@ -17,7 +17,7 @@
"latestDataKeySettingsDirective": "tb-timeseries-table-latest-key-settings",
"hasBasicMode": true,
"basicModeDirective": "tb-timeseries-table-basic-config",
- "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature °C\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = (value + 60)/120 * 100;\\n var color = tinycolor.mix('blue', 'red', percent);\\n color.setAlpha(.5);\\n return {\\n paddingLeft: '20px',\\n color: '#ffffff',\\n background: color.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity, %\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = value;\\n var backgroundColor = tinycolor('blue');\\n backgroundColor.setAlpha(value/100);\\n var color = 'blue';\\n if (value > 50) {\\n color = 'white';\\n }\\n \\n return {\\n paddingLeft: '20px',\\n color: color,\\n background: backgroundColor.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}],\"latestDataKeys\":[]}],\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"enableSearch\":true,\"enableSelectColumnDisplay\":true,\"enableStickyHeader\":true,\"enableStickyAction\":true,\"showCellActionsMenu\":true,\"reserveSpaceForHiddenAction\":\"true\",\"showTimestamp\":true,\"dateFormat\":{\"format\":\"yyyy-MM-dd HH:mm:ss\"},\"displayPagination\":true,\"useEntityLabel\":false,\"defaultPageSize\":10,\"pageStepCount\":3,\"pageStepIncrement\":10,\"hideEmptyLines\":false,\"disableStickyHeader\":false,\"useRowStyleFunction\":false,\"rowStyleFunction\":\"\",\"tabSortKey\":\"timestamp\"},\"title\":\"Time series table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"configMode\":\"basic\",\"titleFont\":null,\"titleColor\":null,\"titleIcon\":null}"
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"entityAliasId\":null,\"filterId\":null,\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature °C\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = (value + 60)/120 * 100;\\n var color = tinycolor.mix('blue', 'red', percent);\\n color.setAlpha(.5);\\n return {\\n paddingLeft: '20px',\\n color: '#ffffff',\\n background: color.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity, %\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = value;\\n var backgroundColor = tinycolor('blue');\\n backgroundColor.setAlpha(value/100);\\n var color = 'blue';\\n if (value > 50) {\\n color = 'white';\\n }\\n \\n return {\\n paddingLeft: '20px',\\n color: color,\\n background: backgroundColor.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}],\"latestDataKeys\":[]}],\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"enableSearch\":true,\"enableSelectColumnDisplay\":true,\"enableStickyHeader\":true,\"enableStickyAction\":true,\"showCellActionsMenu\":true,\"reserveSpaceForHiddenAction\":\"true\",\"showTimestamp\":true,\"dateFormat\":{\"format\":\"yyyy-MM-dd HH:mm:ss\"},\"displayPagination\":true,\"useEntityLabel\":false,\"defaultPageSize\":10,\"pageStepCount\":3,\"pageStepIncrement\":10,\"hideEmptyLines\":false,\"disableStickyHeader\":false,\"useRowStyleFunction\":false,\"rowStyleFunction\":\"\",\"sortOrder\":{\"property\":\"createdTime\",\"direction\":\"DESC\"}},\"title\":\"Time series table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":true,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"configMode\":\"basic\",\"titleFont\":null,\"titleColor\":null,\"titleIcon\":null}"
},
"resources": [
{
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.html
index f236dd1acd..bd223238a8 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.html
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.html
@@ -115,15 +115,22 @@
{{ 'widgets.table.use-entity-label-tab-name' | translate }}
-
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.ts
index ccbb582679..3911506be7 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/timeseries-table-widget-settings.component.ts
@@ -20,7 +20,8 @@ import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { buildPageStepSizeValues } from '@home/components/widget/lib/table-widget.models';
-import { TabSortKey } from '@app/modules/home/components/widget/lib/timeseries-table-widget.component'
+import { Direction } from '@shared/models/page/sort-order';
+import { entityFields } from '@shared/models/entity.models';
@Component({
selector: 'tb-timeseries-table-widget-settings',
@@ -29,7 +30,8 @@ import { TabSortKey } from '@app/modules/home/components/widget/lib/timeseries-t
})
export class TimeseriesTableWidgetSettingsComponent extends WidgetSettingsComponent {
- TabSortKey = TabSortKey;
+ entityFields = entityFields;
+ Direction = Direction;
timeseriesTableWidgetSettingsForm: UntypedFormGroup;
pageStepSizeValues = [];
@@ -62,13 +64,19 @@ export class TimeseriesTableWidgetSettingsComponent extends WidgetSettingsCompon
disableStickyHeader: false,
useRowStyleFunction: false,
rowStyleFunction: '',
- tabSortKey: TabSortKey.TIMESTAMP
+ sortOrder: {
+ property: this.entityFields.createdTime.keyName,
+ direction: Direction.DESC
+ }
};
}
protected prepareInputSettings(settings: WidgetSettings): WidgetSettings {
settings.pageStepIncrement = settings.pageStepIncrement ?? settings.defaultPageSize;
- settings.tabSortKey = settings.tabSortKey ?? TabSortKey.TIMESTAMP;
+ settings.sortOrder = {
+ property: settings.sortOrder?.property || this.entityFields.createdTime.keyName,
+ direction: settings.sortOrder?.direction || Direction.DESC
+ };
this.pageStepSizeValues = buildPageStepSizeValues(settings.pageStepCount, settings.pageStepIncrement);
return settings;
}
@@ -99,7 +107,16 @@ export class TimeseriesTableWidgetSettingsComponent extends WidgetSettingsCompon
disableStickyHeader: [settings.disableStickyHeader, []],
useRowStyleFunction: [settings.useRowStyleFunction, []],
rowStyleFunction: [settings.rowStyleFunction, [Validators.required]],
- tabSortKey: [settings.tabSortKey, []],
+ sortOrder: this.fb.group({
+ property: [
+ settings.sortOrder.property,
+ Validators.required
+ ],
+ direction: [
+ settings.sortOrder.direction,
+ Validators.required
+ ]
+ })
});
}
@@ -107,6 +124,14 @@ export class TimeseriesTableWidgetSettingsComponent extends WidgetSettingsCompon
return ['useRowStyleFunction', 'displayPagination', 'pageStepCount', 'pageStepIncrement'];
}
+ protected prepareOutputSettings(settings: WidgetSettings): WidgetSettings {
+ settings.sortOrder = {
+ property: settings.sortOrder?.property || this.entityFields.createdTime.keyName,
+ direction: settings.sortOrder?.direction || Direction.DESC
+ };
+ return settings;
+ }
+
protected updateValidators(emitEvent: boolean, trigger: string) {
if (trigger === 'pageStepCount' || trigger === 'pageStepIncrement') {
this.timeseriesTableWidgetSettingsForm.get('defaultPageSize').reset();
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts
index dbd4905299..282fab9a3e 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts
@@ -106,19 +106,14 @@ import { ComponentPortal } from '@angular/cdk/portal';
import { FormBuilder } from '@angular/forms';
import { DEFAULT_OVERLAY_POSITIONS } from '@shared/models/overlay.models';
import { DateFormatSettings, ValueFormatProcessor } from '@shared/models/widget-settings.models';
-
-export enum TabSortKey {
- NAME_ASC = 'NAME_ASC',
- NAME_DESC = 'NAME_DESC',
- TIMESTAMP = 'timestamp'
-}
+import { entityFields } from '@shared/models/entity.models';
export interface TimeseriesTableWidgetSettings extends TableWidgetSettings {
showTimestamp: boolean;
showMilliseconds: boolean;
hideEmptyLines: boolean;
dateFormat: DateFormatSettings;
- tabSortKey: TabSortKey;
+ sortOrder: SortOrder;
}
interface TimeseriesWidgetLatestDataKeySettings extends TableWidgetDataKeySettings {
@@ -400,33 +395,39 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
this.updateDatasources();
}
- private getTabLabel(source: Datasource, entityLabelCache:Map):string {
- if (entityLabelCache.has(source.entityId)) {
- return entityLabelCache.get(source.entityId);
- }
-
- const value = this.useEntityLabel
+ private getTabLabel(source: Datasource):string {
+ const value = this.useEntityLabel
? (source.entityLabel || source.entityName)
: source.entityName;
- const translated = this.utils.customTranslation(value);
- entityLabelCache.set(source.entityId, translated);
- return translated;
+ return this.utils.customTranslation(value);
}
- private sortDatasources(source: Datasource[], entityLabelCache:Map): Datasource[] {
- source.forEach(ds => this.getTabLabel(ds, entityLabelCache));
+ private sortDatasources(source: TimeseriesTableSource[]) {
+ const property = this.settings?.sortOrder?.property;
+ const direction = this.settings?.sortOrder?.direction;
+ const isAsc = direction === Direction.ASC;
+
+ if (property === entityFields.name.keyName) {
+ const collator = new Intl.Collator(undefined, {
+ sensitivity: "variant",
+ numeric: true,
+ ignorePunctuation: false
+ });
+
+ source.sort((a, b) => {
+ const valueA = a.displayName || '';
+ const valueB = b.displayName || '';
- if (this.settings.tabSortKey === TabSortKey.TIMESTAMP) {
- return source;
+ return isAsc
+ ? collator.compare(valueA, valueB)
+ : collator.compare(valueB, valueA);
+ });
+ } else if (property === entityFields.createdTime.keyName) {
+ if (isAsc) {
+ source.reverse();
+ }
}
- return source.sort((a, b) => {
- const valueA = entityLabelCache.get(a.entityId);
- const valueB = entityLabelCache.get(b.entityId);
- return this.settings.tabSortKey === TabSortKey.NAME_ASC
- ? valueA.localeCompare(valueB)
- : valueB.localeCompare(valueA);
- });
}
private updateDatasources() {
@@ -434,11 +435,9 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
this.sourceIndex = 0;
let keyOffset = 0;
let latestKeyOffset = 0;
- const entityLabelCache = new Map();
const pageSize = this.displayPagination ? this.defaultPageSize : Number.POSITIVE_INFINITY;
if (this.datasources) {
- const sortedDatasources = this.sortDatasources(this.datasources, entityLabelCache);
- for (const datasource of sortedDatasources) {
+ for (const datasource of this.datasources) {
const sortOrder: SortOrder = sortOrderFromString(this.defaultSortOrder);
const source = {} as TimeseriesTableSource;
source.header = this.prepareHeader(datasource);
@@ -455,7 +454,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
source.pageLink = new PageLink(pageSize, 0, null, sortOrder);
source.rowDataTemplate = {};
source.rowDataTemplate.Timestamp = null;
- source.displayName = entityLabelCache.get(datasource.entityId);
+ source.displayName = this.getTabLabel(datasource);
if (this.showTimestamp) {
source.displayedColumns.push('0');
}
@@ -475,6 +474,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
}
}
if (this.sources.length) {
+ this.sortDatasources(this.sources);
this.sources.forEach((source, index) => {
this.prepareDisplayedColumn(index);
source.displayedColumns = this.displayedColumns[index].filter(value => value.display).map(value => value.def);
diff --git a/ui-ngx/src/assets/dashboard/api_usage.json b/ui-ngx/src/assets/dashboard/api_usage.json
index 994b431ade..bf3fd220be 100644
--- a/ui-ngx/src/assets/dashboard/api_usage.json
+++ b/ui-ngx/src/assets/dashboard/api_usage.json
@@ -99,7 +99,10 @@
"showTimestamp": true,
"displayPagination": true,
"defaultPageSize": 10,
- "tabSortKey": "NAME_ASC"
+ "sortOrder": {
+ "property": "name",
+ "direction": "ASC"
+ }
},
"title": "{i18n:api-usage.exceptions}",
"dropShadow": true,
diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json
index 57aa7c46c7..e4980f81b0 100644
--- a/ui-ngx/src/assets/locale/locale.constant-en_US.json
+++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json
@@ -1643,6 +1643,8 @@
"documentation": "Documentation",
"time-left": "{{time}} left",
"output": "Output",
+ "sort-asc": "Ascending",
+ "sort-desc": "Descending",
"suffix": {
"s": "s",
"ms": "ms"
@@ -9542,8 +9544,6 @@
"show-cell-actions-menu-mobile": "Show cell actions dropdown menu in mobile mode",
"disable-sorting": "Disable sorting",
"sort-by": "Sort tabs by",
- "sort-asc": "Name Ascending",
- "sort-desc": "Name Descending",
"sort-timestamp-option": "Created time"
},
"latest-chart": {
diff --git a/ui-ngx/src/form.scss b/ui-ngx/src/form.scss
index af166b406d..b8e269c14e 100644
--- a/ui-ngx/src/form.scss
+++ b/ui-ngx/src/form.scss
@@ -257,6 +257,12 @@
flex: 1;
}
}
+ &.flex-lt-lg {
+ @media #{$mat-lt-lg} {
+ width: auto;
+ flex: 1;
+ }
+ }
}
.fixed-title-width {
min-width: 200px;