diff --git a/application/src/main/data/json/system/widget_bundles/cards.json b/application/src/main/data/json/system/widget_bundles/cards.json
index f421f48b61..05a008080e 100644
--- a/application/src/main/data/json/system/widget_bundles/cards.json
+++ b/application/src/main/data/json/system/widget_bundles/cards.json
@@ -207,8 +207,9 @@
"templateHtml": "\n\n
\n",
"templateCss": ".dashboard-state-widget-prompt {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n font-size: 32px;\n font-weight: 500;\n color: #999;\n}\n\n.dashboard-state-widget-prompt .title {\n font-size: 32px;\n font-weight: 500;\n color: #999;\n}\n\n.dashboard-state-widget-prompt .subtitle {\n font-size: 24px;\n font-weight: normal;\n color: #999;\n text-align: center;\n}",
"controllerScript": "self.onInit = function() {\n var $injector = self.ctx.$scope.$injector;\n self.ctx.$scope.stateId = self.ctx.settings.stateId || \"\";\n self.ctx.$scope.defaultAutofillLayout = self.ctx.settings.defaultAutofillLayout;\n self.ctx.$scope.defaultMargin = self.ctx.settings.defaultMargin;\n self.ctx.$scope.defaultBackgroundColor = self.ctx.settings.defaultBackgroundColor;\n self.ctx.$scope.syncParentStateParams = self.ctx.settings.syncParentStateParams !== false;\n}\n\n\nself.onDestroy = function() {\n}\n",
- "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"required\": [],\n \"properties\": {\n \"stateId\": {\n \"title\": \"Dashboard state id\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"defaultAutofillLayout\": {\n \"title\": \"Autofill state layout height by default\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"defaultMargin\": {\n \"title\": \"Default widgets margin\",\n \"type\": \"number\",\n \"default\": 0\n },\n \"defaultBackgroundColor\": {\n \"title\": \"Default background color\",\n \"type\": \"string\",\n \"default\": \"#fff\"\n },\n \"syncParentStateParams\": {\n \"title\": \"Sync state params with parent dashboard\",\n \"type\": \"boolean\",\n \"default\": true\n }\n }\n },\n \"form\": [\n \"stateId\",\n \"defaultAutofillLayout\",\n \"defaultMargin\",\n {\n \"key\": \"defaultBackgroundColor\",\n \"type\": \"color\"\n },\n \"syncParentStateParams\"\n ]\n}",
- "dataKeySettingsSchema": "{}\n",
+ "settingsSchema": "",
+ "dataKeySettingsSchema": "",
+ "settingsDirective": "tb-dashboard-state-widget-settings",
"defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":false,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"syncParentStateParams\":true,\"defaultAutofillLayout\":true,\"defaultMargin\":0,\"defaultBackgroundColor\":\"#fff\"},\"title\":\"Dashboard state widget\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"widgetCss\":\"\",\"noDataDisplayMessage\":\"\",\"showLegend\":false}"
}
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/dashboard-state-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/dashboard-state-widget-settings.component.html
new file mode 100644
index 0000000000..a17d3a7a35
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/dashboard-state-widget-settings.component.html
@@ -0,0 +1,64 @@
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/dashboard-state-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/dashboard-state-widget-settings.component.ts
new file mode 100644
index 0000000000..7d2a11e9cc
--- /dev/null
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/dashboard-state-widget-settings.component.ts
@@ -0,0 +1,99 @@
+///
+/// Copyright © 2016-2022 The Thingsboard Authors
+///
+/// Licensed 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.
+///
+
+import { Component, ElementRef, ViewChild } from '@angular/core';
+import { WidgetActionType, WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { Store } from '@ngrx/store';
+import { AppState } from '@core/core.state';
+import { Observable, of } from 'rxjs';
+import { map, mergeMap, startWith } from 'rxjs/operators';
+
+@Component({
+ selector: 'tb-dashboard-state-widget-settings',
+ templateUrl: './dashboard-state-widget-settings.component.html',
+ styleUrls: ['./widget-settings.scss']
+})
+export class DashboardStateWidgetSettingsComponent extends WidgetSettingsComponent {
+
+ @ViewChild('dashboardStateInput') dashboardStateInput: ElementRef;
+
+ dashboardStateWidgetSettingsForm: FormGroup;
+
+ filteredDashboardStates: Observable>;
+ dashboardStateSearchText = '';
+
+ constructor(protected store: Store,
+ private fb: FormBuilder) {
+ super(store);
+ }
+
+ protected settingsForm(): FormGroup {
+ return this.dashboardStateWidgetSettingsForm;
+ }
+
+ protected defaultSettings(): WidgetSettings {
+ return {
+ stateId: '',
+ defaultAutofillLayout: true,
+ defaultMargin: 0,
+ defaultBackgroundColor: '#fff',
+ syncParentStateParams: true
+ };
+ }
+
+ protected onSettingsSet(settings: WidgetSettings) {
+ this.dashboardStateWidgetSettingsForm = this.fb.group({
+ stateId: [settings.stateId, []],
+ defaultAutofillLayout: [settings.defaultAutofillLayout, []],
+ defaultMargin: [settings.defaultMargin, [Validators.min(0)]],
+ defaultBackgroundColor: [settings.defaultBackgroundColor, []],
+ syncParentStateParams: [settings.syncParentStateParams, []]
+ });
+ this.dashboardStateSearchText = '';
+ this.filteredDashboardStates = this.dashboardStateWidgetSettingsForm.get('stateId').valueChanges
+ .pipe(
+ startWith(''),
+ map(value => value ? value : ''),
+ mergeMap(name => this.fetchDashboardStates(name) )
+ );
+ }
+
+ public clearDashboardState(value: string = '') {
+ this.dashboardStateInput.nativeElement.value = value;
+ this.dashboardStateWidgetSettingsForm.get('stateId').patchValue(value, {emitEvent: true});
+ setTimeout(() => {
+ this.dashboardStateInput.nativeElement.blur();
+ this.dashboardStateInput.nativeElement.focus();
+ }, 0);
+ }
+
+ private fetchDashboardStates(searchText?: string): Observable> {
+ this.dashboardStateSearchText = searchText;
+ const stateIds = Object.keys(this.dashboard.configuration.states);
+ const result = searchText ? stateIds.filter(this.createFilterForDashboardState(searchText)) : stateIds;
+ if (result && result.length) {
+ return of(result);
+ } else {
+ return of([searchText]);
+ }
+ }
+
+ private createFilterForDashboardState(query: string): (stateId: string) => boolean {
+ const lowercaseQuery = query.toLowerCase();
+ return stateId => stateId.toLowerCase().indexOf(lowercaseQuery) === 0;
+ }
+}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts
index 577738dbdb..13510dcd93 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts
@@ -38,6 +38,9 @@ import { LabelWidgetSettingsComponent } from '@home/components/widget/lib/settin
import {
SimpleCardWidgetSettingsComponent
} from '@home/components/widget/lib/settings/simple-card-widget-settings.component';
+import {
+ DashboardStateWidgetSettingsComponent
+} from '@home/components/widget/lib/settings/dashboard-state-widget-settings.component';
@NgModule({
declarations: [
@@ -49,7 +52,8 @@ import {
LabelWidgetFontComponent,
LabelWidgetLabelComponent,
LabelWidgetSettingsComponent,
- SimpleCardWidgetSettingsComponent
+ SimpleCardWidgetSettingsComponent,
+ DashboardStateWidgetSettingsComponent
],
imports: [
CommonModule,
@@ -65,7 +69,8 @@ import {
LabelWidgetFontComponent,
LabelWidgetLabelComponent,
LabelWidgetSettingsComponent,
- SimpleCardWidgetSettingsComponent
+ SimpleCardWidgetSettingsComponent,
+ DashboardStateWidgetSettingsComponent
]
})
export class WidgetSettingsModule {
@@ -78,5 +83,6 @@ export const widgetSettingsComponentsMap: {[key: string]: Type