>() {}).getBody();
+ }
+
public void deleteCalculatedField(CalculatedFieldId calculatedFieldId) {
restTemplate.delete(baseURL + "/api/calculatedField/{calculatedFieldId}", calculatedFieldId.getId());
}
diff --git a/transport/coap/src/main/resources/tb-coap-transport.yml b/transport/coap/src/main/resources/tb-coap-transport.yml
index dde415bd91..376075a0a4 100644
--- a/transport/coap/src/main/resources/tb-coap-transport.yml
+++ b/transport/coap/src/main/resources/tb-coap-transport.yml
@@ -421,6 +421,8 @@ usage:
enabled_per_customer: "${USAGE_STATS_REPORT_PER_CUSTOMER_ENABLED:false}"
# Interval of reporting the statistics. By default, the summarized statistics are sent every 10 seconds
interval: "${USAGE_STATS_REPORT_INTERVAL:60}"
+ # Reporting interval for urgent keys (e.g. SMS, Email) that require quicker usage state updates
+ urgent_interval: "${USAGE_STATS_REPORT_URGENT_INTERVAL:10}"
# Amount of statistic messages in pack
pack_size: "${USAGE_STATS_REPORT_PACK_SIZE:1024}"
@@ -435,7 +437,7 @@ management:
web:
exposure:
# Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics).
- include: '${METRICS_ENDPOINTS_EXPOSE:info}'
+ include: "${METRICS_ENDPOINTS_EXPOSE:info}"
# Notification system parameters
notification_system:
diff --git a/transport/http/src/main/resources/tb-http-transport.yml b/transport/http/src/main/resources/tb-http-transport.yml
index 39f7d15fb9..1f6a251324 100644
--- a/transport/http/src/main/resources/tb-http-transport.yml
+++ b/transport/http/src/main/resources/tb-http-transport.yml
@@ -370,6 +370,8 @@ usage:
enabled_per_customer: "${USAGE_STATS_REPORT_PER_CUSTOMER_ENABLED:false}"
# Interval of reporting the statistics. By default, the summarized statistics are sent every 10 seconds
interval: "${USAGE_STATS_REPORT_INTERVAL:60}"
+ # Reporting interval for urgent keys (e.g. SMS, Email) that require quicker usage state updates
+ urgent_interval: "${USAGE_STATS_REPORT_URGENT_INTERVAL:10}"
# Amount of statistic messages in pack
pack_size: "${USAGE_STATS_REPORT_PACK_SIZE:1024}"
@@ -384,7 +386,7 @@ management:
web:
exposure:
# Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics).
- include: '${METRICS_ENDPOINTS_EXPOSE:info}'
+ include: "${METRICS_ENDPOINTS_EXPOSE:info}"
# Notification system parameters
notification_system:
diff --git a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml
index 21687bb697..b4d8d6c7eb 100644
--- a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml
+++ b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml
@@ -471,6 +471,8 @@ usage:
enabled_per_customer: "${USAGE_STATS_REPORT_PER_CUSTOMER_ENABLED:false}"
# Interval of reporting the statistics. By default, the summarized statistics are sent every 10 seconds
interval: "${USAGE_STATS_REPORT_INTERVAL:60}"
+ # Reporting interval for urgent keys (e.g. SMS, Email) that require quicker usage state updates
+ urgent_interval: "${USAGE_STATS_REPORT_URGENT_INTERVAL:10}"
# Amount of statistic messages in pack
pack_size: "${USAGE_STATS_REPORT_PACK_SIZE:1024}"
@@ -485,7 +487,7 @@ management:
web:
exposure:
# Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics).
- include: '${METRICS_ENDPOINTS_EXPOSE:info}'
+ include: "${METRICS_ENDPOINTS_EXPOSE:info}"
# Notification system parameters
notification_system:
diff --git a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml
index 4a7571a734..0b96c57c13 100644
--- a/transport/mqtt/src/main/resources/tb-mqtt-transport.yml
+++ b/transport/mqtt/src/main/resources/tb-mqtt-transport.yml
@@ -404,6 +404,8 @@ usage:
enabled_per_customer: "${USAGE_STATS_REPORT_PER_CUSTOMER_ENABLED:false}"
# Interval of reporting the statistics. By default, the summarized statistics are sent every 10 seconds
interval: "${USAGE_STATS_REPORT_INTERVAL:60}"
+ # Reporting interval for urgent keys (e.g. SMS, Email) that require quicker usage state updates
+ urgent_interval: "${USAGE_STATS_REPORT_URGENT_INTERVAL:10}"
# Amount of statistic messages in pack
pack_size: "${USAGE_STATS_REPORT_PACK_SIZE:1024}"
@@ -418,7 +420,7 @@ management:
web:
exposure:
# Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics).
- include: '${METRICS_ENDPOINTS_EXPOSE:info}'
+ include: "${METRICS_ENDPOINTS_EXPOSE:info}"
# Notification system parameters
notification_system:
diff --git a/transport/snmp/src/main/resources/tb-snmp-transport.yml b/transport/snmp/src/main/resources/tb-snmp-transport.yml
index b98ad2a9e9..71524ba47e 100644
--- a/transport/snmp/src/main/resources/tb-snmp-transport.yml
+++ b/transport/snmp/src/main/resources/tb-snmp-transport.yml
@@ -359,6 +359,8 @@ usage:
enabled_per_customer: "${USAGE_STATS_REPORT_PER_CUSTOMER_ENABLED:false}"
# Interval of reporting the statistics. By default, the summarized statistics are sent every 10 seconds
interval: "${USAGE_STATS_REPORT_INTERVAL:60}"
+ # Reporting interval for urgent keys (e.g. SMS, Email) that require quicker usage state updates
+ urgent_interval: "${USAGE_STATS_REPORT_URGENT_INTERVAL:10}"
# Amount of statistic messages in pack
pack_size: "${USAGE_STATS_REPORT_PACK_SIZE:1024}"
@@ -373,7 +375,7 @@ management:
web:
exposure:
# Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics).
- include: '${METRICS_ENDPOINTS_EXPOSE:info}'
+ include: "${METRICS_ENDPOINTS_EXPOSE:info}"
# Notification system parameters
notification_system:
diff --git a/ui-ngx/patches/@iplab+ngx-color-picker+20.0.0.patch b/ui-ngx/patches/@iplab+ngx-color-picker+20.0.0.patch
index 211a3c8b0b..b0087a67e9 100644
--- a/ui-ngx/patches/@iplab+ngx-color-picker+20.0.0.patch
+++ b/ui-ngx/patches/@iplab+ngx-color-picker+20.0.0.patch
@@ -1,46 +1,13 @@
diff --git a/node_modules/@iplab/ngx-color-picker/fesm2022/iplab-ngx-color-picker.mjs b/node_modules/@iplab/ngx-color-picker/fesm2022/iplab-ngx-color-picker.mjs
-index a372799..a3d709a 100644
+index a372799..f64a6f8 100644
--- a/node_modules/@iplab/ngx-color-picker/fesm2022/iplab-ngx-color-picker.mjs
+++ b/node_modules/@iplab/ngx-color-picker/fesm2022/iplab-ngx-color-picker.mjs
-@@ -1129,11 +1129,11 @@ class RgbaComponent {
- this.color.set(newColor);
+@@ -516,7 +516,7 @@ class Color {
+ const s = (color.saturation / 100) * (l <= 1 ? l : 2 - l);
+ const value = (l + s) / 2;
+ const saturation = (2 * s) / (l + s) || 0;
+- return new Hsva(hue, saturation, value, color.alpha);
++ return new Hsva(hue, saturation * 100, value * 100, color.alpha);
}
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: RgbaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
-- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: RgbaComponent, isStandalone: true, selector: "rgba-input-component", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: true, transformFunction: null }, labelVisible: { classPropertyName: "labelVisible", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, isAlphaVisible: { classPropertyName: "isAlphaVisible", publicName: "alpha", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { color: "colorChange" }, ngImport: i0, template: "\r\n \r\n @if (labelVisible()) {\r\n R\r\n }\r\n
\r\n\r\n \r\n @if (labelVisible()) {\r\n G\r\n }\r\n
\r\n\r\n \r\n @if (labelVisible()) {\r\n B\r\n }\r\n
\r\n@if (isAlphaVisible()) {\r\n \r\n \r\n @if (labelVisible()) {\r\n A\r\n }\r\n
\r\n}", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n", ""], dependencies: [{ kind: "directive", type: ColorPickerInputDirective, selector: "[inputChange]", inputs: ["min", "max"], outputs: ["inputChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
-+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: RgbaComponent, isStandalone: true, selector: "rgba-input-component", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: true, transformFunction: null }, labelVisible: { classPropertyName: "labelVisible", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, isAlphaVisible: { classPropertyName: "isAlphaVisible", publicName: "alpha", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { color: "colorChange" }, ngImport: i0, template: "\r\n \r\n @if (labelVisible()) {\r\n R\r\n }\r\n
\r\n\r\n \r\n @if (labelVisible()) {\r\n G\r\n }\r\n
\r\n\r\n \r\n @if (labelVisible()) {\r\n B\r\n }\r\n
\r\n@if (isAlphaVisible()) {\r\n \r\n \r\n @if (labelVisible()) {\r\n A\r\n }\r\n
\r\n}", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n", ""], dependencies: [{ kind: "directive", type: ColorPickerInputDirective, selector: "[inputChange]", inputs: ["min", "max"], outputs: ["inputChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: RgbaComponent, decorators: [{
- type: Component,
-- args: [{ selector: `rgba-input-component`, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ColorPickerInputDirective], template: "\r\n \r\n @if (labelVisible()) {\r\n R\r\n }\r\n
\r\n\r\n \r\n @if (labelVisible()) {\r\n G\r\n }\r\n
\r\n\r\n \r\n @if (labelVisible()) {\r\n B\r\n }\r\n
\r\n@if (isAlphaVisible()) {\r\n \r\n \r\n @if (labelVisible()) {\r\n A\r\n }\r\n
\r\n}", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n"] }]
-+ args: [{ selector: `rgba-input-component`, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ColorPickerInputDirective], template: "\r\n \r\n @if (labelVisible()) {\r\n R\r\n }\r\n
\r\n\r\n \r\n @if (labelVisible()) {\r\n G\r\n }\r\n
\r\n\r\n \r\n @if (labelVisible()) {\r\n B\r\n }\r\n
\r\n@if (isAlphaVisible()) {\r\n \r\n \r\n @if (labelVisible()) {\r\n A\r\n }\r\n
\r\n}", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n"] }]
- }] });
-
- class HslaComponent {
-@@ -1155,11 +1155,11 @@ class HslaComponent {
- this.color.set(newColor);
- }
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: HslaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
-- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: HslaComponent, isStandalone: true, selector: "hsla-input-component", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: true, transformFunction: null }, labelVisible: { classPropertyName: "labelVisible", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, isAlphaVisible: { classPropertyName: "isAlphaVisible", publicName: "alpha", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { color: "colorChange" }, ngImport: i0, template: "\r\n \r\n @if (labelVisible()) {\r\n H\r\n }\r\n
\r\n\r\n \r\n @if (labelVisible()) {\r\n S\r\n }\r\n
\r\n\r\n \r\n @if (labelVisible()) {\r\n L\r\n }\r\n
\r\n@if (isAlphaVisible()) {\r\n \r\n \r\n @if (labelVisible()) {\r\n A\r\n }\r\n
\r\n}", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n", ""], dependencies: [{ kind: "directive", type: ColorPickerInputDirective, selector: "[inputChange]", inputs: ["min", "max"], outputs: ["inputChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
-+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: HslaComponent, isStandalone: true, selector: "hsla-input-component", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: true, transformFunction: null }, labelVisible: { classPropertyName: "labelVisible", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, isAlphaVisible: { classPropertyName: "isAlphaVisible", publicName: "alpha", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { color: "colorChange" }, ngImport: i0, template: "\r\n \r\n @if (labelVisible()) {\r\n H\r\n }\r\n
\r\n\r\n \r\n @if (labelVisible()) {\r\n S\r\n }\r\n
\r\n\r\n \r\n @if (labelVisible()) {\r\n L\r\n }\r\n
\r\n@if (isAlphaVisible()) {\r\n \r\n \r\n @if (labelVisible()) {\r\n A\r\n }\r\n
\r\n}", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n", ""], dependencies: [{ kind: "directive", type: ColorPickerInputDirective, selector: "[inputChange]", inputs: ["min", "max"], outputs: ["inputChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: HslaComponent, decorators: [{
- type: Component,
-- args: [{ selector: `hsla-input-component`, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ColorPickerInputDirective], template: "\r\n \r\n @if (labelVisible()) {\r\n H\r\n }\r\n
\r\n\r\n \r\n @if (labelVisible()) {\r\n S\r\n }\r\n
\r\n\r\n \r\n @if (labelVisible()) {\r\n L\r\n }\r\n
\r\n@if (isAlphaVisible()) {\r\n \r\n \r\n @if (labelVisible()) {\r\n A\r\n }\r\n
\r\n}", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n"] }]
-+ args: [{ selector: `hsla-input-component`, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ColorPickerInputDirective], template: "\r\n \r\n @if (labelVisible()) {\r\n H\r\n }\r\n
\r\n\r\n \r\n @if (labelVisible()) {\r\n S\r\n }\r\n
\r\n\r\n \r\n @if (labelVisible()) {\r\n L\r\n }\r\n
\r\n@if (isAlphaVisible()) {\r\n \r\n \r\n @if (labelVisible()) {\r\n A\r\n }\r\n
\r\n}", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n"] }]
- }] });
-
- class HexComponent {
-@@ -1190,11 +1190,11 @@ class HexComponent {
- }
- }
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: HexComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
-- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: HexComponent, isStandalone: true, selector: "hex-input-component", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: true, transformFunction: null }, labelVisible: { classPropertyName: "labelVisible", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, prefixValue: { classPropertyName: "prefixValue", publicName: "prefix", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { color: "colorChange" }, ngImport: i0, template: "\r\n \r\n @if (labelVisible()) {\r\n HEX\r\n }\r\n
", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n", ""], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
-+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.4", type: HexComponent, isStandalone: true, selector: "hex-input-component", inputs: { color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: true, transformFunction: null }, labelVisible: { classPropertyName: "labelVisible", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, prefixValue: { classPropertyName: "prefixValue", publicName: "prefix", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { color: "colorChange" }, ngImport: i0, template: "\r\n \r\n @if (labelVisible()) {\r\n HEX\r\n }\r\n
", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n", ""], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
- }
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.4", ngImport: i0, type: HexComponent, decorators: [{
- type: Component,
-- args: [{ selector: `hex-input-component`, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, template: "\r\n \r\n @if (labelVisible()) {\r\n HEX\r\n }\r\n
", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n"] }]
-+ args: [{ selector: `hex-input-component`, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, template: "\r\n \r\n @if (labelVisible()) {\r\n HEX\r\n }\r\n
", styles: [":host,:host ::ng-deep *{padding:0;margin:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}\n", ":host{display:table;width:100%;text-align:center;color:#b4b4b4;font-size:11px}.column{display:table-cell;padding:0 2px}input{width:100%;border:1px solid rgb(218,218,218);color:#272727;text-align:center;font-size:12px;-webkit-appearance:none;border-radius:0;margin:0 0 6px;height:26px;outline:none}\n"] }]
- }] });
-
- const OpacityAnimation = trigger('opacityAnimation', [
+ rgbaToHsva(color) {
+ const red = color.red / 255;
diff --git a/ui-ngx/patches/ngx-hm-carousel+19.0.0.patch b/ui-ngx/patches/ngx-hm-carousel+19.0.0.patch
new file mode 100644
index 0000000000..0dfee707e1
--- /dev/null
+++ b/ui-ngx/patches/ngx-hm-carousel+19.0.0.patch
@@ -0,0 +1,31 @@
+diff --git a/node_modules/ngx-hm-carousel/fesm2022/ngx-hm-carousel.mjs b/node_modules/ngx-hm-carousel/fesm2022/ngx-hm-carousel.mjs
+index 117f782..70e49a7 100644
+--- a/node_modules/ngx-hm-carousel/fesm2022/ngx-hm-carousel.mjs
++++ b/node_modules/ngx-hm-carousel/fesm2022/ngx-hm-carousel.mjs
+@@ -1,5 +1,5 @@
+ import * as i0 from '@angular/core';
+-import { Directive, inject, ViewContainerRef, TemplateRef, input, PLATFORM_ID, DestroyRef, Renderer2, NgZone, ChangeDetectorRef, viewChild, ElementRef, contentChildren, contentChild, computed, signal, effect, forwardRef, Component, ChangeDetectionStrategy } from '@angular/core';
++import { Directive, inject, ViewContainerRef, TemplateRef, input, PLATFORM_ID, DestroyRef, Renderer2, NgZone, ChangeDetectorRef, viewChild, ElementRef, contentChildren, contentChild, computed, signal, effect, forwardRef, Component, ChangeDetectionStrategy, afterNextRender } from '@angular/core';
+ import { DOCUMENT, isPlatformBrowser, NgTemplateOutlet, AsyncPipe } from '@angular/common';
+ import { toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';
+ import { NG_VALUE_ACCESSOR } from '@angular/forms';
+@@ -340,7 +340,7 @@ class NgxHmCarouselComponent {
+ });
+ });
+ });
+- const effectRef = effect(() => {
++ afterNextRender(() => {
+ this.rootElm = this.container().nativeElement;
+ this.containerElm = this.rootElm.children[0];
+ this.init();
+@@ -365,10 +365,6 @@ class NgxHmCarouselComponent {
+ ])
+ .pipe(takeUntilDestroyed(this._destroyRef))
+ .subscribe();
+- // only exec once
+- effectRef.destroy();
+- }, {
+- allowSignalWrites: true,
+ });
+ }
+ ngOnDestroy() {
diff --git a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts
index cadac572c6..039ee7d5fd 100644
--- a/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts
+++ b/ui-ngx/src/app/modules/home/components/alarm-rules/alarm-rules-table-config.ts
@@ -106,6 +106,8 @@ export class AlarmRulesTableConfig extends EntityTableConfig {
this.entityType = EntityType.API_KEY;
this.detailsPanelEnabled = false;
- this.addAsTextButton = true;
this.pageMode = false;
this.entityTranslations = entityTypeTranslations.get(EntityType.API_KEY);
diff --git a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html
index 6a797a37aa..c9247f5549 100644
--- a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html
+++ b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html
@@ -142,7 +142,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts
index 9187b69b73..b26eee40cf 100644
--- a/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts
+++ b/ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts
@@ -40,7 +40,7 @@ import { MatDialog } from '@angular/material/dialog';
import { DialogService } from '@core/services/dialog.service';
import { Direction, SortOrder } from '@shared/models/page/sort-order';
import { forkJoin, merge, Observable, Subject } from 'rxjs';
-import { debounceTime, distinctUntilChanged, takeUntil } from 'rxjs/operators';
+import { debounceTime, distinctUntilChanged, take, takeUntil } from 'rxjs/operators';
import { EntityId } from '@shared/models/id/entity-id';
import {
AttributeData,
@@ -187,6 +187,7 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI
textSearch = this.fb.control('', {nonNullable: true});
private destroy$ = new Subject();
+ selectAllModel: boolean = false;
constructor(protected store: Store,
private attributeService: AttributeService,
@@ -223,6 +224,14 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI
});
});
this.widgetResize$.observe(this.elementRef.nativeElement);
+
+ this.dataSource.selection.changed.pipe(
+ takeUntil(this.destroy$)
+ ).subscribe(() => {
+ this.dataSource.isAllSelected().pipe(take(1)).subscribe(allSelected => {
+ this.selectAllModel = allSelected;
+ });
+ });
}
ngOnDestroy() {
@@ -237,6 +246,7 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI
this.attributeScope = attributeScope;
this.mode = 'default';
this.paginator.pageIndex = 0;
+ this.selectAllModel = false;
this.updateData(true);
}
diff --git a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts
index efb767f92e..aac49a3f37 100644
--- a/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts
+++ b/ui-ngx/src/app/modules/home/components/calculated-fields/calculated-fields-table-config.ts
@@ -110,6 +110,8 @@ export class CalculatedFieldsTableConfig extends EntityTableConfig
-
-
-
@@ -118,6 +94,72 @@
matTooltipPosition="above">
search
+
+
+
+ add
+ {{ translations.add | translate }}
+
+
+ add
+
+
+
+
+
+
+ {{entitiesTableConfig.addActionDescriptors[0].icon}}
+ {{ entitiesTableConfig.addActionDescriptors[0].name }}
+
+
+ {{entitiesTableConfig.addActionDescriptors[0].icon}}
+
+
+
+
+
+ add
+ {{ translations.add | translate }}
+
+
+ add
+
+
+
+
+
+
+ {{actionDescriptor.icon}}
+ {{ actionDescriptor.name }}
+
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.scss b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.scss
index 38ab6294ea..3a1e3f6a1b 100644
--- a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.scss
+++ b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.scss
@@ -43,7 +43,7 @@
}
}
- > .mat-expansion-panel-content {
+ .mat-expansion-panel-content {
> .mat-expansion-panel-body {
padding: 0;
}
diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.html b/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.html
index cc3fb23da5..829c1414db 100644
--- a/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.html
+++ b/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.html
@@ -32,7 +32,8 @@
-
+
update
{{'version-control.create-version' | translate }}
-
- update
- {{'version-control.create-entities-version' | translate }}
-
search
+
+ update
+ {{'version-control.create-entities-version' | translate }}
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.ts
index 9204a901b2..d569bcf37b 100644
--- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/bar-chart-with-labels-basic-config.component.ts
@@ -45,7 +45,10 @@ import {
barChartWithLabelsDefaultSettings,
BarChartWithLabelsWidgetSettings
} from '@home/components/widget/lib/chart/bar-chart-with-labels-widget.models';
-import { TimeSeriesChartType } from '@home/components/widget/lib/chart/time-series-chart.models';
+import {
+ TimeSeriesChartType,
+ updateLatestDataKeys
+} from '@home/components/widget/lib/chart/time-series-chart.models';
import { getSourceTbUnitSymbol } from '@shared/models/unit.models';
@Component({
@@ -76,7 +79,7 @@ export class BarChartWithLabelsBasicConfigComponent extends BasicWidgetConfigCom
tooltipDatePreviewFn = this._tooltipDatePreviewFn.bind(this);
predefinedValues = widgetTitleAutocompleteValues;
-
+
constructor(protected store: Store,
protected widgetConfigComponent: WidgetConfigComponent,
private $injector: Injector,
@@ -167,6 +170,11 @@ export class BarChartWithLabelsBasicConfigComponent extends BasicWidgetConfigCom
});
}
+ protected onConfigChanged(widgetConfig: WidgetConfigComponentData) {
+ updateLatestDataKeys([widgetConfig.config.settings.yAxis], this.datasource, this.callbacks);
+ super.onConfigChanged(widgetConfig);
+ }
+
protected prepareOutputConfig(config: any): WidgetConfigComponentData {
setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig);
this.widgetConfig.config.datasources = config.datasources;
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts
index 0527045650..95dbabfbfc 100644
--- a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/range-chart-basic-config.component.ts
@@ -47,7 +47,7 @@ import {
} from '@home/components/widget/lib/chart/range-chart-widget.models';
import {
lineSeriesStepTypes,
- lineSeriesStepTypeTranslations
+ lineSeriesStepTypeTranslations, updateLatestDataKeys
} from '@home/components/widget/lib/chart/time-series-chart.models';
import {
chartLabelPositions,
@@ -289,6 +289,11 @@ export class RangeChartBasicConfigComponent extends BasicWidgetConfigComponent {
return this.widgetConfig;
}
+ protected onConfigChanged(widgetConfig: WidgetConfigComponentData) {
+ updateLatestDataKeys([widgetConfig.config.settings.yAxis], this.datasource, this.callbacks);
+ super.onConfigChanged(widgetConfig);
+ }
+
protected validatorTriggers(): string[] {
return ['showTitle', 'showIcon', 'showRangeThresholds', 'fillArea', 'showLine',
'step', 'showPointLabel', 'enablePointLabelBackground', 'showLegend', 'showTooltip', 'tooltipShowDate'];
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts
index 1f63cd8638..0287b775c3 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts
@@ -134,6 +134,12 @@ export class BarChartWithLabelsWidgetComponent implements OnInit, OnDestroy, Aft
}
}
+ public onLatestDataUpdated() {
+ if (this.timeSeriesChart) {
+ this.timeSeriesChart.latestUpdated();
+ }
+ }
+
public onLegendKeyEnter(key: DataKey) {
this.timeSeriesChart.keyEnter(key);
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts
index 2412d51a83..4ad45c85a2 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts
@@ -162,6 +162,12 @@ export class RangeChartWidgetComponent implements OnInit, OnDestroy, AfterViewIn
}
}
+ public onLatestDataUpdated() {
+ if (this.timeSeriesChart) {
+ this.timeSeriesChart.latestUpdated();
+ }
+ }
+
public toggleRangeItem(item: RangeItem) {
item.enabled = !item.enabled;
this.timeSeriesChart.toggleVisualMapRange(item.index);
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts
index 4816852768..e815eefe11 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts
@@ -99,6 +99,8 @@ import {
TimeSeriesChartTooltipWidgetSettings
} from '@home/components/widget/lib/chart/time-series-chart-tooltip.models';
import { TbUnit, TbUnitConverter } from '@shared/models/unit.models';
+import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
+import { DataKeysCallbacks } from '@home/components/widget/lib/settings/common/key/data-keys.component.models';
type TimeSeriesChartDataEntry = [number, any, number, number];
@@ -1495,3 +1497,101 @@ const createSeriesLabelOption = (item: TimeSeriesChartDataItem, show: boolean,
}
return labelOption;
};
+
+export const checkLatestDataKeys = (yAxes: TimeSeriesChartYAxes, datasource: Datasource): TimeSeriesChartYAxes => {
+ const latestKeys = datasource?.latestDataKeys || [];
+ const result: TimeSeriesChartYAxes = {};
+
+ for (const [id, axis] of Object.entries(yAxes)) {
+ axis.min = normalizeAxisLimit(axis.min);
+ axis.max = normalizeAxisLimit(axis.max);
+ const minCfg = axis.min;
+ const maxCfg = axis.max;
+
+ const minValid = !!minCfg && (
+ minCfg.type !== ValueSourceType.latestKey ||
+ latestKeys.some(k => isYAxisKey(k, minCfg))
+ );
+
+ const maxValid = !!maxCfg && (
+ maxCfg.type !== ValueSourceType.latestKey ||
+ latestKeys.some(k => isYAxisKey(k, maxCfg))
+ );
+
+ if (minValid && maxValid) {
+ result[id] = axis;
+ }
+ }
+
+ return result;
+}
+
+export const updateLatestDataKeys = (yAxes: TimeSeriesChartYAxisSettings[], datasource: Datasource, dataKeyCallbacks: DataKeysCallbacks)=> {
+ if (datasource) {
+ let latestKeys = datasource.latestDataKeys;
+ if (!latestKeys) {
+ latestKeys = [];
+ datasource.latestDataKeys = latestKeys;
+ }
+ const existingYAxisKeys = latestKeys.filter(k => k.settings?.__yAxisMinKey === true || k.settings?.__yAxisMaxKey === true);
+ const foundYAxisKeys: DataKey[] = [];
+
+ for(const yAxis of yAxes) {
+ const min = yAxis.min as ValueSourceConfig;
+ const max = yAxis.max as ValueSourceConfig;
+ if (min && min.type === ValueSourceType.latestKey) {
+ const found = existingYAxisKeys.find(k => isYAxisKey(k, min));
+ if (!found) {
+ const newKey = dataKeyCallbacks.generateDataKey(min.latestKey, min.latestKeyType,
+ null, true, null);
+ newKey.settings.__yAxisMinKey = true;
+ latestKeys.push(newKey);
+ } else if (foundYAxisKeys.indexOf(found) === -1) {
+ foundYAxisKeys.push(found);
+ }
+ }
+ if (max && max.type === ValueSourceType.latestKey) {
+ const found = existingYAxisKeys.find(k => isYAxisKey(k, max));
+ if (!found) {
+ const newKey = dataKeyCallbacks.generateDataKey(max.latestKey, max.latestKeyType,
+ null, true, null);
+ newKey.settings.__yAxisMaxKey = true;
+ latestKeys.push(newKey);
+ } else if (foundYAxisKeys.indexOf(found) === -1) {
+ foundYAxisKeys.push(found);
+ }
+ }
+ }
+ const toRemove = existingYAxisKeys.filter(k => foundYAxisKeys.indexOf(k) === -1);
+ for (const key of toRemove) {
+ const index = latestKeys.indexOf(key);
+ if (index > -1) {
+ latestKeys.splice(index, 1);
+ }
+ }
+ }
+}
+
+export const isYAxisKey = (d: DataKey, limit: ValueSourceConfig): boolean => {
+ return (d.type === DataKeyType.function && d.label === limit.latestKey) ||
+ (d.type !== DataKeyType.function && d.name === limit.latestKey &&
+ d.type === limit.latestKeyType);
+}
+
+export const normalizeAxisLimit = (limit: string | number | ValueSourceConfig): ValueSourceConfig => {
+ if (!limit) {
+ return {
+ type: ValueSourceType.constant,
+ value: null,
+ entityAlias: null
+ };
+ } else if (typeof limit === 'number' || typeof limit === 'string') {
+ return {
+ type: ValueSourceType.constant,
+ value: Number(limit),
+ entityAlias: null
+ };
+ }
+ return limit;
+}
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts
index 21f5f2d68c..d1928d75f0 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts
@@ -23,7 +23,7 @@ import {
createTimeSeriesYAxis,
defaultTimeSeriesChartYAxisSettings,
generateChartData,
- LineSeriesStepType,
+ LineSeriesStepType, normalizeAxisLimit,
parseThresholdData,
TimeSeriesChartAxis,
TimeSeriesChartDataItem,
@@ -581,8 +581,8 @@ export class TbTimeSeriesChart {
yAxisSettingsList.sort((a1, a2) => a1.order - a2.order);
const axisLimitDatasources: Datasource[] = [];
for (const yAxisSettings of yAxisSettingsList) {
- yAxisSettings.min = this.normalizeAxisLimit(yAxisSettings.min);
- yAxisSettings.max = this.normalizeAxisLimit(yAxisSettings.max);
+ yAxisSettings.min = normalizeAxisLimit(yAxisSettings.min);
+ yAxisSettings.max = normalizeAxisLimit(yAxisSettings.max);
const axisSettings = mergeDeep({} as TimeSeriesChartYAxisSettings,
defaultTimeSeriesChartYAxisSettings, yAxisSettings);
const units = isNotEmptyTbUnits(axisSettings.units) ? axisSettings.units : this.ctx.units;
@@ -1080,21 +1080,4 @@ export class TbTimeSeriesChart {
this.timeSeriesChart.setOption(this.timeSeriesChartOptions);
}
}
-
- private normalizeAxisLimit(limit: string | number | ValueSourceConfig): string | number | ValueSourceConfig {
- if (!limit) {
- return {
- type: ValueSourceType.constant,
- value: null,
- entityAlias: null
- };
- } else if (typeof limit === 'number' || typeof limit === 'string') {
- return {
- type: ValueSourceType.constant,
- value: Number(limit),
- entityAlias: null
- };
- }
- return limit;
- }
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.html
index f832b1982e..e8fba081ee 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.html
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.html
@@ -24,8 +24,15 @@
{{ 'widgets.recent-dashboards.last' | translate }}
{{ 'widgets.recent-dashboards.starred' | translate }}
- {{ 'dashboard.add' | translate }}
+
+ @if (hasDevice) {
+ {{ 'dashboard.add' | translate }}
+ } @else {
+ {{ 'dashboard.add' | translate }}
+ }
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.ts
index 899ffa5aea..5dfc520856 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.ts
@@ -48,6 +48,13 @@ import { Direction, SortOrder } from '@shared/models/page/sort-order';
import { MatSort } from '@angular/material/sort';
import { DashboardInfo } from '@shared/models/dashboard.models';
import { DashboardAutocompleteComponent } from '@shared/components/dashboard-autocomplete.component';
+import { UtilsService } from '@core/services/utils.service';
+import { Datasource, DatasourceType, widgetType } from '@shared/models/widget.models';
+import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models';
+import { formattedDataFormDatasourceData } from '@core/utils';
+import { AliasFilterType } from '@shared/models/alias.models';
+import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
+import { EntityType } from '@shared/models/entity-type.models';
@Component({
selector: 'tb-recent-dashboards-widget',
@@ -78,13 +85,16 @@ export class RecentDashboardsWidgetComponent extends PageComponent implements On
starredDashboardValue = null;
hasDashboardsAccess = true;
+ hasDevice = true;
dirty = false;
public customerId: string;
private isFullscreenMode = getCurrentAuthState(this.store).forceFullscreen;
+ private subscription: IWidgetSubscription;
constructor(protected store: Store,
private cd: ChangeDetectorRef,
+ private utils: UtilsService,
private userSettingService: UserSettingsService) {
super(store);
}
@@ -96,6 +106,41 @@ export class RecentDashboardsWidgetComponent extends PageComponent implements On
this.hasDashboardsAccess = [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER].includes(this.authUser.authority);
if (this.hasDashboardsAccess) {
this.reload();
+
+ if (window.location.pathname.startsWith('/home') && this.authUser.authority === Authority.TENANT_ADMIN) {
+ const ds: Datasource = {
+ type: DatasourceType.entityCount,
+ name: '',
+ entityFilter: {
+ entityType: EntityType.DEVICE,
+ type: AliasFilterType.entityType
+ },
+ dataKeys: [this.utils.createKey({ name: 'count'}, DataKeyType.count)]
+ }
+
+ const apiUsageSubscriptionOptions: WidgetSubscriptionOptions = {
+ datasources: [ds],
+ useDashboardTimewindow: false,
+ type: widgetType.latest,
+ callbacks: {
+ onDataUpdated: (subscription) => {
+ const data = formattedDataFormDatasourceData(subscription.data);
+ this.hasDevice = (data[0].count || 0) !== 0;
+ this.cd.detectChanges();
+ }
+ }
+ };
+ this.ctx.subscriptionApi.createSubscription(apiUsageSubscriptionOptions, true).subscribe((subscription) => {
+ this.subscription = subscription;
+ });
+ }
+ }
+ }
+
+ ngOnDestroy() {
+ super.ngOnDestroy();
+ if (this.subscription) {
+ this.ctx.subscriptionApi.removeSubscription(this.subscription.id);
}
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts
index 0ea5c898bd..4836cc3804 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts
@@ -337,6 +337,9 @@ export abstract class PowerButtonShape {
protected onPowerSymbolLine: Path;
private onIcon$: Observable;
+ private offIcon$: Observable;
+ protected onIconOffsetX: number = 0;
+ protected onIconOffsetY: number = 0;
protected constructor(protected widgetContext: WidgetContext,
protected svgShape: Svg,
@@ -362,10 +365,14 @@ export abstract class PowerButtonShape {
take(1),
map((svgElement) => {
const element = new Element(svgElement.firstChild);
+ const iconGroup = this.svgShape.group();
+ element.addTo(iconGroup);
const box = element.bbox();
const scale = size / box.height;
element.scale(scale);
- return element;
+ const scaledBox = iconGroup.bbox();
+ iconGroup.translate(-scaledBox.cx, -scaledBox.cy);
+ return iconGroup;
}),
catchError(() => of(null)
));
@@ -416,8 +423,15 @@ export abstract class PowerButtonShape {
public drawOffShape(centerGroup: G, label: boolean, labelWeight?: string, circleStroke?: boolean) {
if (this.icons.offButtonIcon.showIcon) {
- this.createIconElement(this.icons.offButtonIcon.icon, this.icons.offButtonIcon.iconSize).subscribe(icon =>
- this.offPowerSymbolIcon = icon.center(cx, cy).addTo(centerGroup));
+ this.offIcon$ = this.createIconElement(this.icons.offButtonIcon.icon, this.icons.offButtonIcon.iconSize)
+ .pipe(shareReplay(1));
+
+ this.offIcon$.pipe(take(1)).subscribe(icon => {
+ this.offPowerSymbolIcon = icon;
+ icon.translate(cx, cy);
+ centerGroup.add(icon);
+ });
+
} else {
if (label) {
this.offLabelShape = this.createOffLabel(labelWeight).addTo(centerGroup);
@@ -434,9 +448,12 @@ export abstract class PowerButtonShape {
.pipe(shareReplay(1));
this.onIcon$.subscribe(icon => {
- this.onPowerSymbolIcon = icon.center(cx, cy);
+ const iconBox = icon.bbox();
+ this.onIconOffsetX = iconBox.cx;
+ this.onIconOffsetY = iconBox.cy;
+ this.onPowerSymbolIcon = icon.translate(cx, cy);
if (isDefinedAndNotNull(onCenterGroup)) {
- this.onPowerSymbolIcon.addTo(onCenterGroup);
+ onCenterGroup.add(this.onPowerSymbolIcon);
}
if (isDefinedAndNotNull(mask)) {
this.createMask(mask, [this.onPowerSymbolIcon]);
@@ -468,7 +485,7 @@ export abstract class PowerButtonShape {
public onCenterTimeLine(timeline: Timeline, label: boolean) {
if (this.icons.onButtonIcon.showIcon) {
if (this.onIcon$) {
- this.onIcon$.subscribe(icon => icon.timeline(timeline))
+ this.onIcon$.subscribe(icon => icon.timeline(timeline));
}
} else {
if (label) {
@@ -482,7 +499,7 @@ export abstract class PowerButtonShape {
public offCenterColor(mainColor: PowerButtonColor, label: boolean) {
if (this.icons.offButtonIcon.showIcon) {
- this.offPowerSymbolIcon.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity});
+ this.offIcon$.subscribe(icon => icon.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}))
} else {
if (label) {
this.offLabelShape.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity});
@@ -495,7 +512,7 @@ export abstract class PowerButtonShape {
public onCenterColor(mainColor: PowerButtonColor, label: boolean) {
if (this.icons.onButtonIcon.showIcon) {
- this.onPowerSymbolIcon.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity});
+ this.onIcon$.subscribe((icon)=> icon.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}))
} else {
if (label) {
this.onLabelShape.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity});
@@ -508,7 +525,12 @@ export abstract class PowerButtonShape {
public buttonAnimation(scale: number, label: boolean) {
if (this.icons.onButtonIcon.showIcon) {
- powerButtonAnimation(this.onPowerSymbolIcon).transform({scale});
+ const translateX = cx - this.onIconOffsetX;
+ const translateY = cy - this.onIconOffsetY;
+ powerButtonAnimation(this.onPowerSymbolIcon).transform({
+ scale: scale,
+ translate: [translateX, translateY]
+ });
} else {
if (label) {
powerButtonAnimation(this.onLabelShape).transform({scale, origin: {x: cx, y: cy}});
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.ts
index 6290572770..55ed3d5035 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.ts
@@ -31,6 +31,7 @@ import {
barChartWithLabelsDefaultSettings
} from '@home/components/widget/lib/chart/bar-chart-with-labels-widget.models';
import { getSourceTbUnitSymbol } from '@shared/models/unit.models';
+import { updateLatestDataKeys } from '@home/components/widget/lib/chart/time-series-chart.models';
@Component({
selector: 'tb-bar-chart-with-labels-widget-settings',
@@ -123,6 +124,11 @@ export class BarChartWithLabelsWidgetSettingsComponent extends WidgetSettingsCom
});
}
+ protected onSettingsChanged(updated: WidgetSettings) {
+ updateLatestDataKeys([updated.yAxis], this.datasource, this.dataKeyCallbacks);
+ super.onSettingsChanged(updated);
+ }
+
protected validatorTriggers(): string[] {
return ['showBarLabel', 'showBarValue', 'showBarBorder', 'showLegend', 'showTooltip', 'tooltipShowDate'];
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts
index 703c621d62..053b6858bc 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/range-chart-widget-settings.component.ts
@@ -30,7 +30,7 @@ import { rangeChartDefaultSettings } from '@home/components/widget/lib/chart/ran
import { DateFormatProcessor, DateFormatSettings } from '@shared/models/widget-settings.models';
import {
lineSeriesStepTypes,
- lineSeriesStepTypeTranslations
+ lineSeriesStepTypeTranslations, updateLatestDataKeys
} from '@home/components/widget/lib/chart/time-series-chart.models';
import {
chartLabelPositions,
@@ -269,6 +269,11 @@ export class RangeChartWidgetSettingsComponent extends WidgetSettingsComponent {
}
}
+ protected onSettingsChanged(updated: WidgetSettings) {
+ updateLatestDataKeys([updated.yAxis], this.datasource, this.dataKeyCallbacks);
+ super.onSettingsChanged(updated);
+ }
+
private _pointLabelPreviewFn(): string {
const units = getSourceTbUnitSymbol(this.widgetConfig.config.units);
const decimals: number = this.widgetConfig.config.decimals;
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/axis-scale-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/axis-scale-row.component.ts
index 24037f2170..914eb0abb2 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/axis-scale-row.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/axis-scale-row.component.ts
@@ -97,7 +97,7 @@ export class AxisScaleRowComponent implements ControlValueAccessor, OnInit, Vali
this.limitForm = this.fb.group({
type: [ValueSourceType.constant],
value: [null],
- entityAlias: [null]
+ entityAlias: [null, [Validators.required]]
});
this.latestKeyFormControl = this.fb.control(null, [Validators.required]);
this.entityKeyFormControl = this.fb.control(null, [Validators.required]);
@@ -169,23 +169,24 @@ export class AxisScaleRowComponent implements ControlValueAccessor, OnInit, Vali
}
private updateValidators() {
- const axisTypeControl = this.limitForm.get('type');
- if (axisTypeControl && this.entityKeyFormControl && this.latestKeyFormControl) {
- const type = axisTypeControl.value;
- if (type === ValueSourceType.latestKey) {
- this.latestKeyFormControl.setValidators([Validators.required]);
- this.entityKeyFormControl.clearValidators();
- } else if (type === ValueSourceType.entity) {
- this.latestKeyFormControl.clearValidators();
- this.limitForm.get('entityAlias').setValidators([Validators.required]);
- this.entityKeyFormControl.setValidators([Validators.required]);
- } else {
- this.latestKeyFormControl.clearValidators();
- this.entityKeyFormControl.clearValidators();
- }
- this.latestKeyFormControl.updateValueAndValidity({ emitEvent: false });
- this.entityKeyFormControl.updateValueAndValidity({ emitEvent: false });
- }
+ const type = this.limitForm.get('type')?.value;
+ const entityAliasCtr = this.limitForm.get('entityAlias');
+
+ const isLatestKey = type === ValueSourceType.latestKey;
+ const isEntity = type === ValueSourceType.entity;
+
+ isLatestKey ? this.latestKeyFormControl.enable({ emitEvent: false })
+ : this.latestKeyFormControl.disable({ emitEvent: false });
+
+ isEntity ? this.entityKeyFormControl.enable({ emitEvent: false })
+ : this.entityKeyFormControl.disable({ emitEvent: false });
+
+ isEntity ? entityAliasCtr.enable({ emitEvent: false })
+ : entityAliasCtr.disable({ emitEvent: false });
+
+ this.latestKeyFormControl.updateValueAndValidity({ emitEvent: false });
+ this.entityKeyFormControl.updateValueAndValidity({ emitEvent: false });
+ entityAliasCtr.updateValueAndValidity({ emitEvent: false });
}
private updateModel() {
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts
index db0f8534a9..2f23666574 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts
@@ -25,7 +25,7 @@ import {
Validators
} from '@angular/forms';
import {
- AxisPosition, defaultXAxisTicksFormat,
+ AxisPosition, defaultXAxisTicksFormat, normalizeAxisLimit,
timeSeriesAxisPositionTranslations,
TimeSeriesChartAxisSettings, TimeSeriesChartXAxisSettings,
TimeSeriesChartYAxisSettings
@@ -138,8 +138,8 @@ export class TimeSeriesChartAxisSettingsComponent implements OnInit, ControlValu
this.axisSettingsFormGroup.addControl('ticksGenerator', this.fb.control(null, []));
this.axisSettingsFormGroup.addControl('interval', this.fb.control(null, [Validators.min(0)]));
this.axisSettingsFormGroup.addControl('splitNumber', this.fb.control(null, [Validators.min(1)]));
- this.axisSettingsFormGroup.addControl('min', this.fb.control(null, []));
- this.axisSettingsFormGroup.addControl('max', this.fb.control(null, []));
+ this.axisSettingsFormGroup.addControl('min', this.fb.control(normalizeAxisLimit(null), []));
+ this.axisSettingsFormGroup.addControl('max', this.fb.control(normalizeAxisLimit(null), []));
} else if (this.axisType === 'xAxis') {
this.axisSettingsFormGroup.addControl('ticksFormat', this.fb.control(null, []));
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts
index 694e856bfa..662214a1df 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts
@@ -36,13 +36,15 @@ import {
Validator
} from '@angular/forms';
import {
+ checkLatestDataKeys,
defaultTimeSeriesChartYAxisSettings,
getNextTimeSeriesYAxisId,
TimeSeriesChartYAxes,
TimeSeriesChartYAxisId,
TimeSeriesChartYAxisSettings,
timeSeriesChartYAxisValid,
- timeSeriesChartYAxisValidator
+ timeSeriesChartYAxisValidator,
+ updateLatestDataKeys
} from '@home/components/widget/lib/chart/time-series-chart.models';
import { mergeDeep } from '@core/utils';
import { CdkDragDrop } from '@angular/cdk/drag-drop';
@@ -50,7 +52,7 @@ import { coerceBoolean } from '@shared/decorators/coercion';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { IAliasController } from '@app/core/public-api';
import { DataKeysCallbacks } from '@home/components/widget/lib/settings/common/key/data-keys.component.models';
-import { DataKey, DataKeyType, Datasource, ValueSourceConfig, ValueSourceType } from '@app/shared/public-api';
+import { Datasource } from '@app/shared/public-api';
@Component({
selector: 'tb-time-series-chart-y-axes-panel',
@@ -127,7 +129,7 @@ export class TimeSeriesChartYAxesPanelComponent implements ControlValueAccessor,
for (const axis of axes) {
yAxes[axis.id] = axis;
}
- this.updateLatestDataKeys(Object.values(yAxes));
+ updateLatestDataKeys(Object.values(yAxes), this.datasource, this.dataKeyCallbacks);
this.propagateChange(yAxes);
}
);
@@ -150,7 +152,7 @@ export class TimeSeriesChartYAxesPanelComponent implements ControlValueAccessor,
}
writeValue(value: TimeSeriesChartYAxes | undefined): void {
- const yAxes: TimeSeriesChartYAxes = this.checkLatestDataKeys(value || {});
+ const yAxes: TimeSeriesChartYAxes = checkLatestDataKeys(value || {}, this.datasource);
if (!yAxes.default) {
yAxes.default = mergeDeep({} as TimeSeriesChartYAxisSettings, defaultTimeSeriesChartYAxisSettings,
{id: 'default', order: 0} as TimeSeriesChartYAxisSettings);
@@ -197,8 +199,6 @@ export class TimeSeriesChartYAxesPanelComponent implements ControlValueAccessor,
const axes: TimeSeriesChartYAxisSettings[] = this.yAxesFormGroup.get('axes').value;
axis.id = getNextTimeSeriesYAxisId(axes);
axis.order = axes.length;
- axis.min = this.normalizeAxisLimit(axis.min);
- axis.max = this.normalizeAxisLimit(axis.max);
const axesArray = this.yAxesFormGroup.get('axes') as UntypedFormArray;
const axisControl = this.fb.control(axis, [timeSeriesChartYAxisValidator]);
axesArray.push(axisControl);
@@ -212,100 +212,4 @@ export class TimeSeriesChartYAxesPanelComponent implements ControlValueAccessor,
return this.fb.array(axesControls);
}
- private checkLatestDataKeys(yAxes: TimeSeriesChartYAxes): TimeSeriesChartYAxes {
- const latestKeys = this.datasource?.latestDataKeys || [];
- const result: TimeSeriesChartYAxes = {};
-
- for (const [id, axis] of Object.entries(yAxes)) {
- axis.min = this.normalizeAxisLimit(axis.min);
- axis.max = this.normalizeAxisLimit(axis.max);
- const minCfg = axis.min;
- const maxCfg = axis.max;
-
- const minValid = !!minCfg && (
- minCfg.type !== ValueSourceType.latestKey ||
- latestKeys.some(k => this.isYAxisKey(k, minCfg))
- );
-
- const maxValid = !!maxCfg && (
- maxCfg.type !== ValueSourceType.latestKey ||
- latestKeys.some(k => this.isYAxisKey(k, maxCfg))
- );
-
- if (minValid && maxValid) {
- result[id] = axis;
- }
- }
-
- return result;
- }
-
- private updateLatestDataKeys(yAxes: TimeSeriesChartYAxisSettings[]) {
- if (this.datasource) {
- let latestKeys = this.datasource.latestDataKeys;
- if (!latestKeys) {
- latestKeys = [];
- this.datasource.latestDataKeys = latestKeys;
- }
- const existingYAxisKeys = latestKeys.filter(k => k.settings?.__yAxisMinKey === true || k.settings?.__yAxisMaxKey === true);
- const foundYAxisKeys: DataKey[] = [];
-
- for(const yAxis of yAxes) {
- const min = yAxis.min as ValueSourceConfig;
- const max = yAxis.max as ValueSourceConfig;
- if (min.type === ValueSourceType.latestKey) {
- const found = existingYAxisKeys.find(k => this.isYAxisKey(k, min));
- if (!found) {
- const newKey = this.dataKeyCallbacks.generateDataKey(min.latestKey, min.latestKeyType,
- null, true, null);
- newKey.settings.__yAxisMinKey = true;
- latestKeys.push(newKey);
- } else if (foundYAxisKeys.indexOf(found) === -1) {
- foundYAxisKeys.push(found);
- }
- }
- if (max.type === ValueSourceType.latestKey) {
- const found = existingYAxisKeys.find(k => this.isYAxisKey(k, max));
- if (!found) {
- const newKey = this.dataKeyCallbacks.generateDataKey(max.latestKey, max.latestKeyType,
- null, true, null);
- newKey.settings.__yAxisMaxKey = true;
- latestKeys.push(newKey);
- } else if (foundYAxisKeys.indexOf(found) === -1) {
- foundYAxisKeys.push(found);
- }
- }
- }
- const toRemove = existingYAxisKeys.filter(k => foundYAxisKeys.indexOf(k) === -1);
- for (const key of toRemove) {
- const index = latestKeys.indexOf(key);
- if (index > -1) {
- latestKeys.splice(index, 1);
- }
- }
- }
- }
-
- private isYAxisKey(d: DataKey, limit: ValueSourceConfig): boolean {
- return (d.type === DataKeyType.function && d.label === limit.latestKey) ||
- (d.type !== DataKeyType.function && d.name === limit.latestKey &&
- d.type === limit.latestKeyType);
- }
-
- private normalizeAxisLimit(limit: string | number | ValueSourceConfig): ValueSourceConfig {
- if (!limit) {
- return {
- type: ValueSourceType.constant,
- value: null,
- entityAlias: null
- };
- } else if (typeof limit === 'number' || typeof limit === 'string') {
- return {
- type: ValueSourceType.constant,
- value: Number(limit),
- entityAlias: null
- };
- }
- return limit;
- }
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts
index 5298b7f45b..93e91124d5 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts
@@ -29,7 +29,7 @@ import {
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import {
- AxisPosition,
+ AxisPosition, normalizeAxisLimit,
timeSeriesAxisPositionTranslations,
TimeSeriesChartYAxisSettings
} from '@home/components/widget/lib/chart/time-series-chart.models';
@@ -136,8 +136,8 @@ export class TimeSeriesChartYAxisRowComponent implements ControlValueAccessor, O
writeValue(value: TimeSeriesChartYAxisSettings): void {
this.modelValue = value;
- const min = this.normalizeLimit(value.min);
- const max = this.normalizeLimit(value.max);
+ const min = normalizeAxisLimit(value.min);
+ const max = normalizeAxisLimit(value.max);
this.axisFormGroup.patchValue({
label: value.label,
@@ -252,27 +252,4 @@ export class TimeSeriesChartYAxisRowComponent implements ControlValueAccessor, O
entityKeyType: [null, []]
});
}
-
- private normalizeLimit(limit: any) {
- const base = {
- type: ValueSourceType.constant,
- value: null,
- latestKey: null,
- latestKeyType: null,
- entityAlias: null,
- entityKey: null,
- entityKeyType: null
- };
-
- if (limit == null) return base;
-
- if (typeof limit === 'number' || typeof limit === 'string') {
- return { ...base, type: ValueSourceType.constant, value: Number(limit) };
- }
-
- return {
- ...base,
- ...limit,
- };
- }
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/image-cards-select.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/image-cards-select.component.scss
index 62915cc390..de1901aa4a 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/image-cards-select.component.scss
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/image-cards-select.component.scss
@@ -44,7 +44,7 @@
display: none;
}
}
- > .mat-expansion-panel-content {
+ .mat-expansion-panel-content {
> .mat-expansion-panel-body {
padding: 0 16px 16px !important;
}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings.component.ts
index 6da6c809e0..989e9ef0c7 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/marker-image-settings.component.ts
@@ -82,11 +82,12 @@ export class MarkerImageSettingsComponent implements ControlValueAccessor {
renderer: this.renderer,
componentType: MarkerImageSettingsPanelComponent,
hostView: this.viewContainerRef,
- preferredPlacement: 'left',
+ preferredPlacement: 'leftOnly',
context: {
markerImageSettings: this.modelValue,
},
- isModal: true
+ isModal: true,
+ overlayStyle: {padding: '10px'}
});
markerImageSettingsPanelPopover.tbComponentRef.instance.popover = markerImageSettingsPanelPopover;
markerImageSettingsPanelPopover.tbComponentRef.instance.markerImageSettingsApplied.subscribe((markerImageSettings) => {
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/shape-fill-image-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/shape-fill-image-settings.component.ts
index 5c9bbb260f..74f4ddd1eb 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/shape-fill-image-settings.component.ts
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/map/shape-fill-image-settings.component.ts
@@ -82,11 +82,12 @@ export class ShapeFillImageSettingsComponent implements ControlValueAccessor {
renderer: this.renderer,
componentType: ShapeFillImageSettingsPanelComponent,
hostView: this.viewContainerRef,
- preferredPlacement: 'left',
+ preferredPlacement: 'leftOnly',
context: {
shapeFillImageSettings: this.modelValue,
},
- isModal: true
+ isModal: true,
+ overlayStyle: {padding: '10px'}
}).tbComponentRef.instance.shapeFillImageSettingsApplied.subscribe((shapeFillImageSettings) => {
this.modelValue = shapeFillImageSettings;
this.propagateChange(this.modelValue);
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.scss
index 8579f2a872..4d1c34e131 100644
--- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.scss
+++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.scss
@@ -108,7 +108,7 @@
align-items: center;
}
- > .mat-expansion-panel-content {
+ .mat-expansion-panel-content {
> .mat-expansion-panel-body {
padding: 0;
}
diff --git a/ui-ngx/src/app/modules/home/models/datasource/attribute-datasource.ts b/ui-ngx/src/app/modules/home/models/datasource/attribute-datasource.ts
index eaeade89ae..cc3112442f 100644
--- a/ui-ngx/src/app/modules/home/models/datasource/attribute-datasource.ts
+++ b/ui-ngx/src/app/modules/home/models/datasource/attribute-datasource.ts
@@ -115,9 +115,11 @@ export class AttributeDatasource implements DataSource {
}
isAllSelected(): Observable {
- const numSelected = this.selection.selected.length;
return this.attributesSubject.pipe(
- map((attributes) => numSelected === attributes.length)
+ map((attributes) => {
+ const numSelected = this.selection.selected.length;
+ return attributes.length > 0 && numSelected === attributes.length
+ })
);
}
diff --git a/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts b/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts
index 913e754fb7..336ac035da 100644
--- a/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts
+++ b/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts
@@ -175,7 +175,7 @@ export class EntityTableConfig, P extends PageLink = P
selectionEnabled = true;
searchEnabled = true;
addEnabled = true;
- addAsTextButton = false;
+ addAsTextButton = true;
entitiesDeleteEnabled = true;
detailsPanelEnabled = true;
hideDetailsTabsOnEdit = true;
diff --git a/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.html b/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.html
index 76d255f40e..2ba7adda3b 100644
--- a/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.html
+++ b/ui-ngx/src/app/modules/home/pages/admin/mail-server.component.html
@@ -280,7 +280,7 @@
-
+
admin.oauth2.redirect-uri-template
diff --git a/ui-ngx/src/app/modules/home/pages/ai-model/ai-model-table-config.resolve.ts b/ui-ngx/src/app/modules/home/pages/ai-model/ai-model-table-config.resolve.ts
index 2a73488d7e..7c78bd7c02 100644
--- a/ui-ngx/src/app/modules/home/pages/ai-model/ai-model-table-config.resolve.ts
+++ b/ui-ngx/src/app/modules/home/pages/ai-model/ai-model-table-config.resolve.ts
@@ -47,7 +47,6 @@ export class AiModelsTableConfigResolver {
) {
this.config.selectionEnabled = true;
this.config.entityType = EntityType.AI_MODEL;
- this.config.addAsTextButton = true;
this.config.rowPointer = true;
this.config.detailsPanelEnabled = false;
this.config.entityTranslations = entityTypeTranslations.get(EntityType.AI_MODEL);
diff --git a/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-table-config.resolver.ts
index 13698ac4a5..274edf5933 100644
--- a/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-table-config.resolver.ts
+++ b/ui-ngx/src/app/modules/home/pages/mobile/applications/mobile-app-table-config.resolver.ts
@@ -57,7 +57,6 @@ export class MobileAppTableConfigResolver {
) {
this.config.selectionEnabled = false;
this.config.entityType = EntityType.MOBILE_APP;
- this.config.addAsTextButton = true;
this.config.entitiesDeleteEnabled = false;
this.config.rowPointer = true;
this.config.entityTranslations = entityTypeTranslations.get(EntityType.MOBILE_APP);
diff --git a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-table-config.resolve.ts b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-table-config.resolve.ts
index 69be801980..ba20624852 100644
--- a/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-table-config.resolve.ts
+++ b/ui-ngx/src/app/modules/home/pages/mobile/bundes/mobile-bundle-table-config.resolve.ts
@@ -63,7 +63,6 @@ export class MobileBundleTableConfigResolver {
) {
this.config.selectionEnabled = false;
this.config.entityType = EntityType.MOBILE_APP_BUNDLE;
- this.config.addAsTextButton = true;
this.config.rowPointer = true;
this.config.detailsPanelEnabled = false;
this.config.entityTranslations = entityTypeTranslations.get(EntityType.MOBILE_APP_BUNDLE);
diff --git a/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts b/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts
index 9f2e850b53..dbc1a5d9c8 100644
--- a/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts
+++ b/ui-ngx/src/app/modules/home/pages/mobile/common/editor-panel.component.ts
@@ -48,7 +48,7 @@ export class EditorPanelComponent implements OnInit {
tinyMceOptions: Partial = {
base_url: '/assets/tinymce',
suffix: '.min',
- plugins: ['link', 'table', 'image', 'imagetools', 'lists', 'fullscreen'],
+ plugins: ['link', 'table', 'image', 'lists', 'fullscreen'],
menubar: 'edit insert view format',
toolbar: ['fontfamily fontsize | bold italic underline strikethrough forecolor backcolor',
'alignleft aligncenter alignright alignjustify | bullist | link table image | fullscreen'],
diff --git a/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-table-config.resolver.ts
index c2fc08fb7c..8e729e94ef 100644
--- a/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-table-config.resolver.ts
+++ b/ui-ngx/src/app/modules/home/pages/notification/recipient/recipient-table-config.resolver.ts
@@ -48,7 +48,6 @@ export class RecipientTableConfigResolver {
this.config.entityType = EntityType.NOTIFICATION_TARGET;
this.config.detailsPanelEnabled = false;
- this.config.addAsTextButton = true;
this.config.rowPointer = true;
this.config.entityTranslations = entityTypeTranslations.get(EntityType.NOTIFICATION_TARGET);
diff --git a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-table-config.resolver.ts
index 066db92c0a..f71197ae95 100644
--- a/ui-ngx/src/app/modules/home/pages/notification/rule/rule-table-config.resolver.ts
+++ b/ui-ngx/src/app/modules/home/pages/notification/rule/rule-table-config.resolver.ts
@@ -49,7 +49,6 @@ export class RuleTableConfigResolver {
this.config.entityType = EntityType.NOTIFICATION_RULE;
this.config.detailsPanelEnabled = false;
- this.config.addAsTextButton = true;
this.config.rowPointer = true;
this.config.entityTranslations = entityTypeTranslations.get(EntityType.NOTIFICATION_RULE);
diff --git a/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.scss b/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.scss
index 34bff387e6..de9c0a06a7 100644
--- a/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.scss
+++ b/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.scss
@@ -65,7 +65,7 @@
:host ::ng-deep {
.tb-form-panel .mat-expansion-panel.tb-settings {
- & > .mat-expansion-panel-content > .mat-expansion-panel-body {
+ .mat-expansion-panel-content > .mat-expansion-panel-body {
gap: 0;
}
}
diff --git a/ui-ngx/src/app/modules/home/pages/notification/template/template-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/notification/template/template-table-config.resolver.ts
index 0ee0b921f4..b1bc3eef3e 100644
--- a/ui-ngx/src/app/modules/home/pages/notification/template/template-table-config.resolver.ts
+++ b/ui-ngx/src/app/modules/home/pages/notification/template/template-table-config.resolver.ts
@@ -47,7 +47,6 @@ export class TemplateTableConfigResolver {
this.config.entityType = EntityType.NOTIFICATION_TEMPLATE;
this.config.detailsPanelEnabled = false;
- this.config.addAsTextButton = true;
this.config.rowPointer = true;
this.config.entityTranslations = entityTypeTranslations.get(EntityType.NOTIFICATION_TEMPLATE);
diff --git a/ui-ngx/src/app/modules/login/pages/login/login.component.ts b/ui-ngx/src/app/modules/login/pages/login/login.component.ts
index 1cf115f9dc..a10e213be5 100644
--- a/ui-ngx/src/app/modules/login/pages/login/login.component.ts
+++ b/ui-ngx/src/app/modules/login/pages/login/login.component.ts
@@ -76,7 +76,7 @@ export class LoginComponent extends PageComponent implements OnInit {
getOAuth2Uri(oauth2Client: OAuth2ClientLoginInfo): string {
let result = "";
if (this.authService.redirectUrl) {
- result += "?prevUri=" + this.authService.redirectUrl;
+ result += "?prevUri=" + encodeURIComponent(this.authService.redirectUrl);
}
return oauth2Client.url + result;
}
diff --git a/ui-ngx/src/app/shared/components/color-picker/color-input.base.scss b/ui-ngx/src/app/shared/components/color-picker/color-input.base.scss
new file mode 100644
index 0000000000..bae2e433aa
--- /dev/null
+++ b/ui-ngx/src/app/shared/components/color-picker/color-input.base.scss
@@ -0,0 +1,28 @@
+/**
+ * Copyright © 2016-2026 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.
+ */
+:host {
+ .color-input-container {
+ display: flex;
+ gap: 4px;
+ align-items: center;
+ }
+ .color-input {
+ max-width: 72px;
+ margin-bottom: 4px;
+ }
+}
+
+
diff --git a/ui-ngx/src/app/shared/components/color-picker/color-picker-panel.component.scss b/ui-ngx/src/app/shared/components/color-picker/color-picker-panel.component.scss
index f9db566be8..5e867f9e61 100644
--- a/ui-ngx/src/app/shared/components/color-picker/color-picker-panel.component.scss
+++ b/ui-ngx/src/app/shared/components/color-picker/color-picker-panel.component.scss
@@ -16,7 +16,7 @@
@import "../scss/constants";
.tb-color-picker-panel {
- width: 342px;
+ width: 370px;
display: flex;
flex-direction: column;
max-height: calc(100vh - 24px);
diff --git a/ui-ngx/src/app/shared/components/color-picker/color-picker.component.html b/ui-ngx/src/app/shared/components/color-picker/color-picker.component.html
index 676dafbfef..fcb1cb067d 100644
--- a/ui-ngx/src/app/shared/components/color-picker/color-picker.component.html
+++ b/ui-ngx/src/app/shared/components/color-picker/color-picker.component.html
@@ -37,10 +37,8 @@
HSLA
-
-
+
+
diff --git a/ui-ngx/src/app/shared/components/color-picker/color-picker.component.scss b/ui-ngx/src/app/shared/components/color-picker/color-picker.component.scss
index ed161a5b9f..799bcdf00c 100644
--- a/ui-ngx/src/app/shared/components/color-picker/color-picker.component.scss
+++ b/ui-ngx/src/app/shared/components/color-picker/color-picker.component.scss
@@ -83,7 +83,7 @@
height: 56px;
display: flex;
align-items: center;
- gap: 20px;
+ gap: 8px;
.presentation-select {
font-size: 14px;
@@ -104,7 +104,7 @@
display: flex;
flex-direction: row;
flex-wrap: wrap;
- justify-content: space-between;
+ justify-content: center;
gap: 8px;
@media #{$mat-xs} {
flex-direction: column;
diff --git a/ui-ngx/src/app/shared/components/color-picker/hex-input.component.scss b/ui-ngx/src/app/shared/components/color-picker/hex-input.component.scss
index 2f7a404c0d..73a6aff9f6 100644
--- a/ui-ngx/src/app/shared/components/color-picker/hex-input.component.scss
+++ b/ui-ngx/src/app/shared/components/color-picker/hex-input.component.scss
@@ -19,11 +19,11 @@
gap: 8px;
}
.hex-input {
- max-width: 190px;
+ max-width: 220px;
}
.alpha-input {
- min-width: 60px;
- max-width: 60px;
+ min-width: 72px;
+ max-width: 72px;
}
::ng-deep {
diff --git a/ui-ngx/src/app/shared/components/color-picker/hsla-input.component.html b/ui-ngx/src/app/shared/components/color-picker/hsla-input.component.html
new file mode 100644
index 0000000000..cf8e25eb51
--- /dev/null
+++ b/ui-ngx/src/app/shared/components/color-picker/hsla-input.component.html
@@ -0,0 +1,54 @@
+
+
diff --git a/ui-ngx/src/app/shared/components/color-picker/hsla-input.component.ts b/ui-ngx/src/app/shared/components/color-picker/hsla-input.component.ts
new file mode 100644
index 0000000000..40ec056972
--- /dev/null
+++ b/ui-ngx/src/app/shared/components/color-picker/hsla-input.component.ts
@@ -0,0 +1,72 @@
+///
+/// Copyright © 2016-2026 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, EventEmitter, Input, Output } from '@angular/core';
+import { Color } from '@iplab/ngx-color-picker';
+import { coerceBoolean } from '@shared/decorators/coercion';
+
+type Channel = 'H' | 'S' | 'L';
+
+@Component({
+ selector: 'tb-hsla-input',
+ templateUrl: './hsla-input.component.html',
+ styleUrl: './color-input.base.scss',
+ standalone: false
+})
+export class HslaInputComponent {
+
+ @Input()
+ public color: Color;
+
+ @Output()
+ public colorChange = new EventEmitter
(false);
+
+ @Input()
+ @coerceBoolean()
+ public labelVisible = false;
+
+ @Input()
+ public suffixValue = '%';
+
+ public get value() {
+ return this.color.getHsla();
+ }
+
+ public get alphaValue(): number {
+ return this.color ? Math.round(this.color.getHsla().getAlpha() * 100) : 0;
+ }
+
+ public onAlphaInputChange(inputValue: number): void {
+ if (!this.color) return;
+ const hsla = this.color.getHsla();
+ const alpha = +inputValue / 100;
+ if (hsla.alpha !== alpha) {
+ const newColor = new Color().setHsla(hsla.getHue(), hsla.getSaturation(), hsla.getLightness(), alpha);
+ this.colorChange.emit(newColor);
+ }
+ }
+
+ public onInputChange(newValue: number, channel: Channel): void {
+ if (!this.color) return;
+ const hsla = this.value;
+ const hue = channel === 'H' ? +newValue : hsla.getHue();
+ const saturation = channel === 'S' ? +newValue : hsla.getSaturation();
+ const lightness = channel === 'L' ? +newValue : hsla.getLightness();
+ if (hue === hsla.getHue() && saturation === hsla.getSaturation() && lightness === hsla.getLightness()) return;
+ const newColor = new Color().setHsla(hue, saturation, lightness, hsla.getAlpha());
+ this.colorChange.emit(newColor);
+ }
+}
diff --git a/ui-ngx/src/app/shared/components/color-picker/input-change.directive.ts b/ui-ngx/src/app/shared/components/color-picker/input-change.directive.ts
new file mode 100644
index 0000000000..55604df28e
--- /dev/null
+++ b/ui-ngx/src/app/shared/components/color-picker/input-change.directive.ts
@@ -0,0 +1,46 @@
+///
+/// Copyright © 2016-2026 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 { Directive, EventEmitter, HostBinding, HostListener, Input, numberAttribute, Output } from '@angular/core';
+
+@Directive({
+ selector: '[inputChange]',
+ standalone: false
+})
+export class InputChangeDirective {
+
+ @Input({transform: numberAttribute})
+ @HostBinding('attr.min')
+ min = 0;
+
+ @Input({transform: numberAttribute})
+ @HostBinding('attr.max')
+ max = 255;
+
+ @Output()
+ public inputChange = new EventEmitter();
+
+ @HostListener('input', ['$event'])
+ public inputChanges(event: any): void {
+ const element = event.target as HTMLInputElement || event.srcElement as HTMLInputElement;
+ const value = element.value;
+
+ const numeric = parseFloat(value);
+ if (!isNaN(numeric) && numeric >= this.min && numeric <= this.max) {
+ this.inputChange.emit(numeric);
+ }
+ }
+}
diff --git a/ui-ngx/src/app/shared/components/color-picker/rgba-input.component.html b/ui-ngx/src/app/shared/components/color-picker/rgba-input.component.html
new file mode 100644
index 0000000000..3cb856f147
--- /dev/null
+++ b/ui-ngx/src/app/shared/components/color-picker/rgba-input.component.html
@@ -0,0 +1,52 @@
+
+
diff --git a/ui-ngx/src/app/shared/components/color-picker/rgba-input.component.ts b/ui-ngx/src/app/shared/components/color-picker/rgba-input.component.ts
new file mode 100644
index 0000000000..04f2dbc44b
--- /dev/null
+++ b/ui-ngx/src/app/shared/components/color-picker/rgba-input.component.ts
@@ -0,0 +1,71 @@
+///
+/// Copyright © 2016-2026 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, EventEmitter, Input, Output } from '@angular/core';
+import { Color } from '@iplab/ngx-color-picker';
+import { coerceBoolean } from '@shared/decorators/coercion';
+
+type Channel = 'R' | 'G' | 'B';
+
+@Component({
+ selector: 'tb-rgba-input',
+ templateUrl: './rgba-input.component.html',
+ styleUrl: './color-input.base.scss',
+ standalone: false
+})
+export class RgbaInputComponent {
+
+ @Input()
+ public color: Color;
+
+ @Output()
+ public colorChange = new EventEmitter(false);
+
+ @Input()
+ @coerceBoolean()
+ public labelVisible = false;
+
+ @Input()
+ public suffixValue = '%';
+
+ public get value() {
+ return this.color.getRgba();
+ }
+
+ public get alphaValue(): string {
+ return this.color ? Math.round(this.color.getRgba().getAlpha() * 100).toString() : '';
+ }
+
+ public onAlphaInputChange(inputValue: number): void {
+ if (!this.color) return;
+ const color = this.color.getRgba();
+ const alpha = +inputValue / 100;
+ if (color.getAlpha() !== alpha) {
+ const newColor = new Color().setRgba(color.getRed(), color.getGreen(), color.getBlue(), alpha).toRgbaString();
+ this.colorChange.emit(new Color(newColor));
+ }
+ }
+
+ onInputChange(newValue: number, channel: Channel) {
+ if (!this.color) return;
+ const rgba = this.value;
+ const red = channel === 'R' ? newValue : rgba.getRed();
+ const green = channel === 'G' ? newValue : rgba.getGreen();
+ const blue = channel === 'B' ? newValue : rgba.getBlue();
+ if (red === rgba.getRed() && green === rgba.getGreen() && blue === rgba.getBlue()) return;
+ this.colorChange.emit(new Color().setRgba(red, green, blue, rgba.alpha));
+ }
+}
diff --git a/ui-ngx/src/app/shared/components/dialog/dynamic/dynamic-dialog.module.ts b/ui-ngx/src/app/shared/components/dialog/dynamic/dynamic-dialog.module.ts
index c1defae012..078234dbda 100644
--- a/ui-ngx/src/app/shared/components/dialog/dynamic/dynamic-dialog.module.ts
+++ b/ui-ngx/src/app/shared/components/dialog/dynamic/dynamic-dialog.module.ts
@@ -14,9 +14,9 @@
/// limitations under the License.
///
-import { OverlayModule } from '@angular/cdk/overlay';
+import { Overlay, OverlayContainer, OverlayModule } from '@angular/cdk/overlay';
import { NgModule } from '@angular/core';
-import { DEFAULT_DIALOG_CONFIG, DialogConfig, DialogModule } from '@angular/cdk/dialog';
+import { DEFAULT_DIALOG_CONFIG, Dialog, DialogConfig, DialogModule } from '@angular/cdk/dialog';
import { MatDialogModule } from '@angular/material/dialog';
import { DynamicDialog, DynamicMatDialog } from './dynamic-dialog';
import { DynamicOverlay } from './dynamic-overlay';
@@ -24,8 +24,11 @@ import { DynamicOverlayContainer } from './dynamic-overlay-container';
export const DYNAMIC_MAT_DIALOG_PROVIDERS = [
DynamicOverlayContainer,
+ { provide: OverlayContainer, useExisting: DynamicOverlayContainer },
DynamicOverlay,
+ { provide: Overlay, useExisting: DynamicOverlay },
DynamicDialog,
+ { provide: Dialog, useExisting: DynamicDialog },
DynamicMatDialog,
{
provide: DEFAULT_DIALOG_CONFIG,
diff --git a/ui-ngx/src/app/shared/components/dialog/dynamic/dynamic-dialog.ts b/ui-ngx/src/app/shared/components/dialog/dynamic/dynamic-dialog.ts
index 2fdc63f381..f717ccb0a7 100644
--- a/ui-ngx/src/app/shared/components/dialog/dynamic/dynamic-dialog.ts
+++ b/ui-ngx/src/app/shared/components/dialog/dynamic/dynamic-dialog.ts
@@ -14,18 +14,11 @@
/// limitations under the License.
///
-import { Location } from "@angular/common";
-import { Inject, Injectable, Injector, Optional, SkipSelf, TemplateRef } from '@angular/core';
-import {
- MAT_DIALOG_DEFAULT_OPTIONS,
- MAT_DIALOG_SCROLL_STRATEGY,
- MatDialog,
- MatDialogConfig, MatDialogRef
-} from '@angular/material/dialog';
-import { DynamicOverlay } from "./dynamic-overlay";
-import { DynamicOverlayContainer } from '@shared/components/dialog/dynamic/dynamic-overlay-container';
-import { ComponentType, ScrollStrategy } from '@angular/cdk/overlay';
-import { DEFAULT_DIALOG_CONFIG, Dialog, DialogConfig } from '@angular/cdk/dialog';
+import { inject, Injectable, TemplateRef } from '@angular/core';
+import { MatDialog, MatDialogConfig, MatDialogRef } from '@angular/material/dialog';
+import { DynamicOverlay } from './dynamic-overlay';
+import { ComponentType } from '@angular/cdk/overlay';
+import { Dialog } from '@angular/cdk/dialog';
export interface DynamicMatDialogConfig extends MatDialogConfig {
containerElement?: HTMLElement;
@@ -34,25 +27,12 @@ export interface DynamicMatDialogConfig extends MatDialogConfig {
@Injectable()
export class DynamicMatDialog extends MatDialog {
- private _customOverlay: DynamicOverlay;
+ private _customOverlay = inject(DynamicOverlay);
- constructor( _overlay: DynamicOverlay,
- _injector: Injector,
- @Optional() location: Location,
- @Inject( MAT_DIALOG_DEFAULT_OPTIONS ) _defaultOptions: MatDialogConfig,
- @Inject( MAT_DIALOG_SCROLL_STRATEGY ) _scrollStrategy: ScrollStrategy,
- @Optional() @SkipSelf() _parentDialog:DynamicMatDialog,
- _overlayContainer: DynamicOverlayContainer) {
-
- super( _overlay, _injector, location, _defaultOptions, _scrollStrategy, _parentDialog, _overlayContainer );
- this._dialog = _injector.get(DynamicDialog);
- this._customOverlay = _overlay;
- }
-
- public open(component: ComponentType