This is an automated email from the ASF dual-hosted git repository.
riemer pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampipes.git
The following commit(s) were added to refs/heads/dev by this push:
new 9c36aabf2c feat(#3992): Improve widget handling in dashboard (#4011)
9c36aabf2c is described below
commit 9c36aabf2cdb65d98f6e7cbf00e7d74203fb02cf
Author: Dominik Riemer <[email protected]>
AuthorDate: Wed Dec 3 21:21:17 2025 +0100
feat(#3992): Improve widget handling in dashboard (#4011)
---
.../streampipes/model/dashboard/DashboardItem.java | 37 +++
.../management/DataExplorerResourceManager.java | 3 +-
.../datalake/KioskDashboardDataLakeResource.java | 2 +-
.../core/migrations/AvailableMigrations.java | 4 +-
.../v099/UniqueDashboardIdMigration.java | 73 +++++
ui/package-lock.json | 32 +--
ui/package.json | 2 +-
.../streampipes/platform-services/package.json | 3 +-
.../src/lib/model/dashboard/dashboard.model.ts | 15 +-
.../static-free-input.component.ts | 1 -
.../kiosk/dashboard-kiosk.component.html | 4 +-
.../kiosk/dashboard-kiosk.component.scss | 1 +
.../components/kiosk/dashboard-kiosk.component.ts | 2 +-
.../chart-view/abstract-chart-view.directive.ts | 39 +--
.../grid-view/dashboard-grid-view.component.html | 70 +++--
.../grid-view/dashboard-grid-view.component.scss | 31 +--
.../grid-view/dashboard-grid-view.component.ts | 87 +++---
.../slide-view/dashboard-slide-view.component.html | 6 +-
.../slide-view/dashboard-slide-view.component.ts | 33 +--
.../dashboard-shared/dashboard-shared.module.ts | 4 +-
.../chart-selection-panel.component.html | 5 -
.../panel/dashboard-panel.component.scss | 2 +-
.../components/panel/dashboard-panel.component.ts | 28 +-
ui/src/app/dashboard/dashboard.module.ts | 2 -
.../data-explorer-chart-container.component.html | 298 ++++++++++-----------
.../data-explorer-chart-container.component.scss | 4 +-
.../data-explorer-chart-container.component.ts | 98 ++++---
.../base/base-data-explorer-widget.directive.ts | 66 ++---
.../charts/base/echarts-widget.component.html | 2 +-
.../charts/base/echarts-widget.component.ts | 20 +-
.../charts/gauge/gauge-renderer.service.ts | 10 +-
.../charts/heatmap/heatmap-renderer.service.ts | 5 +-
.../charts/image/image-widget.component.ts | 5 +-
.../charts/status/status-widget.component.html | 19 +-
.../charts/status/status-widget.component.scss | 22 +-
.../charts/status/status-widget.component.ts | 11 +-
.../charts/table/table-widget.component.ts | 2 +-
.../traffic-light-widget.component.html | 40 +--
.../traffic-light-widget.component.scss | 37 ++-
.../traffic-light-widget.component.ts | 20 +-
.../data-explorer-shared.module.ts | 2 -
.../models/dataview-dashboard.model.ts | 13 +-
.../models/gridster-info.model.ts | 9 +-
.../services/resize.service.ts | 6 +-
.../data-explorer-chart-view.component.html | 5 +-
.../data-explorer-chart-view.component.ts | 14 +-
ui/src/app/data-explorer/data-explorer.module.ts | 3 -
ui/src/app/editor/editor.module.ts | 2 -
ui/src/scss/main.scss | 2 +
49 files changed, 587 insertions(+), 614 deletions(-)
diff --git
a/streampipes-model/src/main/java/org/apache/streampipes/model/dashboard/DashboardItem.java
b/streampipes-model/src/main/java/org/apache/streampipes/model/dashboard/DashboardItem.java
index 81759642cb..108cfd68ef 100644
---
a/streampipes-model/src/main/java/org/apache/streampipes/model/dashboard/DashboardItem.java
+++
b/streampipes-model/src/main/java/org/apache/streampipes/model/dashboard/DashboardItem.java
@@ -25,6 +25,7 @@ public class DashboardItem {
private String id;
private String name;
private String component;
+ private String dataViewElementId;
private List<String> settings;
@@ -33,6 +34,9 @@ public class DashboardItem {
private Integer x;
private Integer y;
private Map<String, Object> timeSettings;
+ private String widgetId;
+ private Integer w;
+ private Integer h;
public DashboardItem() {
@@ -109,4 +113,37 @@ public class DashboardItem {
public void setTimeSettings(Map<String, Object> timeSettings) {
this.timeSettings = timeSettings;
}
+
+ @Deprecated(since = "0.99.0", forRemoval = true)
+ public String getWidgetId() {
+ return widgetId;
+ }
+
+ public void setWidgetId(String widgetId) {
+ this.widgetId = widgetId;
+ }
+
+ public Integer getW() {
+ return w;
+ }
+
+ public void setW(Integer w) {
+ this.w = w;
+ }
+
+ public Integer getH() {
+ return h;
+ }
+
+ public void setH(Integer h) {
+ this.h = h;
+ }
+
+ public String getDataViewElementId() {
+ return dataViewElementId;
+ }
+
+ public void setDataViewElementId(String dataViewElementId) {
+ this.dataViewElementId = dataViewElementId;
+ }
}
diff --git
a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/DataExplorerResourceManager.java
b/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/DataExplorerResourceManager.java
index 6a2f8cf867..a55d0fb42e 100644
---
a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/DataExplorerResourceManager.java
+++
b/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/DataExplorerResourceManager.java
@@ -40,7 +40,8 @@ public class DataExplorerResourceManager extends
CrudResourceManager<DashboardMo
public CompositeDashboardModel getCompositeDashboard(String dashboardId) {
var dashboard = db.getElementById(dashboardId);
- var widgets = dashboard.getWidgets().stream().map(w ->
widgetStorage.getElementById(w.getId())).toList();
+ var widgets = dashboard.getWidgets().stream()
+ .map(w ->
widgetStorage.getElementById(w.getDataViewElementId())).toList();
var dataLakeMeasures =
getMeasureNames(widgets).stream().map(dataLakeMeasureStorage::getByMeasureName).toList();
return new CompositeDashboardModel(dashboard, widgets, dataLakeMeasures);
diff --git
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/KioskDashboardDataLakeResource.java
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/KioskDashboardDataLakeResource.java
index 76f99977fd..885fd80dff 100644
---
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/KioskDashboardDataLakeResource.java
+++
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/KioskDashboardDataLakeResource.java
@@ -76,7 +76,7 @@ public class KioskDashboardDataLakeResource extends
AbstractAuthGuardedRestResou
@PathVariable("widgetId") String widgetId,
@RequestBody Map<String, String>
queryParams) {
var dashboard = dashboardStorage.getElementById(dashboardId);
- if (dashboard.getWidgets().stream().noneMatch(w ->
w.getId().equals(widgetId))) {
+ if (dashboard.getWidgets().stream().noneMatch(w ->
w.getDataViewElementId().equals(widgetId))) {
return badRequest(String.format("Widget with id %s not found in
dashboard", widgetId));
}
var widget = dataExplorerWidgetStorage.getElementById(widgetId);
diff --git
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/AvailableMigrations.java
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/AvailableMigrations.java
index deedfd63ca..c1f72c466c 100644
---
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/AvailableMigrations.java
+++
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/AvailableMigrations.java
@@ -34,6 +34,7 @@ import
org.apache.streampipes.service.core.migrations.v099.AddAssetManagementVie
import
org.apache.streampipes.service.core.migrations.v099.CreateAssetPermissionMigration;
import
org.apache.streampipes.service.core.migrations.v099.MoveAssetContentMigration;
import
org.apache.streampipes.service.core.migrations.v099.RemoveObsoletePrivilegesMigration;
+import
org.apache.streampipes.service.core.migrations.v099.UniqueDashboardIdMigration;
import
org.apache.streampipes.service.core.migrations.v970.AddDataLakePipelineTemplateMigration;
import
org.apache.streampipes.service.core.migrations.v970.AddLinkSettingsMigration;
import
org.apache.streampipes.service.core.migrations.v970.AddRolesToUserDbMigration;
@@ -68,7 +69,8 @@ public class AvailableMigrations {
new AddAssetManagementViewMigration(),
new MoveAssetContentMigration(),
new CreateAssetPermissionMigration(),
- new RemoveObsoletePrivilegesMigration()
+ new RemoveObsoletePrivilegesMigration(),
+ new UniqueDashboardIdMigration()
);
}
}
diff --git
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v099/UniqueDashboardIdMigration.java
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v099/UniqueDashboardIdMigration.java
new file mode 100644
index 0000000000..4bdd2bfe6b
--- /dev/null
+++
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v099/UniqueDashboardIdMigration.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.streampipes.service.core.migrations.v099;
+
+import org.apache.streampipes.model.dashboard.DashboardModel;
+import org.apache.streampipes.service.core.migrations.Migration;
+import org.apache.streampipes.storage.api.CRUDStorage;
+import org.apache.streampipes.storage.management.StorageDispatcher;
+
+import org.apache.commons.lang3.RandomStringUtils;
+
+import java.io.IOException;
+import java.util.Objects;
+
+public class UniqueDashboardIdMigration implements Migration {
+
+ private final CRUDStorage<DashboardModel> dashboardStorage;
+ private static final String Prefix = "sp:dataexplorerwidgetmodel";
+
+ public UniqueDashboardIdMigration() {
+ this.dashboardStorage =
StorageDispatcher.INSTANCE.getNoSqlStore().getDataExplorerDashboardStorage();
+ }
+
+ @Override
+ public boolean shouldExecute() {
+ return dashboardStorage
+ .findAll()
+ .stream()
+ .anyMatch(d -> d.getWidgets()
+ .stream()
+ .anyMatch(w -> Objects.nonNull(w.getId()) &&
w.getId().startsWith(Prefix)));
+ }
+
+ @Override
+ public void executeMigration() throws IOException {
+ var allDashboards = dashboardStorage.findAll();
+
+ allDashboards.forEach(d -> {
+ d.getWidgets().forEach(w -> {
+ if (Objects.nonNull(w.getId()) && w.getId().startsWith(Prefix)) {
+ w.setDataViewElementId(w.getId());
+ var uniqueDashboardWidgetId = Objects.nonNull(w.getId())
+ ? w.getId()
+ : RandomStringUtils.randomAlphanumeric(16);
+ w.setId(uniqueDashboardWidgetId);
+ w.setWidgetId(null);
+ }
+ });
+ dashboardStorage.updateElement(d);
+ });
+ }
+
+ @Override
+ public String getDescription() {
+ return "Moving dashboard widget IDs to dataViewElementId field and
generating new unique IDs for widgets.";
+ }
+}
diff --git a/ui/package-lock.json b/ui/package-lock.json
index eba111ede7..1049ed3bb5 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -31,7 +31,6 @@
"@ngx-translate/core": "^16.0.4",
"@ngx-translate/http-loader": "^16.0.1",
"@panzoom/panzoom": "^4.5.1",
- "angular-gridster2": "^19.0.0",
"codemirror": "^5.65.11",
"console-browserify": "^1.2.0",
"d3-array": "^3.2.4",
@@ -41,6 +40,7 @@
"echarts": "^5.6.0",
"echarts-simple-transform": "^1.0.0",
"file-saver": "2.0.5",
+ "gridstack": "^12.3.3",
"jquery": "^3.7.0",
"jquery-ui-dist": "1.13.2",
"jshint": "^2.13.6",
@@ -7857,20 +7857,6 @@
"ajv": "^8.8.2"
}
},
- "node_modules/angular-gridster2": {
- "version": "19.0.0",
- "resolved":
"https://registry.npmjs.org/angular-gridster2/-/angular-gridster2-19.0.0.tgz",
- "integrity":
"sha512-82SHZzwOmGRvR77VtbpV5Eh7CoTtxLslwOVzTYB3qNQIGGFaOsS8nRAuYpZOlZpVc+n6fBz1HU0yP0icnQ9ppg==",
- "license": "MIT",
- "dependencies": {
- "tslib": "^2.4.0"
- },
- "peerDependencies": {
- "@angular/common": "^19.0.0",
- "@angular/core": "^19.0.0",
- "rxjs": "^7.0.0"
- }
- },
"node_modules/ansi-colors": {
"version": "4.1.3",
"resolved":
"https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
@@ -12581,6 +12567,22 @@
"lodash": "^4.17.15"
}
},
+ "node_modules/gridstack": {
+ "version": "12.3.3",
+ "resolved":
"https://registry.npmjs.org/gridstack/-/gridstack-12.3.3.tgz",
+ "integrity":
"sha512-Bboi4gj7HXGnx1VFXQNde4Nwi5srdUSuCCnOSszKhFjBs8EtMEWhsKX02BjIKkErq/FjQUkNUbXUYeQaVMQ0jQ==",
+ "funding": [
+ {
+ "type": "paypal",
+ "url": "https://www.paypal.me/alaind831"
+ },
+ {
+ "type": "venmo",
+ "url": "https://www.venmo.com/adumesny"
+ }
+ ],
+ "license": "MIT"
+ },
"node_modules/hachure-fill": {
"version": "0.5.2",
"resolved":
"https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz",
diff --git a/ui/package.json b/ui/package.json
index a7fca186b3..de4d772674 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -52,7 +52,6 @@
"@ngx-translate/core": "^16.0.4",
"@ngx-translate/http-loader": "^16.0.1",
"@panzoom/panzoom": "^4.5.1",
- "angular-gridster2": "^19.0.0",
"codemirror": "^5.65.11",
"console-browserify": "^1.2.0",
"d3-array": "^3.2.4",
@@ -61,6 +60,7 @@
"date-fns": "^3.6.0",
"echarts": "^5.6.0",
"echarts-simple-transform": "^1.0.0",
+ "gridstack": "^12.3.3",
"file-saver": "2.0.5",
"jquery": "^3.7.0",
"jquery-ui-dist": "1.13.2",
diff --git a/ui/projects/streampipes/platform-services/package.json
b/ui/projects/streampipes/platform-services/package.json
index 9ed63a5dd7..49bd897560 100644
--- a/ui/projects/streampipes/platform-services/package.json
+++ b/ui/projects/streampipes/platform-services/package.json
@@ -6,8 +6,7 @@
"@angular/core": "^19.2.13",
"@angular/platform-browser": "^19.2.13",
"@ngx-loading-bar/http-client": "6.0.2",
- "rxjs": "^7.8.0",
- "angular-gridster2": "^19.0.0"
+ "rxjs": "^7.8.0"
},
"dependencies": {
"tslib": "^2.6.2"
diff --git
a/ui/projects/streampipes/platform-services/src/lib/model/dashboard/dashboard.model.ts
b/ui/projects/streampipes/platform-services/src/lib/model/dashboard/dashboard.model.ts
index 70c2e3e3ef..7ae48ef399 100644
---
a/ui/projects/streampipes/platform-services/src/lib/model/dashboard/dashboard.model.ts
+++
b/ui/projects/streampipes/platform-services/src/lib/model/dashboard/dashboard.model.ts
@@ -16,23 +16,24 @@
*
*/
-import { GridsterConfig, GridsterItem } from 'angular-gridster2';
import { TimeSettings } from '../datalake/DateRange';
import {
- DashboardModel,
DataExplorerWidgetModel,
DataLakeMeasure,
ResourceMetadata,
} from '../gen/streampipes-model';
-// tslint:disable-next-line:no-empty-interface
-export interface DashboardConfig extends GridsterConfig {}
-
-export interface ClientDashboardItem extends GridsterItem {
- widgetId: string;
+export interface ClientDashboardItem {
+ dataViewElementId: string;
widgetType: string;
timeSettings?: TimeSettings;
id: string;
+ cols?: number;
+ rows?: number;
+ x: number;
+ y: number;
+ w?: number;
+ h?: number;
}
export interface DashboardLiveSettings {
diff --git
a/ui/src/app/core-ui/static-properties/static-free-input/static-free-input.component.ts
b/ui/src/app/core-ui/static-properties/static-free-input/static-free-input.component.ts
index f5a963b626..1ca578e426 100644
---
a/ui/src/app/core-ui/static-properties/static-free-input/static-free-input.component.ts
+++
b/ui/src/app/core-ui/static-properties/static-free-input/static-free-input.component.ts
@@ -72,7 +72,6 @@ export class StaticFreeInputComponent
this.addValidator(this.staticProperty.value, this.collectValidators());
this.enableValidators();
this.emitUpdate();
- console.log(this.staticProperty);
}
collectValidators() {
diff --git
a/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.html
b/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.html
index c6550df07e..401e6e2b15 100644
--- a/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.html
+++ b/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.html
@@ -18,7 +18,7 @@
<div
fxLayout="column"
- style="height: 100vh"
+ style="height: 100%"
class="light-mode w-100 standalone-outer"
>
<mat-toolbar class="standalone-toolbar">
@@ -55,7 +55,7 @@
</div>
</div>
</mat-toolbar>
- <div fxLayout="column" style="height: calc(100vh - 40px)">
+ <div fxLayout="column" style="height: calc(100% - 40px)">
@if (dashboard) {
@if (dashboard?.widgets.length > 0) {
<sp-dashboard-grid-view
diff --git
a/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.scss
b/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.scss
index 0937b31c4c..76ab40d896 100644
--- a/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.scss
+++ b/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.scss
@@ -22,6 +22,7 @@
.standalone-outer {
background: var(--color-bg-1);
+ min-height: 100vh;
}
.dashboard-grid {
diff --git
a/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.ts
b/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.ts
index 1e7009994e..b4ccb7154a 100644
--- a/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.ts
+++ b/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.ts
@@ -63,7 +63,7 @@ export class DashboardKioskComponent implements OnInit,
OnDestroy {
if (res.ok) {
const cd = res.body;
cd.dashboard.widgets.forEach(w => {
- w.widgetId ??=
+ w.id ??=
this.dataExplorerDashboardService.makeUniqueWidgetId();
});
const eTag = res.headers.get('ETag');
diff --git
a/ui/src/app/dashboard-shared/components/chart-view/abstract-chart-view.directive.ts
b/ui/src/app/dashboard-shared/components/chart-view/abstract-chart-view.directive.ts
index 324bae85f1..0b18b81cc3 100644
---
a/ui/src/app/dashboard-shared/components/chart-view/abstract-chart-view.directive.ts
+++
b/ui/src/app/dashboard-shared/components/chart-view/abstract-chart-view.directive.ts
@@ -19,18 +19,17 @@
import { Directive, EventEmitter, inject, Input, Output } from '@angular/core';
import {
ChartService,
+ ClientDashboardItem,
Dashboard,
DataExplorerWidgetModel,
DataLakeMeasure,
TimeSettings,
} from '@streampipes/platform-services';
-import { ResizeService } from
'../../../data-explorer-shared/services/resize.service';
import { DataExplorerChartRegistry } from
'../../../data-explorer-shared/registry/data-explorer-chart-registry';
import { ObservableGenerator } from
'../../../data-explorer-shared/models/dataview-dashboard.model';
@Directive()
export abstract class AbstractChartViewDirective {
- protected resizeService = inject(ResizeService);
protected dataViewDataExplorerService = inject(ChartService);
protected widgetRegistryService = inject(DataExplorerChartRegistry);
@@ -43,9 +42,6 @@ export abstract class AbstractChartViewDirective {
@Input()
editMode: boolean;
- @Input()
- currentlyConfiguredWidgetId: string;
-
@Input()
observableGenerator: ObservableGenerator;
@@ -73,13 +69,14 @@ export abstract class AbstractChartViewDirective {
startEditMode(value: DataExplorerWidgetModel) {
this.startEditModeEmitter.emit(value);
- this.currentlyConfiguredWidgetId = value.elementId;
}
loadWidgetConfigs() {
this.dashboard.widgets.forEach(widgetConfig => {
+ widgetConfig.w ??= widgetConfig.cols;
+ widgetConfig.h ??= widgetConfig.rows;
const availableWidget = this.widgets.find(
- w => w.elementId === widgetConfig.id,
+ w => w.elementId === widgetConfig.dataViewElementId,
);
this.processWidget(availableWidget);
});
@@ -87,23 +84,16 @@ export abstract class AbstractChartViewDirective {
this.widgetsAvailable = true;
}
- loadWidgetConfig(widgetId: string, setCurrentlyConfigured?: boolean) {
+ loadWidgetConfig(dashboardItem: ClientDashboardItem) {
if (!this.isGridView()) {
this.widgetsAvailable = false;
}
this.dataViewDataExplorerService
- .getChart(widgetId)
+ .getChart(dashboardItem.dataViewElementId)
.subscribe(response => {
this.processWidget(response);
- if (setCurrentlyConfigured) {
- this.propagateWidgetSelection(
- this.configuredWidgets.get(widgetId),
- );
- if (!this.isGridView()) {
- this.selectNewWidget(widgetId);
- }
- }
if (!this.isGridView()) {
+ this.selectNewWidget(dashboardItem.id);
this.widgetsVisible = true;
}
this.widgetsAvailable = true;
@@ -123,21 +113,6 @@ export abstract class AbstractChartViewDirective {
}
}
- propagateItemRemoval(widgetIndex: number) {
- this.deleteCallback.emit(widgetIndex);
- }
-
- propagateWidgetSelection(configuredWidget: DataExplorerWidgetModel) {
- if (configuredWidget) {
- this.currentlyConfiguredWidgetId = configuredWidget.elementId;
- } else {
- this.currentlyConfiguredWidgetId = undefined;
- }
- this.onOptionsChanged();
- }
-
- abstract onOptionsChanged(): void;
-
abstract onWidgetsAvailable(): void;
abstract isGridView(): boolean;
diff --git
a/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.html
b/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.html
index cfa89e8de7..5929888514 100644
---
a/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.html
+++
b/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.html
@@ -22,41 +22,39 @@
<h3>{{ dashboard.description }}</h3>
</div>
}
-<gridster
- [options]="options"
- [ngClass]="editMode ? 'edit' : ''"
- class="custom-gridster-style"
+<gridstack
+ [options]="gridOptions"
+ (changeCB)="onGridChange($event)"
+ class="dashboard-outer"
+ #grid
>
- @for (item of dashboard.widgets; let i = $index; track item.widgetId) {
- <ng-container>
- <gridster-item
- [item]="item"
- #gridsterItemComponent
- class="widget-outer"
- >
- @if (widgetsAvailable && configuredWidgets.has(item.id)) {
- <sp-data-explorer-chart-container
- [ngStyle]="{
- height: gridsterItemComponent.height - 13 + 'px'
- }"
- [timeSettings]="timeSettings"
- [globalTimeEnabled]="
-
dashboard.dashboardGeneralSettings.globalTimeEnabled
- "
- (deleteCallback)="propagateItemRemoval($event)"
- (startEditModeEmitter)="startEditMode($event)"
- [dashboardItem]="item"
- [configuredWidget]="configuredWidgets.get(item.id)"
- [dataLakeMeasure]="dataLakeMeasures.get(item.id)"
- [observableGenerator]="observableGenerator"
- [editMode]="editMode"
- [kioskMode]="kioskMode"
- [gridMode]="true"
- [widgetIndex]="i"
- [gridsterItemComponent]="gridsterItemComponent"
- ></sp-data-explorer-chart-container>
- }
- </gridster-item>
- </ng-container>
+ @for (item of dashboard.widgets; let i = $index; track item.id) {
+ <gridstack-item [options]="item">
+ @if (
+ widgetsAvailable &&
+ configuredWidgets.has(item.dataViewElementId)
+ ) {
+ <sp-data-explorer-chart-container
+ [timeSettings]="timeSettings"
+ [globalTimeEnabled]="
+ dashboard.dashboardGeneralSettings.globalTimeEnabled
+ "
+ (deleteCallback)="deleteCallback.emit($event)"
+ (startEditModeEmitter)="startEditMode($event)"
+ [dashboardItem]="item"
+ [configuredWidget]="
+ configuredWidgets.get(item.dataViewElementId)
+ "
+ [dataLakeMeasure]="
+ dataLakeMeasures.get(item.dataViewElementId)
+ "
+ [observableGenerator]="observableGenerator"
+ [editMode]="editMode"
+ [kioskMode]="kioskMode"
+ [gridMode]="true"
+ [widgetIndex]="i"
+ ></sp-data-explorer-chart-container>
+ }
+ </gridstack-item>
}
-</gridster>
+</gridstack>
diff --git
a/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.scss
b/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.scss
index 77cf1cf14c..98b6560038 100644
---
a/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.scss
+++
b/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.scss
@@ -16,33 +16,10 @@
*
*/
-gridster.custom-gridster-style ::ng-deep {
- background: var(--color-bg-1);
-}
-
-gridster.custom-gridster-style.edit ::ng-deep {
- background: var(--mat-color-bg-1);
-}
-
-gridster.scrollVertical ::ng-deep {
- min-height: 100%;
- display: flex;
- flex: 1 1 100%;
-}
-
-::ng-deep gridster.custom-gridster-style > .gridster-row {
- border-bottom: 1px solid var(--mat-sys-outline-variant);
- border-top: 1px solid var(--mat-sys-outline-variant);
-}
-
-::ng-deep gridster.custom-gridster-style > div.gridster-column {
- border-left: 1px solid var(--mat-sys-outline-variant);
- border-right: 1px solid var(--mat-sys-outline-variant);
-}
-
.widget-outer {
- //box-shadow:
- // rgba(50, 50, 93, 0.25) 0px 2px 5px -1px,
- // rgba(0, 0, 0, 0.3) 0px 1px 3px -1px;
border: 1px solid var(--color-bg-2);
}
+
+.dashboard-outer {
+ margin: 5px;
+}
diff --git
a/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.ts
b/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.ts
index 4e8d67e906..cf0d893425 100644
---
a/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.ts
+++
b/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.ts
@@ -17,22 +17,17 @@
*/
import {
+ AfterViewInit,
Component,
Input,
OnChanges,
OnInit,
- QueryList,
SimpleChanges,
- ViewChildren,
+ ViewChild,
} from '@angular/core';
-import {
- DisplayGrid,
- GridsterItemComponent,
- GridType,
-} from 'angular-gridster2';
-import { GridsterInfo } from
'../../../../data-explorer-shared/models/gridster-info.model';
-import { IDataViewDashboardConfig } from
'../../../../data-explorer-shared/models/dataview-dashboard.model';
import { AbstractChartViewDirective } from '../abstract-chart-view.directive';
+import { GridStack, GridStackOptions } from 'gridstack';
+import { GridstackComponent, nodesCB } from 'gridstack/dist/angular';
@Component({
selector: 'sp-dashboard-grid-view',
@@ -42,60 +37,60 @@ import { AbstractChartViewDirective } from
'../abstract-chart-view.directive';
})
export class DashboardGridViewComponent
extends AbstractChartViewDirective
- implements OnInit, OnChanges
+ implements OnInit, AfterViewInit, OnChanges
{
@Input()
kioskMode = false;
- options: IDataViewDashboardConfig;
loaded = false;
- @ViewChildren(GridsterItemComponent)
- gridsterItemComponents: QueryList<GridsterItemComponent>;
+ @ViewChild('grid', { static: true })
+ gridComp: GridstackComponent;
+
+ grid: GridStack;
+
+ gridOptions: GridStackOptions = {};
+
+ ngAfterViewInit() {
+ this.grid = this.gridComp.grid;
+ }
ngOnInit(): void {
this.loadWidgetConfigs();
- this.options = {
- disablePushOnDrag: true,
- draggable: { enabled: this.editMode },
- gridType: GridType.VerticalFixed,
- minCols: this.dashboard.gridColumns,
- maxCols: this.dashboard.gridColumns,
- minRows: 4,
- fixedRowHeight: 100,
- fixedColWidth: 100,
- margin: 3,
- displayGrid: this.editMode
- ? DisplayGrid.OnDragAndResize
- : DisplayGrid.None,
- resizable: { enabled: this.editMode },
- itemResizeCallback: (item, itemComponent) => {
- this.resizeService.notify({
- gridsterItem: item,
- gridsterItemComponent: itemComponent,
- } as GridsterInfo);
- },
- itemInitCallback: (item, itemComponent) => {
- this.resizeService.notify({
- gridsterItem: item,
- gridsterItemComponent: itemComponent,
- } as GridsterInfo);
- window.dispatchEvent(new Event('resize'));
+ this.gridOptions = {
+ minRow: 5,
+ column: this.dashboard.gridColumns,
+ margin: 2,
+ cellHeight: 'initial',
+ disableResize: !this.editMode,
+ disableDrag: !this.editMode,
+ float: true,
+ resizable: {
+ handles: 'w,e,se',
},
};
}
ngOnChanges(changes: SimpleChanges): void {
- if (changes['editMode'] && this.options) {
- this.options.draggable.enabled = this.editMode;
- this.options.resizable.enabled = this.editMode;
- this.options.displayGrid = this.editMode ? 'always' : 'none';
- this.options.api.optionsChanged();
+ if (changes['editMode'] && this.grid) {
+ this.gridOptions.disableResize = !this.editMode;
+ this.gridOptions.disableDrag = !this.editMode;
+ this.grid.updateOptions(this.gridOptions);
}
}
- onOptionsChanged() {
- this.options.api.optionsChanged();
+ onGridChange(data: nodesCB): void {
+ data.nodes.forEach(changed => {
+ const widget = this.dashboard.widgets.find(
+ w => w.id === (changed as any).id,
+ );
+ if (widget) {
+ widget.x = changed.x;
+ widget.y = changed.y;
+ widget.w = changed.w;
+ widget.h = changed.h;
+ }
+ });
}
onWidgetsAvailable(): void {}
diff --git
a/ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.html
b/ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.html
index 660c84dd6c..2d35c6a182 100644
---
a/ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.html
+++
b/ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.html
@@ -55,14 +55,11 @@
widgetsVisible
) {
<sp-data-explorer-chart-container
- [ngStyle]="{
- height: gridsterItemComponent.height - 15 + 'px'
- }"
[timeSettings]="timeSettings"
[globalTimeEnabled]="
dashboard.dashboardGeneralSettings.globalTimeEnabled
"
- (deleteCallback)="propagateItemRemoval($event)"
+ (deleteCallback)="deleteCallback.emit($event)"
(startEditModeEmitter)="startEditMode($event)"
[dashboardItem]="currentDashboardItem"
[configuredWidget]="currentWidget"
@@ -71,7 +68,6 @@
[editMode]="editMode"
[gridMode]="false"
[widgetIndex]="i"
- [gridsterItemComponent]="gridsterItemComponent"
></sp-data-explorer-chart-container>
}
</div>
diff --git
a/ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.ts
b/ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.ts
index c8c76a1f6e..835fb2762d 100644
---
a/ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.ts
+++
b/ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.ts
@@ -16,17 +16,10 @@
*
*/
-import {
- AfterViewInit,
- Component,
- ElementRef,
- OnInit,
- ViewChild,
-} from '@angular/core';
+import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { AbstractChartViewDirective } from '../abstract-chart-view.directive';
import {
ClientDashboardItem,
- DashboardItem,
DataExplorerWidgetModel,
DataLakeMeasure,
} from '@streampipes/platform-services';
@@ -39,12 +32,9 @@ import {
})
export class DashboardSlideViewComponent
extends AbstractChartViewDirective
- implements OnInit, AfterViewInit
+ implements OnInit
{
selectedWidgetIndex = 0;
-
- gridsterItemComponent: any = { width: 100, height: 100 };
-
currentWidget: DataExplorerWidgetModel;
currentMeasure: DataLakeMeasure;
currentDashboardItem: ClientDashboardItem;
@@ -64,29 +54,10 @@ export class DashboardSlideViewComponent
this.currentWidget = this.configuredWidgets.get(widgetId);
this.currentMeasure = this.dataLakeMeasures.get(widgetId);
this.currentDashboardItem = this.dashboard.widgets[index];
- this.currentlyConfiguredWidgetId = widgetId;
this.displayWidget = true;
});
}
- ngAfterViewInit(): void {
- const obs = new ResizeObserver(entries => {
- entries.forEach(entry => {
- const cr = entry.contentRect;
- this.gridsterItemComponent.width = cr.width;
- this.gridsterItemComponent.height = cr.height;
- this.resizeService.notify({
- gridsterItem:
- this.dashboard.widgets[this.selectedWidgetIndex],
- gridsterItemComponent: this.gridsterItemComponent,
- });
- });
- });
- obs.observe(document.getElementById('slideViewOuter'));
- }
-
- onOptionsChanged() {}
-
onWidgetsAvailable(): void {
this.selectWidget(0, this.dashboard.widgets[0].id);
}
diff --git a/ui/src/app/dashboard-shared/dashboard-shared.module.ts
b/ui/src/app/dashboard-shared/dashboard-shared.module.ts
index 0a4e008572..2ccc3f216e 100644
--- a/ui/src/app/dashboard-shared/dashboard-shared.module.ts
+++ b/ui/src/app/dashboard-shared/dashboard-shared.module.ts
@@ -19,7 +19,6 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@ngbracket/ngx-layout';
-import { GridsterModule } from 'angular-gridster2';
import { MatTabsModule } from '@angular/material/tabs';
import { FormsModule } from '@angular/forms';
import { ColorPickerComponent } from 'ngx-color-picker';
@@ -60,6 +59,7 @@ import { DataExplorerSharedModule } from
'../data-explorer-shared/data-explorer-
import { DashboardGridViewComponent } from
'./components/chart-view/grid-view/dashboard-grid-view.component';
import { DashboardSlideViewComponent } from
'./components/chart-view/slide-view/dashboard-slide-view.component';
import { TranslateModule } from '@ngx-translate/core';
+import { GridstackModule } from 'gridstack/dist/angular';
@NgModule({
imports: [
@@ -88,7 +88,6 @@ import { TranslateModule } from '@ngx-translate/core';
CommonModule,
CoreUiModule,
MatTabsModule,
- GridsterModule,
FlexLayoutModule,
FormsModule,
ColorPickerComponent,
@@ -103,6 +102,7 @@ import { TranslateModule } from '@ngx-translate/core';
ServicesModule,
SharedUiModule,
DataExplorerSharedModule,
+ GridstackModule,
TranslateModule.forChild(),
],
declarations: [DashboardGridViewComponent, DashboardSlideViewComponent],
diff --git
a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection-panel.component.html
b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection-panel.component.html
index 1c4f323cb4..37166e0a2e 100644
---
a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection-panel.component.html
+++
b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection-panel.component.html
@@ -17,11 +17,6 @@
-->
<div fxFlex="100" class="designer-panel-content" fxLayout="column">
- <div fxLayout="row" class="sp-tab-bg designer-panel-header">
- <div fxLayoutAlign="start center" class="designer-panel-title">
- <span> {{ 'Configure dashboard' | translate }} </span>
- </div>
- </div>
<div fxFlex="100" fxLayout="column" class="no-overflow">
<mat-tab-group
color="accent"
diff --git
a/ui/src/app/dashboard/components/panel/dashboard-panel.component.scss
b/ui/src/app/dashboard/components/panel/dashboard-panel.component.scss
index ad4770c30a..6b94dd13a0 100644
--- a/ui/src/app/dashboard/components/panel/dashboard-panel.component.scss
+++ b/ui/src/app/dashboard/components/panel/dashboard-panel.component.scss
@@ -40,7 +40,7 @@
}
.designer-panel {
- width: 450px;
+ width: 350px;
}
.edit-menu-btn {
diff --git a/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
b/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
index 69879120c9..50e88b20c2 100644
--- a/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
+++ b/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
@@ -85,8 +85,8 @@ export class DashboardPanelComponent
public items: Dashboard[];
dataLakeMeasure: DataLakeMeasure;
- authSubscription: Subscription;
- refreshSubscription: Subscription;
+ auth$: Subscription;
+ refresh$: Subscription;
private detectChangesService = inject(ChartDetectChangesService);
private dialog = inject(MatDialog);
@@ -113,7 +113,7 @@ export class DashboardPanelComponent
this.getDashboard(params.id, startTime, endTime);
- this.authSubscription = this.currentUserService.user$.subscribe(_ => {
+ this.auth$ = this.currentUserService.user$.subscribe(_ => {
this.hasDashboardWritePrivileges = this.authService.hasRole(
UserPrivilege.PRIVILEGE_WRITE_DASHBOARD,
);
@@ -124,26 +124,28 @@ export class DashboardPanelComponent
}
ngOnDestroy() {
- this.authSubscription?.unsubscribe();
- this.refreshSubscription?.unsubscribe();
+ this.auth$?.unsubscribe();
+ this.refresh$?.unsubscribe();
}
addChartToDashboard(dataViewElementId: string) {
// eslint-disable-next-line
@typescript-eslint/consistent-type-assertions
const dashboardItem = {} as ClientDashboardItem;
- dashboardItem.id = dataViewElementId;
+ dashboardItem.id =
+ this.dataExplorerDashboardService.makeUniqueWidgetId();
dashboardItem.cols = 3;
dashboardItem.rows = 4;
+ dashboardItem.w = 3;
+ dashboardItem.h = 4;
dashboardItem.x = 0;
dashboardItem.y = 0;
- dashboardItem.widgetId =
- this.dataExplorerDashboardService.makeUniqueWidgetId();
+ dashboardItem.dataViewElementId = dataViewElementId;
this.dashboard.widgets.push(dashboardItem);
setTimeout(() => {
if (this.viewMode === 'grid') {
- this.dashboardGrid.loadWidgetConfig(dataViewElementId, true);
+ this.dashboardGrid.loadWidgetConfig(dashboardItem);
} else {
- this.dashboardSlide.loadWidgetConfig(dataViewElementId, true);
+ this.dashboardSlide.loadWidgetConfig(dashboardItem);
}
});
}
@@ -231,7 +233,7 @@ export class DashboardPanelComponent
if (resp.ok) {
const compositeDashboard = resp.body;
compositeDashboard.dashboard.widgets.forEach(w => {
- w.widgetId ??=
+ w.id ??=
this.dataExplorerDashboardService.makeUniqueWidgetId();
});
this.dashboard = compositeDashboard.dashboard;
@@ -329,14 +331,14 @@ export class DashboardPanelComponent
modifyRefreshInterval(liveSettings: DashboardLiveSettings): void {
this.dashboard.dashboardLiveSettings = liveSettings;
- this.refreshSubscription?.unsubscribe();
+ this.refresh$?.unsubscribe();
if (this.dashboard.dashboardLiveSettings.refreshModeActive) {
this.createQuerySubscription();
}
}
createQuerySubscription() {
- this.refreshSubscription = timer(
+ this.refresh$ = timer(
0,
this.dashboard.dashboardLiveSettings.refreshIntervalInSeconds *
1000,
diff --git a/ui/src/app/dashboard/dashboard.module.ts
b/ui/src/app/dashboard/dashboard.module.ts
index 93e0fc7966..7382505c3f 100644
--- a/ui/src/app/dashboard/dashboard.module.ts
+++ b/ui/src/app/dashboard/dashboard.module.ts
@@ -19,7 +19,6 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@ngbracket/ngx-layout';
-import { GridsterModule } from 'angular-gridster2';
import { MatTabsModule } from '@angular/material/tabs';
import { FormsModule } from '@angular/forms';
import { ColorPickerComponent } from 'ngx-color-picker';
@@ -99,7 +98,6 @@ import { MatProgressSpinnerModule } from
'@angular/material/progress-spinner';
CommonModule,
CoreUiModule,
MatTabsModule,
- GridsterModule,
FlexLayoutModule,
FormsModule,
ColorPickerComponent,
diff --git
a/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.html
b/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.html
index b8997d02c8..bddbd7de47 100644
---
a/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.html
+++
b/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.html
@@ -16,181 +16,165 @@
~
-->
-<div class="h-100">
- <div
- class="box"
- [ngStyle]="{
- background: configuredWidget.baseAppearanceConfig.backgroundColor,
- color: configuredWidget.baseAppearanceConfig.textColor,
- height: gridsterItemComponent.height - 13 + 'px',
- border:
- '2px solid ' +
- configuredWidget.baseAppearanceConfig.backgroundColor
- }"
- [attr.data-cy]="
- 'widget-' + configuredWidget.baseAppearanceConfig.widgetTitle
- "
- >
- @if (!previewMode) {
- <div class="widget-header h-40 w-100">
- <div fxFlex="100" fxLayout="row">
- <div
- fxFlex
- fxLayout="row"
- fxLayoutAlign="start center"
- class="widget-header-text"
- >
- {{ configuredWidget.baseAppearanceConfig.widgetTitle }}
- </div>
- @if (!kioskMode) {
- <div fxLayout="row" fxLayoutAlign="end center">
- @if (editMode) {
- @if (timerActive) {
- <mat-spinner
- [diameter]="20"
- color="primary"
- class="mr-10"
- >
- </mat-spinner>
- }
- <sp-label
- size="small"
- [labelBackground]="
- configuredWidget.baseAppearanceConfig
- .textColor
- "
- [labelText]="loadingTime + 's'"
+<div
+ class="box"
+ [ngStyle]="{
+ background: configuredWidget.baseAppearanceConfig.backgroundColor,
+ color: configuredWidget.baseAppearanceConfig.textColor,
+ height: '100%'
+ }"
+ [attr.data-cy]="
+ 'widget-' + configuredWidget.baseAppearanceConfig.widgetTitle
+ "
+>
+ @if (!previewMode) {
+ <div class="widget-header h-40 w-100">
+ <div fxFlex="100" fxLayout="row">
+ <div
+ fxFlex
+ fxLayout="row"
+ fxLayoutAlign="start center"
+ class="widget-header-text"
+ >
+ {{ configuredWidget.baseAppearanceConfig.widgetTitle }}
+ </div>
+ @if (!kioskMode) {
+ <div fxLayout="row" fxLayoutAlign="end center">
+ @if (editMode) {
+ @if (timerActive) {
+ <mat-spinner
+ [diameter]="20"
+ color="primary"
class="mr-10"
- ></sp-label>
+ >
+ </mat-spinner>
}
- @if (!dataViewMode) {
+ <sp-label
+ size="small"
+ [labelBackground]="
+ configuredWidget.baseAppearanceConfig
+ .textColor
+ "
+ [labelText]="loadingTime + 's'"
+ class="mr-10"
+ ></sp-label>
+ }
+ @if (!dataViewMode) {
+ <button
+ mat-icon-button
+ [matMenuTriggerFor]="menu"
+ [matTooltip]="'More options' | translate"
+ [attr.data-cy]="
+ 'more-options-' +
+
configuredWidget.baseAppearanceConfig.widgetTitle.replaceAll(
+ ' ',
+ ''
+ )
+ "
+ >
+ <mat-icon>more_vert</mat-icon>
+ </button>
+ }
+ <mat-menu #menu="matMenu">
+ <button
+ mat-menu-item
+ (click)="downloadDataAsFile()"
+ >
+ <mat-icon>get_app</mat-icon>
+ <span>{{ 'Download data' | translate }}</span>
+ </button>
+ @if (hasDataExplorerWritePrivileges) {
<button
- mat-icon-button
- [matMenuTriggerFor]="menu"
- [matTooltip]="'More options' | translate"
+ mat-menu-item
+ (click)="startEditMode()"
[attr.data-cy]="
- 'more-options-' +
+ 'start-edit-' +
configuredWidget.baseAppearanceConfig.widgetTitle.replaceAll(
' ',
''
)
"
>
- <mat-icon>more_vert</mat-icon>
+ <mat-icon>edit</mat-icon>
+ <span>{{ 'Edit Chart' | translate }}</span>
</button>
}
- <mat-menu #menu="matMenu">
- <button
- mat-menu-item
- (click)="downloadDataAsFile()"
+ </mat-menu>
+ @if (!globalTimeEnabled) {
+ <button
+ mat-icon-button
+ [matMenuTriggerFor]="optMenu"
+ data-cy="options-data-explorer"
+ #menuTrigger="matMenuTrigger"
+ [matTooltip]="tooltipText"
+ matTooltipClass="no-wrap-tooltip"
+ >
+ <mat-icon
+ [color]="
+ timeSettingsModified
+ ? 'primary'
+ : 'default'
+ "
+ >alarm_clock</mat-icon
+ >
+ </button>
+ }
+ <mat-menu #optMenu="matMenu" class="large-menu">
+ @if (quickSelections) {
+ <sp-time-selector-menu
+ #timeSelectorMenu
+ [timeSettings]="clonedTimeSettings"
+ [quickSelections]="quickSelections"
+ [enableTimePicker]="enableTimePicker"
+ [maxDayRange]="maxDayRange"
+ [labels]="labels"
+ (timeSettingsEmitter)="
+ modifyWidgetTimeSettings($event)
+ "
+ class="w-100"
>
- <mat-icon>get_app</mat-icon>
- <span>{{
- 'Download data' | translate
- }}</span>
- </button>
- @if (hasDataExplorerWritePrivileges) {
<button
- mat-menu-item
- (click)="startEditMode()"
- [attr.data-cy]="
- 'start-edit-' +
-
configuredWidget.baseAppearanceConfig.widgetTitle.replaceAll(
- ' ',
- ''
- )
- "
+ mat-flat-button
+ class="mat-basic"
+ (click)="resetWidgetTimeSettings()"
>
- <mat-icon>edit</mat-icon>
- <span>{{
- 'Edit Chart' | translate
- }}</span>
+ {{ 'Reset' | translate }}
</button>
- }
- </mat-menu>
- @if (!globalTimeEnabled) {
- <button
- mat-icon-button
- [matMenuTriggerFor]="optMenu"
- data-cy="options-data-explorer"
- #menuTrigger="matMenuTrigger"
- [matTooltip]="tooltipText"
- matTooltipClass="no-wrap-tooltip"
- >
- <mat-icon
- [color]="
- timeSettingsModified
- ? 'primary'
- : 'default'
- "
- >alarm_clock</mat-icon
- >
- </button>
- }
- <mat-menu #optMenu="matMenu" class="large-menu">
- @if (quickSelections) {
- <sp-time-selector-menu
- #timeSelectorMenu
- [timeSettings]="clonedTimeSettings"
- [quickSelections]="quickSelections"
- [enableTimePicker]="enableTimePicker"
- [maxDayRange]="maxDayRange"
- [labels]="labels"
- (timeSettingsEmitter)="
- modifyWidgetTimeSettings($event)
- "
- class="w-100"
- >
- <button
- mat-flat-button
- class="mat-basic"
- (click)="resetWidgetTimeSettings()"
- >
- {{ 'Reset' | translate }}
- </button>
- </sp-time-selector-menu>
- }
- </mat-menu>
- @if (
- !dataViewMode &&
- editMode &&
- hasDashboardWritePrivileges
- ) {
- <button
- mat-icon-button
- (click)="removeWidget()"
- [matTooltip]="'Delete Chart' | translate"
- [attr.data-cy]="
- 'remove-' +
- configuredWidget.baseAppearanceConfig
- .widgetTitle
- "
- >
- <mat-icon>clear</mat-icon>
- </button>
+ </sp-time-selector-menu>
}
- </div>
- }
- </div>
+ </mat-menu>
+ @if (
+ !dataViewMode &&
+ editMode &&
+ hasDashboardWritePrivileges
+ ) {
+ <button
+ mat-icon-button
+ (click)="removeWidget()"
+ [matTooltip]="'Delete Chart' | translate"
+ [attr.data-cy]="
+ 'remove-' +
+ configuredWidget.baseAppearanceConfig
+ .widgetTitle
+ "
+ >
+ <mat-icon>clear</mat-icon>
+ </button>
+ }
+ </div>
+ }
</div>
- }
- <div
- fxLayout="column"
- class="widget-content p-0 gridster-item-content ml-0 mr-0 h-100
mw-100"
- >
- <ng-template spWidgetHost class="h-100 p-0"></ng-template>
- @if (errorMessage) {
- <div
- fxFlex="100"
- fxLayout="column"
- fxLayoutAlign="center center"
- >
- <sp-exception-message
- [message]="errorMessage"
- [showDetails]="true"
- ></sp-exception-message>
- </div>
- }
</div>
+ }
+ <div fxLayout="column" class="widget-content p-0 ml-0 mr-0 h-100 mw-100">
+ <ng-template spWidgetHost class="h-100 p-0"></ng-template>
+ @if (errorMessage) {
+ <div fxFlex="100" fxLayout="column" fxLayoutAlign="center center">
+ <sp-exception-message
+ [message]="errorMessage"
+ [showDetails]="true"
+ ></sp-exception-message>
+ </div>
+ }
</div>
</div>
diff --git
a/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.scss
b/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.scss
index 592687545c..edbf29c51d 100644
---
a/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.scss
+++
b/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.scss
@@ -39,8 +39,7 @@
.box {
display: flex;
flex-flow: column;
- height: 100%;
- padding-top: 5px;
+ max-height: 100%;
}
.box .row.content {
@@ -85,4 +84,5 @@
max-width: 100%;
width: 100%;
overflow-x: hidden;
+ overflow-y: hidden;
}
diff --git
a/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.ts
b/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.ts
index 584696694a..1009b2037a 100644
---
a/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.ts
+++
b/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.ts
@@ -17,9 +17,11 @@
*/
import {
+ AfterViewInit,
Component,
ComponentFactoryResolver,
ComponentRef,
+ ElementRef,
EventEmitter,
Input,
OnChanges,
@@ -29,7 +31,6 @@ import {
SimpleChanges,
ViewChild,
} from '@angular/core';
-import { GridsterItemComponent } from 'angular-gridster2';
import {
ClientDashboardItem,
DataExplorerWidgetModel,
@@ -53,12 +54,13 @@ import {
TimeSelectionService,
TimeSelectorLabel,
} from '@streampipes/shared-ui';
+import { ChartSharedService } from '../../services/chart-shared.service';
import {
BaseWidgetData,
ObservableGenerator,
} from '../../models/dataview-dashboard.model';
-import { ChartSharedService } from '../../services/chart-shared.service';
import { MatMenuTrigger } from '@angular/material/menu';
+import { ResizeService } from '../../services/resize.service';
@Component({
selector: 'sp-data-explorer-chart-container',
@@ -67,7 +69,7 @@ import { MatMenuTrigger } from '@angular/material/menu';
standalone: false,
})
export class DataExplorerChartContainerComponent
- implements OnInit, OnDestroy, OnChanges
+ implements OnInit, OnDestroy, OnChanges, AfterViewInit
{
@ViewChild('menuTrigger') menu: MatMenuTrigger;
@ViewChild('timeSelectorMenu')
@@ -87,9 +89,6 @@ export class DataExplorerChartContainerComponent
@Input()
dataViewMode = false;
- @Input()
- gridsterItemComponent: GridsterItemComponent;
-
@Input()
previewMode = false;
@@ -140,9 +139,9 @@ export class DataExplorerChartContainerComponent
hasDataExplorerWritePrivileges = false;
hasDashboardWritePrivileges = false;
- authSubscription: Subscription;
- widgetTypeChangedSubscription: Subscription;
- intervalSubscription: Subscription;
+ auth$: Subscription;
+ widgetTypeChanged$: Subscription;
+ interval$: Subscription;
errorMessage: SpLogMessage;
@@ -158,8 +157,33 @@ export class DataExplorerChartContainerComponent
private authService: AuthService,
private currentUserService: CurrentUserService,
private timeSelectionService: TimeSelectionService,
+ private el: ElementRef<HTMLDivElement>,
+ private resizeService: ResizeService,
) {}
+ resizeObserver: ResizeObserver;
+ resizeTimeout: any;
+
+ ngAfterViewInit(): void {
+ const container = this.el.nativeElement.querySelector(
+ '.widget-content',
+ ) as HTMLDivElement;
+ const obs = new ResizeObserver(entries => {
+ clearTimeout(this.resizeTimeout);
+ this.resizeTimeout = setTimeout(() => {
+ const { width, height } =
+ entries[entries.length - 1].contentRect;
+
+ this.resizeService.notify({
+ width,
+ height,
+ widgetId: this.dashboardItem?.id || undefined,
+ });
+ }, 100);
+ });
+ obs.observe(container);
+ }
+
ngOnChanges(changes: SimpleChanges): void {
if (changes.widgetIndex && this.componentRef?.instance) {
this.componentRef.instance.widgetIndex =
@@ -171,19 +195,17 @@ export class DataExplorerChartContainerComponent
this.quickSelections ??=
this.timeSelectionService.defaultQuickTimeSelections;
this.labels ??= this.timeSelectionService.defaultLabels;
- this.authSubscription = this.currentUserService.user$.subscribe(
- user => {
- this.hasDataExplorerWritePrivileges = this.authService.hasRole(
- UserPrivilege.PRIVILEGE_WRITE_DATA_EXPLORER_VIEW,
- );
- this.hasDashboardWritePrivileges = this.authService.hasRole(
- UserPrivilege.PRIVILEGE_WRITE_DASHBOARD,
- );
- },
- );
+ this.auth$ = this.currentUserService.user$.subscribe(user => {
+ this.hasDataExplorerWritePrivileges = this.authService.hasRole(
+ UserPrivilege.PRIVILEGE_WRITE_DATA_EXPLORER_VIEW,
+ );
+ this.hasDashboardWritePrivileges = this.authService.hasRole(
+ UserPrivilege.PRIVILEGE_WRITE_DASHBOARD,
+ );
+ });
this.widgetLoaded = true;
this.title = this.dataLakeMeasure.measureName;
- this.widgetTypeChangedSubscription =
+ this.widgetTypeChanged$ =
this.widgetTypeService.chartTypeChangeSubject.subscribe(
typeChange => {
if (
@@ -230,9 +252,11 @@ export class DataExplorerChartContainerComponent
}
ngOnDestroy() {
+ this.resizeObserver?.disconnect();
this.componentRef?.destroy();
- this.authSubscription?.unsubscribe();
- this.widgetTypeChangedSubscription?.unsubscribe();
+ this.auth$?.unsubscribe();
+ this.widgetTypeChanged$?.unsubscribe();
+ this.interval$?.unsubscribe();
}
chooseWidget(widgetTypeId: string) {
@@ -244,6 +268,13 @@ export class DataExplorerChartContainerComponent
}
loadComponent(widgetToDisplay) {
+ const container = this.el.nativeElement.querySelector(
+ '.widget-content',
+ ) as HTMLDivElement;
+ const initialSize = {
+ width: container.clientWidth,
+ height: container.clientHeight,
+ };
const componentFactory =
this.componentFactoryResolver.resolveComponentFactory<
BaseWidgetData<any>
@@ -257,15 +288,14 @@ export class DataExplorerChartContainerComponent
componentFactory,
);
this.componentRef.instance.dataExplorerWidget = this.configuredWidget;
+ this.componentRef.instance.initialSize = initialSize;
this.componentRef.instance.timeSettings = this.getTimeSettings();
this.timeSelectionService.updateTimeSettings(
this.quickSelections,
this.getTimeSettings(),
new Date(),
);
- this.componentRef.instance.gridsterItem = this.dashboardItem;
- this.componentRef.instance.gridsterItemComponent =
- this.gridsterItemComponent;
+ this.componentRef.instance.dataViewMode = this.dataViewMode;
this.componentRef.instance.editMode = this.editMode;
this.componentRef.instance.kioskMode = this.kioskMode;
this.componentRef.instance.dataViewDashboardItem = this.dashboardItem;
@@ -275,22 +305,22 @@ export class DataExplorerChartContainerComponent
this.componentRef.instance.widgetIndex = this.widgetIndex;
this.componentRef.instance.observableGenerator =
this.observableGenerator;
- const removeSub =
+ const remove$ =
this.componentRef.instance.removeWidgetCallback.subscribe(ev =>
this.removeWidget(),
);
- const timerSub = this.componentRef.instance.timerCallback.subscribe(
- ev => this.handleTimer(ev),
+ const timer$ = this.componentRef.instance.timerCallback.subscribe(ev =>
+ this.handleTimer(ev),
);
- const errorSub = this.componentRef.instance.errorCallback.subscribe(
+ const error$ = this.componentRef.instance.errorCallback.subscribe(
ev => (this.errorMessage = ev),
);
this.componentRef.onDestroy(destroy => {
this.componentRef.instance.cleanupSubscriptions();
- removeSub?.unsubscribe();
- timerSub?.unsubscribe();
- errorSub?.unsubscribe();
+ remove$?.unsubscribe();
+ timer$?.unsubscribe();
+ error$?.unsubscribe();
});
}
@@ -317,7 +347,7 @@ export class DataExplorerChartContainerComponent
startLoadingTimer() {
this.timerActive = true;
- this.intervalSubscription = interval(100)
+ this.interval$ = interval(100)
.pipe(takeWhile(() => this.timerActive))
.subscribe(value => {
this.loadingTime = (value * 100) / 1000;
@@ -326,7 +356,7 @@ export class DataExplorerChartContainerComponent
stopLoadingTimer() {
this.timerActive = false;
- this.intervalSubscription.unsubscribe();
+ this.interval$.unsubscribe();
}
handleTimer(start: boolean) {
diff --git
a/ui/src/app/data-explorer-shared/components/charts/base/base-data-explorer-widget.directive.ts
b/ui/src/app/data-explorer-shared/components/charts/base/base-data-explorer-widget.directive.ts
index e322d0fc06..c7c4cc0dd7 100644
---
a/ui/src/app/data-explorer-shared/components/charts/base/base-data-explorer-widget.directive.ts
+++
b/ui/src/app/data-explorer-shared/components/charts/base/base-data-explorer-widget.directive.ts
@@ -25,14 +25,12 @@ import {
OnInit,
Output,
} from '@angular/core';
-import { GridsterItem, GridsterItemComponent } from 'angular-gridster2';
import { ChartConfigurationService } from
'../../../services/chart-configuration.service';
import {
ClientDashboardItem,
DataExplorerDataConfig,
DataExplorerField,
DataExplorerWidgetModel,
- DataViewQueryGeneratorService,
SpLogMessage,
SpQueryResult,
TimeSettings,
@@ -49,6 +47,7 @@ import { catchError, switchMap } from 'rxjs/operators';
import { DataExplorerChartRegistry } from
'../../../registry/data-explorer-chart-registry';
import { SpFieldUpdateService } from '../../../services/field-update.service';
import { TimeSelectionService } from '@streampipes/shared-ui';
+import { WidgetSize } from '../../../models/dataset.model';
@Directive()
export abstract class BaseDataExplorerWidgetDirective<
@@ -68,10 +67,9 @@ export abstract class BaseDataExplorerWidgetDirective<
errorCallback: EventEmitter<SpLogMessage> =
new EventEmitter<SpLogMessage>();
- @Input() gridsterItem: GridsterItem;
- @Input() gridsterItemComponent: GridsterItemComponent;
@Input() editMode: boolean;
@Input() kioskMode: boolean;
+ @Input() dataViewMode: boolean;
@Input() observableGenerator: ObservableGenerator;
@Input() timeSettings: TimeSettings;
@@ -90,6 +88,12 @@ export abstract class BaseDataExplorerWidgetDirective<
@HostBinding('class') className = 'h-100';
+ @Input()
+ initialSize: WidgetSize;
+
+ currentWidth: number;
+ currentHeight: number;
+
public selectedProperties: string[];
public showNoDataInDateRange: boolean;
@@ -101,12 +105,9 @@ export abstract class BaseDataExplorerWidgetDirective<
fieldProvider: FieldProvider;
- widgetConfigurationSub: Subscription;
- resizeSub: Subscription;
- timeSelectionSub: Subscription;
-
- widthOffset: number;
- heightOffset: number;
+ widgetConfiguration$: Subscription;
+ resize$: Subscription;
+ timeSelection$: Subscription;
requestQueue$: Subject<Observable<SpQueryResult>[]> = new Subject<
Observable<SpQueryResult>[]
@@ -114,17 +115,14 @@ export abstract class BaseDataExplorerWidgetDirective<
protected widgetConfigurationService = inject(ChartConfigurationService);
protected resizeService = inject(ResizeService);
- protected dataViewQueryGeneratorService = inject(
- DataViewQueryGeneratorService,
- );
protected timeSelectionService = inject(TimeSelectionService);
protected widgetRegistryService = inject(DataExplorerChartRegistry);
protected fieldUpdateService = inject(SpFieldUpdateService);
public fieldService = inject(ChartFieldProviderService);
ngOnInit(): void {
- this.heightOffset = this.gridMode ? 70 : 65;
- this.widthOffset = this.gridMode ? 10 : 10;
+ this.currentWidth = this.initialSize.width;
+ this.currentHeight = this.initialSize.height;
this.showData = true;
const sourceConfigs = this.dataExplorerWidget.dataConfig.sourceConfigs;
this.fieldProvider =
@@ -153,7 +151,7 @@ export abstract class BaseDataExplorerWidgetDirective<
});
});
- this.widgetConfigurationSub =
+ this.widgetConfiguration$ =
this.widgetConfigurationService.configurationChangedSubject.subscribe(
refreshMessage => {
if (refreshMessage.refreshData) {
@@ -179,22 +177,18 @@ export abstract class BaseDataExplorerWidgetDirective<
},
);
if (!this.previewMode) {
- this.resizeSub = this.resizeService.resizeSubject.subscribe(
- info => {
- if (
- info.gridsterItem.id ===
- this.dataExplorerWidget.elementId
- ) {
- this.onResize(
- this.gridsterItemComponent.width -
this.widthOffset,
- this.gridsterItemComponent.height -
- this.heightOffset,
- );
- }
- },
- );
+ this.resize$ = this.resizeService.resizeSubject.subscribe(info => {
+ if (
+ this.dataViewMode ||
+ info.widgetId === this.dataViewDashboardItem.id
+ ) {
+ this.currentWidth = info.width;
+ this.currentHeight = info.height;
+ this.onResize(info.width, info.height);
+ }
+ });
}
- this.timeSelectionSub =
+ this.timeSelection$ =
this.timeSelectionService.timeSelectionChangeSubject.subscribe(
widgetTimeSettings => {
if (
@@ -216,16 +210,12 @@ export abstract class BaseDataExplorerWidgetDirective<
},
);
this.updateData();
- this.onResize(
- this.gridsterItemComponent.width - this.widthOffset,
- this.gridsterItemComponent.height - this.heightOffset,
- );
}
public cleanupSubscriptions(): void {
- this.widgetConfigurationSub?.unsubscribe();
- this.resizeSub?.unsubscribe();
- this.timeSelectionSub?.unsubscribe();
+ this.widgetConfiguration$?.unsubscribe();
+ this.resize$?.unsubscribe();
+ this.timeSelection$.unsubscribe();
this.requestQueue$?.unsubscribe();
}
diff --git
a/ui/src/app/data-explorer-shared/components/charts/base/echarts-widget.component.html
b/ui/src/app/data-explorer-shared/components/charts/base/echarts-widget.component.html
index f7d787b876..02753931c0 100644
---
a/ui/src/app/data-explorer-shared/components/charts/base/echarts-widget.component.html
+++
b/ui/src/app/data-explorer-shared/components/charts/base/echarts-widget.component.html
@@ -51,7 +51,7 @@
[attr.data-cy]="dataExplorerWidget.widgetType"
echarts
[options]="option"
- [ngStyle]="{ width: currentWidth, height: '100%' }"
+ [ngStyle]="{ width: currentWidth, height: 'calc(100% - 1px)' }"
(chartInit)="onChartInit($event)"
></div>
}
diff --git
a/ui/src/app/data-explorer-shared/components/charts/base/echarts-widget.component.ts
b/ui/src/app/data-explorer-shared/components/charts/base/echarts-widget.component.ts
index 375499beda..6561c5d527 100644
---
a/ui/src/app/data-explorer-shared/components/charts/base/echarts-widget.component.ts
+++
b/ui/src/app/data-explorer-shared/components/charts/base/echarts-widget.component.ts
@@ -41,9 +41,6 @@ export class SpEchartsWidgetComponent<T extends
DataExplorerWidgetModel>
implements OnInit
{
eChartsInstance: ECharts;
- currentWidth: number;
- currentHeight: number;
-
option: EChartsOption;
configReady = false;
@@ -63,8 +60,7 @@ export class SpEchartsWidgetComponent<T extends
DataExplorerWidgetModel>
this.renderer = this.getRenderer();
this.resizeEcharts$ =
this.resizeEchartsService.echartsResizeSubject.subscribe(width => {
- this.currentWidth = width - this.widthOffset;
- this.applySize(this.currentWidth, this.currentHeight);
+ this.currentWidth = width;
this.refreshView();
});
this.renderSubject$ = this.renderSubject
@@ -80,30 +76,20 @@ export class SpEchartsWidgetComponent<T extends
DataExplorerWidgetModel>
beforeDataFetched() {}
onDataReceived(spQueryResult: SpQueryResult[]) {
- this.renderChartOptions(spQueryResult);
this.latestData = spQueryResult;
+ this.renderChartOptions(spQueryResult);
this.setShownComponents(false, true, false, false);
}
onResize(width: number, height: number) {
- this.currentWidth = width;
- this.currentHeight = height;
this.configReady = true;
- this.applySize(width, height);
if (this.latestData) {
- this.renderSubject.next();
+ this.refreshView();
}
}
onChartInit(ec: ECharts) {
this.eChartsInstance = ec;
- this.applySize(this.currentWidth, this.currentHeight);
- }
-
- applySize(width: number, height: number) {
- if (this.eChartsInstance) {
- this.eChartsInstance.resize({ width, height });
- }
}
renderChartOptions(spQueryResult: SpQueryResult[]): void {
diff --git
a/ui/src/app/data-explorer-shared/components/charts/gauge/gauge-renderer.service.ts
b/ui/src/app/data-explorer-shared/components/charts/gauge/gauge-renderer.service.ts
index 376bbf2cbf..7be7324f64 100644
---
a/ui/src/app/data-explorer-shared/components/charts/gauge/gauge-renderer.service.ts
+++
b/ui/src/app/data-explorer-shared/components/charts/gauge/gauge-renderer.service.ts
@@ -46,18 +46,25 @@ export class SpGaugeRendererService
fieldName: string,
value: number,
widgetConfig: GaugeWidgetModel,
+ widgetSize: WidgetSize,
): GaugeSeriesOption {
const visConfig = widgetConfig.visualizationConfig;
+ const clamp = Math.min(Math.max(widgetSize.width / 400, 0.7), 1.4);
return {
name: seriesName,
type: 'gauge',
progress: {
show: true,
},
+ axisLabel: {
+ fontSize: 10 * clamp,
+ },
detail: {
show: true,
valueAnimation: false,
formatter: '{value}',
+ fontSize: 14 * clamp,
+ offsetCenter: [0, '70%'],
},
min: visConfig.min,
max: visConfig.max,
@@ -87,7 +94,7 @@ export class SpGaugeRendererService
render(
queryResult: SpQueryResult[],
widgetConfig: GaugeWidgetModel,
- _widgetSize: WidgetSize,
+ widgetSize: WidgetSize,
): EChartsOption {
const option = this.echartsBaseOptionsGenerator.makeBaseConfig(
widgetConfig.baseAppearanceConfig as WidgetEchartsAppearanceConfig,
@@ -110,6 +117,7 @@ export class SpGaugeRendererService
selectedField.fullDbName,
data,
widgetConfig,
+ widgetSize,
),
});
diff --git
a/ui/src/app/data-explorer-shared/components/charts/heatmap/heatmap-renderer.service.ts
b/ui/src/app/data-explorer-shared/components/charts/heatmap/heatmap-renderer.service.ts
index 08c87275b0..90abf4ee55 100644
---
a/ui/src/app/data-explorer-shared/components/charts/heatmap/heatmap-renderer.service.ts
+++
b/ui/src/app/data-explorer-shared/components/charts/heatmap/heatmap-renderer.service.ts
@@ -109,10 +109,7 @@ export class SpHeatmapRendererService extends
SpBaseEchartsRenderer<HeatmapWidge
options: EChartsOption,
widgetConfig: HeatmapWidgetModel,
): void {
- options.grid = {
- height: '80%',
- top: '80',
- };
+ options.grid = {};
options.xAxis = {
type: 'category',
splitArea: {
diff --git
a/ui/src/app/data-explorer-shared/components/charts/image/image-widget.component.ts
b/ui/src/app/data-explorer-shared/components/charts/image/image-widget.component.ts
index c66e0cbed2..6c2508e7c6 100644
---
a/ui/src/app/data-explorer-shared/components/charts/image/image-widget.component.ts
+++
b/ui/src/app/data-explorer-shared/components/charts/image/image-widget.component.ts
@@ -51,10 +51,7 @@ export class ImageWidgetComponent
ngOnInit(): void {
super.ngOnInit();
- this.onResize(
- this.gridsterItemComponent.width,
- this.gridsterItemComponent.height - 53,
- );
+ this.onResize(this.currentWidth, this.currentHeight - 53);
this.imageBaseUrl = this.dataLakeRestService.dataLakeUrl + '/images/';
}
diff --git
a/ui/src/app/data-explorer-shared/components/charts/status/status-widget.component.html
b/ui/src/app/data-explorer-shared/components/charts/status/status-widget.component.html
index 24929b45c0..239fe7aad1 100644
---
a/ui/src/app/data-explorer-shared/components/charts/status/status-widget.component.html
+++
b/ui/src/app/data-explorer-shared/components/charts/status/status-widget.component.html
@@ -31,24 +31,19 @@
</sp-no-data-in-date-range>
}
- <div fxLayoutAlign="center center">
- <div
- class="tl-container"
- [ngStyle]="{
- width: containerWidth + 'px',
- height: containerHeight + 'px'
- }"
- >
+ <div
+ class="status-light-wrapper"
+ fxFlex
+ fxLayout="column"
+ fxLayoutAlign="center center"
+ >
+ <div class="tl-container">
<div
class="light"
[ngClass]="{
'light-red': !active,
'light-green': active
}"
- [ngStyle]="{
- width: lightWidth + 'px',
- height: lightHeight + 'px'
- }"
></div>
</div>
</div>
diff --git
a/ui/src/app/data-explorer-shared/components/charts/status/status-widget.component.scss
b/ui/src/app/data-explorer-shared/components/charts/status/status-widget.component.scss
index ac656663e5..c18549cdc5 100644
---
a/ui/src/app/data-explorer-shared/components/charts/status/status-widget.component.scss
+++
b/ui/src/app/data-explorer-shared/components/charts/status/status-widget.component.scss
@@ -19,19 +19,33 @@
height: 100%;
}
+.status-light-wrapper {
+ display: inline-flex;
+ flex-direction: column;
+ align-items: center;
+}
+
.tl-container {
background-color: #222;
display: flex;
align-items: center;
- flex-direction: column;
- padding: 20px;
+ padding: 10%;
border-radius: 10px;
+ box-sizing: border-box;
+ height: 90%;
+ max-width: 100%;
+ aspect-ratio: 1 / 1;
+ flex-direction: column;
+ justify-content: space-between;
+ overflow: hidden;
}
.light {
+ width: 100%;
+ height: 100%;
+ aspect-ratio: 1 / 1;
border-radius: 50%;
- background-color: #3d3535;
- background: repeating-linear-gradient(#333, #443 5px);
+ background: #3d3535 repeating-linear-gradient(#333, #443 5px);
}
.light-red,
diff --git
a/ui/src/app/data-explorer-shared/components/charts/status/status-widget.component.ts
b/ui/src/app/data-explorer-shared/components/charts/status/status-widget.component.ts
index b62fcc18f0..220598544c 100644
---
a/ui/src/app/data-explorer-shared/components/charts/status/status-widget.component.ts
+++
b/ui/src/app/data-explorer-shared/components/charts/status/status-widget.component.ts
@@ -58,10 +58,6 @@ export class StatusWidgetComponent
ngOnInit(): void {
super.ngOnInit();
- this.onResize(
- this.gridsterItemComponent.width - this.widthOffset,
- this.gridsterItemComponent.height - this.heightOffset,
- );
this.updateSettings();
}
@@ -134,12 +130,7 @@ export class StatusWidgetComponent
}
}
- onResize(width: number, heigth: number): void {
- this.containerHeight = heigth * 0.7;
- this.containerWidth = this.containerHeight;
- this.lightWidth = this.containerHeight;
- this.lightHeight = this.lightWidth;
- }
+ onResize(width: number, heigth: number): void {}
handleUpdatedFields(
addedFields: DataExplorerField[],
diff --git
a/ui/src/app/data-explorer-shared/components/charts/table/table-widget.component.ts
b/ui/src/app/data-explorer-shared/components/charts/table/table-widget.component.ts
index a125322eee..e922ad992e 100644
---
a/ui/src/app/data-explorer-shared/components/charts/table/table-widget.component.ts
+++
b/ui/src/app/data-explorer-shared/components/charts/table/table-widget.component.ts
@@ -142,7 +142,7 @@ export class TableWidgetComponent
this.refreshColumns();
}
- onResize(width: number, height: number) {}
+ onResize(_width: number, _height: number) {}
beforeDataFetched() {}
diff --git
a/ui/src/app/data-explorer-shared/components/charts/traffic-light/traffic-light-widget.component.html
b/ui/src/app/data-explorer-shared/components/charts/traffic-light/traffic-light-widget.component.html
index aa6aad2576..d18e569eab 100644
---
a/ui/src/app/data-explorer-shared/components/charts/traffic-light/traffic-light-widget.component.html
+++
b/ui/src/app/data-explorer-shared/components/charts/traffic-light/traffic-light-widget.component.html
@@ -30,49 +30,35 @@
</sp-no-data-in-date-range>
}
- <div fxLayoutAlign="center center">
+ <div
+ class="traffic-light-wrapper"
+ fxFlex
+ fxLayout="column"
+ fxLayoutAlign="center center"
+ >
<div
class="tl-container"
[ngClass]="selectedToShowValue ? 'no-border-container' : ''"
- [ngStyle]="{
- width: containerWidth + 'px',
- height: containerHeight + 'px'
- }"
>
<div
class="light"
[ngClass]="{ 'light-red': activeClass === 'red' }"
- [ngStyle]="{
- width: lightWidth + 'px',
- height: lightHeight + 'px'
- }"
></div>
<div
class="light"
[ngClass]="{ 'light-yellow': activeClass === 'yellow' }"
- [ngStyle]="{
- width: lightWidth + 'px',
- height: lightHeight + 'px'
- }"
></div>
<div
class="light"
[ngClass]="{ 'light-green': activeClass === 'green' }"
- [ngStyle]="{
- width: lightWidth + 'px',
- height: lightHeight + 'px'
- }"
></div>
</div>
- </div>
- <div class="light-value-container">
- @if (selectedToShowValue) {
- <div
- class="light-value"
- [ngStyle]="{ width: containerWidth + 'px' }"
- >
- {{ displayed_value }}
- </div>
- }
+ <div class="light-value-container">
+ @if (selectedToShowValue) {
+ <div class="light-value">
+ {{ displayed_value }}
+ </div>
+ }
+ </div>
</div>
</div>
diff --git
a/ui/src/app/data-explorer-shared/components/charts/traffic-light/traffic-light-widget.component.scss
b/ui/src/app/data-explorer-shared/components/charts/traffic-light/traffic-light-widget.component.scss
index cc13ba4625..918f615ee5 100644
---
a/ui/src/app/data-explorer-shared/components/charts/traffic-light/traffic-light-widget.component.scss
+++
b/ui/src/app/data-explorer-shared/components/charts/traffic-light/traffic-light-widget.component.scss
@@ -19,27 +19,40 @@
height: 100%;
}
-.tl-container {
- background-color: #222;
- display: flex;
- align-items: center;
- justify-content: space-between;
+.traffic-light-wrapper {
+ display: inline-flex;
flex-direction: column;
+ align-items: center;
+}
+
+.tl-container {
+ height: 90%;
+ aspect-ratio: 1 / 3;
+
+ box-sizing: border-box;
padding: 10px;
border-radius: 10px;
+ background: #222;
+
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ align-items: center;
}
.no-border-container {
- border-bottom-left-radius: 0;
- border-bottom-right-radius: 0;
- border-top-left-radius: 10px;
- border-top-right-radius: 10px;
+ border-radius: 10px 10px 0 0;
}
.light {
+ height: auto;
+ flex: 0 0 auto;
+
+ width: 96%;
+ aspect-ratio: 1 / 1;
border-radius: 50%;
- background-color: #3d3535;
- background: repeating-linear-gradient(#333, #443 5px);
+
+ background: #3d3535 repeating-linear-gradient(#333, #443 5px);
}
.light-red,
@@ -73,6 +86,7 @@
.light-value-container {
background-color: #222;
border-radius: 10px;
+ width: 100%;
}
.light-value {
@@ -82,6 +96,7 @@
text-align: center;
background-color: #222;
padding: 10px;
+
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
border-top-left-radius: 0;
diff --git
a/ui/src/app/data-explorer-shared/components/charts/traffic-light/traffic-light-widget.component.ts
b/ui/src/app/data-explorer-shared/components/charts/traffic-light/traffic-light-widget.component.ts
index c7da1f2704..5720b0baec 100644
---
a/ui/src/app/data-explorer-shared/components/charts/traffic-light/traffic-light-widget.component.ts
+++
b/ui/src/app/data-explorer-shared/components/charts/traffic-light/traffic-light-widget.component.ts
@@ -38,15 +38,6 @@ export class TrafficLightWidgetComponent
header: string[];
fieldIndex: number;
- width: number;
- height: number;
-
- containerWidth: number;
- containerHeight: number;
-
- lightWidth: number;
- lightHeight: number;
-
selectedWarningRange: number;
selectedFieldToObserve: DataExplorerField;
selectedUpperLimit: boolean;
@@ -58,10 +49,6 @@ export class TrafficLightWidgetComponent
ngOnInit(): void {
super.ngOnInit();
- this.onResize(
- this.gridsterItemComponent.width - this.widthOffset,
- this.gridsterItemComponent.height - this.heightOffset,
- );
this.updateSettings();
}
@@ -155,12 +142,7 @@ export class TrafficLightWidgetComponent
}
}
- onResize(width: number, heigth: number) {
- this.containerHeight = heigth * 0.8;
- this.containerWidth = this.containerHeight / 3;
- this.lightWidth = (this.containerHeight * 0.9) / 3;
- this.lightHeight = this.lightWidth;
- }
+ onResize(width: number, heigth: number) {}
handleUpdatedFields(
addedFields: DataExplorerField[],
diff --git a/ui/src/app/data-explorer-shared/data-explorer-shared.module.ts
b/ui/src/app/data-explorer-shared/data-explorer-shared.module.ts
index 32b3580848..2e27da662a 100644
--- a/ui/src/app/data-explorer-shared/data-explorer-shared.module.ts
+++ b/ui/src/app/data-explorer-shared/data-explorer-shared.module.ts
@@ -43,7 +43,6 @@ import { CommonModule } from '@angular/common';
import { LeafletModule } from '@bluehalo/ngx-leaflet';
import { CoreUiModule } from '../core-ui/core-ui.module';
import { MatTabsModule } from '@angular/material/tabs';
-import { GridsterModule } from 'angular-gridster2';
import { FlexLayoutModule } from '@ngbracket/ngx-layout';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ColorPickerComponent, ColorPickerDirective } from 'ngx-color-picker';
@@ -127,7 +126,6 @@ import { ColorMappingOptionsConfigComponent } from
'./components/chart-config/co
LeafletModule,
CoreUiModule,
MatTabsModule,
- GridsterModule,
FlexLayoutModule,
FormsModule,
ColorPickerComponent,
diff --git a/ui/src/app/data-explorer-shared/models/dataview-dashboard.model.ts
b/ui/src/app/data-explorer-shared/models/dataview-dashboard.model.ts
index 86c996c499..0340c63f46 100644
--- a/ui/src/app/data-explorer-shared/models/dataview-dashboard.model.ts
+++ b/ui/src/app/data-explorer-shared/models/dataview-dashboard.model.ts
@@ -16,11 +16,6 @@
*
*/
-import {
- GridsterConfig,
- GridsterItem,
- GridsterItemComponent,
-} from 'angular-gridster2';
import {
ClientDashboardItem,
DataExplorerDataConfig,
@@ -37,24 +32,20 @@ import { FieldUpdateInfo } from './field-update.model';
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
-// eslint-disable-next-line @typescript-eslint/no-empty-interface
-export type IDataViewDashboardConfig = GridsterConfig;
-
export interface BaseWidgetData<T extends DataExplorerWidgetModel> {
removeWidgetCallback: EventEmitter<boolean>;
timerCallback: EventEmitter<boolean>;
errorCallback: EventEmitter<SpLogMessage>;
- gridsterItem: GridsterItem;
- gridsterItemComponent: GridsterItemComponent;
editMode: boolean;
kioskMode: boolean;
observableGenerator: ObservableGenerator;
-
+ initialSize: WidgetSize;
timeSettings: TimeSettings;
dataViewDashboardItem: ClientDashboardItem;
dataExplorerWidget: T;
+ dataViewMode: boolean;
previewMode: boolean;
gridMode: boolean;
widgetIndex?: number;
diff --git a/ui/src/app/data-explorer-shared/models/gridster-info.model.ts
b/ui/src/app/data-explorer-shared/models/gridster-info.model.ts
index 58af6d1329..504306c32d 100644
--- a/ui/src/app/data-explorer-shared/models/gridster-info.model.ts
+++ b/ui/src/app/data-explorer-shared/models/gridster-info.model.ts
@@ -16,9 +16,8 @@
*
*/
-import { GridsterItem, GridsterItemComponent } from 'angular-gridster2';
-
-export interface GridsterInfo {
- gridsterItem: GridsterItem;
- gridsterItemComponent: GridsterItemComponent;
+export interface ResizeInfo {
+ width: number;
+ height: number;
+ widgetId: string;
}
diff --git a/ui/src/app/data-explorer-shared/services/resize.service.ts
b/ui/src/app/data-explorer-shared/services/resize.service.ts
index f0821b9065..ead8ea0057 100644
--- a/ui/src/app/data-explorer-shared/services/resize.service.ts
+++ b/ui/src/app/data-explorer-shared/services/resize.service.ts
@@ -18,13 +18,13 @@
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
-import { GridsterInfo } from '../models/gridster-info.model';
+import { ResizeInfo } from '../models/gridster-info.model';
@Injectable({ providedIn: 'root' })
export class ResizeService {
- public resizeSubject: Subject<GridsterInfo> = new Subject<GridsterInfo>();
+ public resizeSubject: Subject<ResizeInfo> = new Subject<ResizeInfo>();
- public notify(info: GridsterInfo): void {
+ public notify(info: ResizeInfo): void {
this.resizeSubject.next(info);
}
}
diff --git
a/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.html
b/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.html
index 15adc5ecbc..59931c9164 100644
---
a/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.html
+++
b/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.html
@@ -74,16 +74,13 @@
<div #panel fxFlex="100" fxLayout="column">
@if (
dataView &&
- gridsterItemComponent &&
dataView.dataConfig?.sourceConfigs?.length > 0
) {
<sp-data-explorer-chart-container
+ fxFlex
[dataViewMode]="true"
[editMode]="editMode"
[configuredWidget]="dataView"
- [gridsterItemComponent]="
- gridsterItemComponent
- "
[timeSettings]="timeSettings"
[observableGenerator]="observableGenerator"
[dataLakeMeasure]="
diff --git
a/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.ts
b/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.ts
index 4c033bedcc..04c836b948 100644
---
a/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.ts
+++
b/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.ts
@@ -26,7 +26,6 @@ import {
} from '@angular/core';
import {
ChartService,
- DashboardConfig,
DataExplorerWidgetModel,
DataLakeMeasure,
EventPropertyUnion,
@@ -60,8 +59,8 @@ import { ResizeEchartsService } from
'../../../data-explorer-shared/services/res
import { AssetDialogComponent } from '../../dialog/asset-dialog.component';
import { AuthService } from '../../../services/auth.service';
import { UserRole } from '../../../_enums/user-role.enum';
-import { Tuple2 } from 'src/app/core-model/base/Tuple2';
-import { ChartFieldProviderService } from
'src/app/data-explorer-shared/services/chart-field-provider.service';
+import { ChartFieldProviderService } from
'../../../data-explorer-shared/services/chart-field-provider.service';
+import { Tuple2 } from '../../../core-model/base/Tuple2';
@Component({
selector: 'sp-data-explorer-data-view',
@@ -79,7 +78,6 @@ export class DataExplorerChartViewComponent
dataView: DataExplorerWidgetModel;
originalDataView: DataExplorerWidgetModel;
dataLakeMeasure: DataLakeMeasure;
- gridsterItemComponent: any;
drawerWidth = 450;
panelWidth = '100%';
@@ -216,7 +214,6 @@ export class DataExplorerChartViewComponent
setTimeout(() => {
const width = this.outerPanel.nativeElement.offsetWidth;
const height = this.outerPanel.nativeElement.offsetHeight;
- this.gridsterItemComponent = { width, height };
this.timeSelectionService.notify(this.timeSettings);
this.updateQueryParams(this.timeSettings);
});
@@ -402,10 +399,7 @@ export class DataExplorerChartViewComponent
}, 100);
}
- private async saveAssets(
- linkageData: LinkageData[],
- data: DashboardConfig,
- ): Promise<void> {
+ private async saveAssets(linkageData: LinkageData[]): Promise<void> {
await this.assetSaveService.saveSelectedAssets(
this.selectedAssets,
linkageData,
@@ -419,7 +413,7 @@ export class DataExplorerChartViewComponent
try {
linkageData = this.createLinkageData(data);
- this.saveAssets(linkageData, data);
+ this.saveAssets(linkageData);
} catch (err) {
console.error('Error in addToAsset:', err);
}
diff --git a/ui/src/app/data-explorer/data-explorer.module.ts
b/ui/src/app/data-explorer/data-explorer.module.ts
index e35f0a553a..2b16241630 100644
--- a/ui/src/app/data-explorer/data-explorer.module.ts
+++ b/ui/src/app/data-explorer/data-explorer.module.ts
@@ -29,8 +29,6 @@ import { MatSliderModule } from '@angular/material/slider';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatTabsModule } from '@angular/material/tabs';
import { LeafletModule } from '@bluehalo/ngx-leaflet';
-
-import { GridsterModule } from 'angular-gridster2';
import { ColorPickerComponent, ColorPickerDirective } from 'ngx-color-picker';
import { PlatformServicesModule } from '@streampipes/platform-services';
import { CoreUiModule } from '../core-ui/core-ui.module';
@@ -118,7 +116,6 @@ import { AssetDialogComponent } from
'./dialog/asset-dialog.component';
LeafletModule,
CoreUiModule,
MatTabsModule,
- GridsterModule,
FlexLayoutModule,
FormsModule,
ColorPickerComponent,
diff --git a/ui/src/app/editor/editor.module.ts
b/ui/src/app/editor/editor.module.ts
index f78a1691c2..a6dc1cd36d 100644
--- a/ui/src/app/editor/editor.module.ts
+++ b/ui/src/app/editor/editor.module.ts
@@ -19,7 +19,6 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@ngbracket/ngx-layout';
-import { GridsterModule } from 'angular-gridster2';
import { MatTabsModule } from '@angular/material/tabs';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { EditorComponent } from './editor.component';
@@ -117,7 +116,6 @@ import { TranslatePipe } from '@ngx-translate/core';
CommonModule,
MatTabsModule,
MatListModule,
- GridsterModule,
FlexLayoutModule,
FormsModule,
MatProgressSpinnerModule,
diff --git a/ui/src/scss/main.scss b/ui/src/scss/main.scss
index 7541a2ee68..132079f868 100644
--- a/ui/src/scss/main.scss
+++ b/ui/src/scss/main.scss
@@ -62,3 +62,5 @@
@use './sp/buttons-mat3';
@use './sp/forms-mat3';
+
+@use 'gridstack/dist/gridstack.min.css';