diff --git a/.betterer.results b/.betterer.results index cdce8379..d3985a01 100644 --- a/.betterer.results +++ b/.betterer.results @@ -89,31 +89,6 @@ exports[`strictNullChecks`] = { "src/app/core/components/widget-embedded/widget-embedded.component.ts:3928020917": [ [114, 4, 13, "Type \'Type | undefined\' is not assignable to type \'Type\'.\\n Type \'undefined\' is not assignable to type \'Type\'.", "2689238020"] ], - "src/app/core/components/widget-history-chart-dialog/widget-history-chart-dialog.component.ts:4108070123": [ - [221, 12, 1, "Type \'number | null\' is not assignable to type \'number\'.\\n Type \'null\' is not assignable to type \'number\'.", "177628"], - [284, 6, 12, "Object is possibly \'null\'.", "765689629"], - [285, 6, 12, "Object is possibly \'null\'.", "765689629"], - [286, 6, 12, "Object is possibly \'null\'.", "765689629"], - [287, 6, 12, "Object is possibly \'null\'.", "765689629"], - [288, 6, 12, "Object is possibly \'null\'.", "765689629"], - [289, 6, 12, "Object is possibly \'null\'.", "765689629"], - [290, 6, 12, "Object is possibly \'null\'.", "765689629"], - [291, 6, 12, "Object is possibly \'null\'.", "765689629"], - [305, 23, 12, "Object is possibly \'null\'.", "765689629"], - [306, 56, 6, "Property \'length\' does not exist on type \'never\'.", "1433765721"], - [418, 4, 6, "Type \'({ seriesId: string; datasetUuid: string; path: string; expansionMode: null; familyKey: null; allowedIds: null; trackedDevices?: null | undefined; ownerWidgetUuid: string; ownerWidgetSelector: string | null; ... 8 more ...; reconcileTs?: number | undefined; } | { ...; })[]\' is not assignable to type \'IKipSeriesDefinition[]\'.\\n Type \'{ seriesId: string; datasetUuid: string; path: string; expansionMode: null; familyKey: null; allowedIds: null; trackedDevices?: null | undefined; ownerWidgetUuid: string; ownerWidgetSelector: string | null; ... 8 more ...; reconcileTs?: number | undefined; } | { ...; }\' is not assignable to type \'IKipSeriesDefinition\'.\\n Type \'{ seriesId: string; datasetUuid: string; path: string; expansionMode: null; familyKey: null; allowedIds: null; ownerWidgetSelector: \\"widget-bms\\" | \\"widget-solar-charger\\" | \\"widget-charger\\" | \\"widget-inverter\\" | \\"widget-alternator\\" | \\"widget-ac\\"; ... 10 more ...; reconcileTs?: number; }\' is not assignable to type \'IKipSeriesDefinition\'.\\n Type \'{ seriesId: string; datasetUuid: string; path: string; expansionMode: null; familyKey: null; allowedIds: null; ownerWidgetSelector: \\"widget-bms\\" | \\"widget-solar-charger\\" | \\"widget-charger\\" | \\"widget-inverter\\" | \\"widget-alternator\\" | \\"widget-ac\\"; ... 10 more ...; reconcileTs?: number; }\' is not assignable to type \'IKipConcreteSeriesDefinition\'.\\n Types of property \'trackedDevices\' are incompatible.\\n Type \'readonly IElectricalTrackedDeviceRef[] | null | undefined\' is not assignable to type \'null | undefined\'.\\n Type \'readonly IElectricalTrackedDeviceRef[]\' is not assignable to type \'null | undefined\'.", "2123913871"], - [560, 21, 12, "Object is possibly \'null\'.", "765689629"], - [563, 21, 12, "Object is possibly \'null\'.", "765689629"], - [571, 21, 12, "Object is possibly \'null\'.", "765689629"], - [590, 59, 12, "Object is possibly \'null\'.", "765689629"], - [643, 19, 12, "Object is possibly \'null\'.", "765689629"], - [646, 19, 12, "Object is possibly \'null\'.", "765689629"], - [651, 19, 12, "Object is possibly \'null\'.", "765689629"], - [664, 10, 12, "Object is possibly \'null\'.", "765689629"], - [672, 17, 12, "Object is possibly \'null\'.", "765689629"], - [676, 17, 12, "Object is possibly \'null\'.", "765689629"], - [705, 4, 6, "Type \'{ fillStyle: Color; lineDash: _DeepPartialArray; text: string; borderRadius?: number | BorderRadius | undefined; datasetIndex?: number | undefined; ... 10 more ...; textAlign?: TextAlign | undefined; }[]\' is not assignable to type \'LegendItem[]\'.\\n Type \'{ fillStyle: Color; lineDash: _DeepPartialArray; text: string; borderRadius?: number | BorderRadius; datasetIndex?: number; ... 10 more ...; textAlign?: TextAlign; }\' is not assignable to type \'LegendItem\'.\\n Types of property \'lineDash\' are incompatible.\\n Type \'_DeepPartialArray\' is not assignable to type \'number[]\'.\\n Type \'number | undefined\' is not assignable to type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "2123913871"] - ], "src/app/core/components/widget-host2/widget-host2.component.ts:867588761": [ [137, 4, 13, "Type \'Type | undefined\' is not assignable to type \'Type\'.\\n Type \'undefined\' is not assignable to type \'Type\'.", "2689238020"], [253, 32, 9, "Type \'null\' cannot be used as an index type.", "2171020380"] @@ -181,41 +156,6 @@ exports[`strictNullChecks`] = { [156, 4, 20, "Object is possibly \'undefined\'.", "2522653710"], [157, 4, 20, "Object is possibly \'undefined\'.", "2522653710"] ], - "src/app/widgets/minichart/minichart.component.ts:765110486": [ - [43, 9, 5, "Type \'null\' is not assignable to type \'string\'.", "176948952"], - [44, 9, 8, "Type \'null\' is not assignable to type \'string\'.", "806402680"], - [45, 9, 10, "Type \'null\' is not assignable to type \'string\'.", "1967398792"], - [46, 9, 13, "Type \'null\' is not assignable to type \'string\'.", "2466344431"], - [47, 9, 10, "Type \'null\' is not assignable to type \'number\'.", "2101602424"], - [48, 9, 9, "Type \'null\' is not assignable to type \'number\'.", "212862030"], - [49, 9, 9, "Type \'null\' is not assignable to type \'number\'.", "212862288"], - [77, 10, 9, "Type \'null\' is not assignable to type \'Subscription\'.", "4113745821"], - [78, 10, 14, "Type \'null\' is not assignable to type \'IChartDataSourceInfo\'.", "2985720646"], - [152, 4, 21, "Object is possibly \'undefined\'.", "406043295"], - [153, 4, 21, "Object is possibly \'undefined\'.", "406043295"], - [155, 4, 21, "Object is possibly \'undefined\'.", "406043295"], - [158, 6, 21, "Object is possibly \'undefined\'.", "406043295"], - [162, 10, 12, "Type \'number | null\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "3614877034"], - [163, 10, 12, "Type \'number | null\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "3614877300"], - [164, 10, 3, "Type \'number | null\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "193413807"], - [165, 10, 3, "Type \'number | null\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "193414065"], - [219, 6, 21, "Object is possibly \'undefined\'.", "406043295"], - [254, 10, 12, "Type \'number | null\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "3614877034"], - [255, 10, 12, "Type \'number | null\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "3614877300"], - [256, 10, 3, "Type \'number | null\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "193413807"], - [257, 10, 3, "Type \'number | null\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "193414065"], - [281, 4, 21, "Object is possibly \'undefined\'.", "406043295"], - [294, 26, 21, "Object is possibly \'undefined\'.", "406043295"], - [339, 6, 9, "Type \'null\' is not assignable to type \'string\'.", "384690336"], - [340, 6, 9, "Type \'null\' is not assignable to type \'string\'.", "384749729"], - [341, 6, 11, "Type \'null\' is not assignable to type \'string\'.", "936435848"], - [342, 6, 11, "Type \'null\' is not assignable to type \'string\'.", "935954057"], - [343, 6, 16, "Type \'null\' is not assignable to type \'string\'.", "505309252"], - [344, 6, 10, "Type \'null\' is not assignable to type \'string\'.", "1290121711"], - [345, 6, 10, "Type \'null\' is not assignable to type \'string\'.", "1316340194"], - [546, 4, 14, "Type \'null\' is not assignable to type \'Subscription\'.", "2516429621"], - [558, 4, 6, "Type \'({ x: number | null; y: number; } | { x: number; y: number | null; })[]\' is not assignable to type \'IDataSetRow[]\'.\\n Type \'{ x: number | null; y: number; } | { x: number; y: number | null; }\' is not assignable to type \'IDataSetRow\'.\\n Type \'{ x: number | null; y: number; }\' is not assignable to type \'IDataSetRow\'.\\n Types of property \'x\' are incompatible.\\n Type \'number | null\' is not assignable to type \'number\'.\\n Type \'null\' is not assignable to type \'number\'.", "2123913871"] - ], "src/app/widgets/svg-racesteer/svg-racesteer.component.ts:3776013780": [ [36, 46, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'number\'.", "2620553983"], [37, 47, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'number\'.", "2620553983"], @@ -261,243 +201,10 @@ exports[`strictNullChecks`] = { [1353, 21, 11, "\'b.longitude\' is possibly \'undefined\'.", "3370058570"], [1353, 35, 11, "\'a.longitude\' is possibly \'undefined\'.", "2985066057"] ], - "src/app/widgets/widget-autopilot/widget-autopilot.component.ts:2942713930": [ - [275, 6, 10, "Type \'null\' is not assignable to type \'\\"v1\\" | \\"v2\\"\'.", "2208951047"], - [324, 23, 22, "Object is possibly \'undefined\'.", "576852750"], - [324, 23, 32, "Object is possibly \'undefined\'.", "525756929"], - [336, 23, 22, "Object is possibly \'undefined\'.", "576852750"], - [336, 23, 32, "Object is possibly \'undefined\'.", "525756929"], - [341, 47, 5, "Argument of type \'string | null\' is not assignable to parameter of type \'string\'.\\n Type \'null\' is not assignable to type \'string\'.", "195031250"], - [351, 23, 22, "Object is possibly \'undefined\'.", "576852750"], - [351, 23, 32, "Object is possibly \'undefined\'.", "525756929"], - [363, 77, 4, "Argument of type \'string | null\' is not assignable to parameter of type \'string\'.\\n Type \'null\' is not assignable to type \'string\'.", "2087698470"], - [370, 77, 4, "Argument of type \'string | null\' is not assignable to parameter of type \'string\'.\\n Type \'null\' is not assignable to type \'string\'.", "2087698470"], - [401, 23, 22, "Object is possibly \'undefined\'.", "576852750"], - [401, 23, 32, "Object is possibly \'undefined\'.", "525756929"], - [413, 52, 4, "Argument of type \'string | null\' is not assignable to parameter of type \'string\'.\\n Type \'null\' is not assignable to type \'string\'.", "2087698470"], - [421, 52, 4, "Argument of type \'string | null\' is not assignable to parameter of type \'string\'.\\n Type \'null\' is not assignable to type \'string\'.", "2087698470"], - [612, 23, 22, "Object is possibly \'undefined\'.", "576852750"], - [612, 23, 32, "Object is possibly \'undefined\'.", "525756929"], - [613, 23, 22, "Object is possibly \'undefined\'.", "576852750"], - [613, 23, 32, "Object is possibly \'undefined\'.", "525756929"], - [614, 21, 22, "Object is possibly \'undefined\'.", "576852750"], - [614, 21, 32, "Object is possibly \'undefined\'.", "525756929"], - [674, 18, 22, "Object is possibly \'undefined\'.", "576852750"], - [674, 18, 32, "Object is possibly \'undefined\'.", "525756929"], - [688, 4, 12, "Object is possibly \'null\'.", "4261782498"], - [696, 4, 12, "Object is possibly \'null\'.", "4261782498"], - [704, 4, 12, "Object is possibly \'null\'.", "4261782498"], - [712, 4, 12, "Object is possibly \'null\'.", "4261782498"], - [726, 4, 22, "Object is possibly \'undefined\'.", "576852750"], - [726, 4, 28, "Object is possibly \'undefined\'.", "3500113022"], - [727, 4, 12, "Object is possibly \'null\'.", "4261782498"], - [735, 4, 12, "Object is possibly \'null\'.", "4261782498"], - [736, 4, 12, "Object is possibly \'null\'.", "4261782498"], - [747, 4, 12, "Object is possibly \'null\'.", "4261782498"], - [748, 4, 12, "Object is possibly \'null\'.", "4261782498"], - [752, 26, 22, "Object is possibly \'undefined\'.", "576852750"], - [752, 26, 32, "Object is possibly \'undefined\'.", "525756929"], - [757, 8, 22, "Object is possibly \'undefined\'.", "576852750"], - [757, 8, 32, "Object is possibly \'undefined\'.", "525756929"], - [758, 6, 12, "Object is possibly \'null\'.", "4261782498"], - [760, 6, 12, "Object is possibly \'null\'.", "4261782498"], - [763, 4, 12, "Object is possibly \'null\'.", "4261782498"], - [801, 10, 22, "Object is possibly \'undefined\'.", "576852750"], - [801, 10, 32, "Object is possibly \'undefined\'.", "525756929"], - [834, 23, 22, "Object is possibly \'undefined\'.", "576852750"], - [834, 23, 32, "Object is possibly \'undefined\'.", "525756929"], - [1016, 10, 8, "Variable \'response\' is used before being assigned.", "2137101542"], - [1020, 104, 8, "Variable \'response\' is used before being assigned.", "2137101542"], - [1021, 15, 8, "Variable \'response\' is used before being assigned.", "2137101542"], - [1059, 8, 22, "Object is possibly \'undefined\'.", "576852750"], - [1059, 8, 32, "Object is possibly \'undefined\'.", "525756929"], - [1072, 8, 22, "Object is possibly \'undefined\'.", "576852750"], - [1072, 8, 32, "Object is possibly \'undefined\'.", "525756929"], - [1081, 8, 22, "Object is possibly \'undefined\'.", "576852750"], - [1081, 8, 32, "Object is possibly \'undefined\'.", "525756929"], - [1133, 17, 31, "No overload matches this call.\\n Overload 1 of 2, \'(timeout: string | number | Timeout | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'string | number | Timeout | undefined\'.\\n Type \'null\' is not assignable to type \'string | number | Timeout | undefined\'.\\n Overload 2 of 2, \'(id: number | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "315042064"], - [1143, 17, 31, "No overload matches this call.\\n Overload 1 of 2, \'(timeout: string | number | Timeout | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'string | number | Timeout | undefined\'.\\n Type \'null\' is not assignable to type \'string | number | Timeout | undefined\'.\\n Overload 2 of 2, \'(id: number | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "315042064"], - [1144, 17, 34, "No overload matches this call.\\n Overload 1 of 2, \'(timeout: string | number | Timeout | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'string | number | Timeout | undefined\'.\\n Type \'null\' is not assignable to type \'string | number | Timeout | undefined\'.\\n Overload 2 of 2, \'(id: number | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "4009237785"], - [1153, 19, 34, "No overload matches this call.\\n Overload 1 of 2, \'(timeout: string | number | Timeout | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'string | number | Timeout | undefined\'.\\n Type \'null\' is not assignable to type \'string | number | Timeout | undefined\'.\\n Overload 2 of 2, \'(id: number | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "4009237785"], - [1161, 21, 34, "No overload matches this call.\\n Overload 1 of 2, \'(timeout: string | number | Timeout | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'string | number | Timeout | undefined\'.\\n Type \'null\' is not assignable to type \'string | number | Timeout | undefined\'.\\n Overload 2 of 2, \'(id: number | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "4009237785"], - [1179, 17, 30, "No overload matches this call.\\n Overload 1 of 2, \'(timeout: string | number | Timeout | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'string | number | Timeout | undefined\'.\\n Type \'null\' is not assignable to type \'string | number | Timeout | undefined\'.\\n Overload 2 of 2, \'(id: number | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "1882595422"], - [1204, 23, 22, "Object is possibly \'undefined\'.", "576852750"], - [1204, 23, 32, "Object is possibly \'undefined\'.", "525756929"], - [1244, 17, 34, "No overload matches this call.\\n Overload 1 of 2, \'(timeout: string | number | Timeout | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'string | number | Timeout | undefined\'.\\n Type \'null\' is not assignable to type \'string | number | Timeout | undefined\'.\\n Overload 2 of 2, \'(id: number | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "4009237785"], - [1245, 17, 31, "No overload matches this call.\\n Overload 1 of 2, \'(timeout: string | number | Timeout | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'string | number | Timeout | undefined\'.\\n Type \'null\' is not assignable to type \'string | number | Timeout | undefined\'.\\n Overload 2 of 2, \'(id: number | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "315042064"], - [1246, 17, 30, "No overload matches this call.\\n Overload 1 of 2, \'(timeout: string | number | Timeout | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'string | number | Timeout | undefined\'.\\n Type \'null\' is not assignable to type \'string | number | Timeout | undefined\'.\\n Overload 2 of 2, \'(id: number | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "1882595422"], - [1247, 17, 25, "No overload matches this call.\\n Overload 1 of 2, \'(timeout: string | number | Timeout | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'string | number | Timeout | undefined\'.\\n Type \'null\' is not assignable to type \'string | number | Timeout | undefined\'.\\n Overload 2 of 2, \'(id: number | undefined): void\', gave the following error.\\n Argument of type \'Timeout | null\' is not assignable to parameter of type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "398879383"] - ], "src/app/widgets/widget-boolean-switch/widget-boolean-switch.component.ts:46644229": [ [76, 38, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"], [99, 10, 12, "Object is possibly \'null\'.", "4261782498"] ], - "src/app/widgets/widget-data-chart/widget-data-chart.component.ts:2822430409": [ - [80, 4, 18, "Type \'null\' is not assignable to type \'number | undefined\'.", "1866818091"], - [81, 4, 18, "Type \'null\' is not assignable to type \'number | undefined\'.", "1866818357"], - [83, 4, 9, "Type \'null\' is not assignable to type \'number | undefined\'.", "212862030"], - [84, 4, 9, "Type \'null\' is not assignable to type \'number | undefined\'.", "212862288"], - [107, 9, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], - [110, 12, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], - [110, 31, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], - [110, 50, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], - [110, 71, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], - [110, 86, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], - [110, 98, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], - [123, 33, 22, "Argument of type \'IWidgetSvcConfig | undefined\' is not assignable to parameter of type \'IWidgetSvcConfig\'.\\n Type \'undefined\' is not assignable to type \'IWidgetSvcConfig\'.", "576852750"], - [137, 10, 22, "Type \'boolean | undefined\' is not assignable to type \'boolean | null\'.\\n Type \'undefined\' is not assignable to type \'boolean | null\'.", "609774233"], - [171, 71, 10, "Argument of type \'number | undefined\' is not assignable to parameter of type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "3461732844"], - [179, 6, 6, "Type \'number | undefined\' is not assignable to type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "1893769376"], - [187, 27, 22, "Object is possibly \'undefined\'.", "1240567228"], - [197, 4, 21, "Object is possibly \'undefined\'.", "406043295"], - [198, 4, 21, "Object is possibly \'undefined\'.", "406043295"], - [199, 4, 21, "Object is possibly \'undefined\'.", "406043295"], - [202, 6, 21, "Object is possibly \'undefined\'.", "406043295"], - [211, 26, 18, "Object is possibly \'null\'.", "3639984181"], - [211, 55, 18, "Object is possibly \'null\'.", "3639984181"], - [216, 18, 18, "Object is possibly \'null\'.", "3639984181"], - [237, 19, 12, "Object is possibly \'null\'.", "765689629"], - [244, 10, 12, "Type \'number | null | undefined\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "3614877034"], - [245, 10, 12, "Type \'number | null | undefined\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "3614877300"], - [246, 10, 3, "Type \'number | null | undefined\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "193413807"], - [247, 10, 3, "Type \'number | null | undefined\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "193414065"], - [266, 19, 12, "Object is possibly \'null\'.", "765689629"], - [271, 6, 21, "Object is possibly \'undefined\'.", "406043295"], - [277, 26, 18, "Object is possibly \'null\'.", "3639984181"], - [277, 55, 18, "Object is possibly \'null\'.", "3639984181"], - [282, 18, 18, "Object is possibly \'null\'.", "3639984181"], - [303, 19, 12, "Object is possibly \'null\'.", "765689629"], - [309, 10, 12, "Type \'number | null | undefined\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "3614877034"], - [310, 10, 12, "Type \'number | null | undefined\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "3614877300"], - [311, 10, 3, "Type \'number | null | undefined\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "193413807"], - [312, 10, 3, "Type \'number | null | undefined\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "193414065"], - [331, 19, 12, "Object is possibly \'null\'.", "765689629"], - [336, 4, 21, "Object is possibly \'undefined\'.", "406043295"], - [365, 8, 11, "Type \'{ minimumLine: { type: \\"line\\"; scaleID: string; display: boolean | undefined; value: null; drawTime: \\"afterDatasetsDraw\\"; label: { display: true; position: \\"start\\"; yAdjust: number; padding: number; color: string; backgroundColor: string; }; }; maximumLine: { ...; }; averageLine: { ...; }; }\' is not assignable to type \'_DeepPartialObject>> | _DeepPartialArray> | undefined\'.\\n Types of property \'minimumLine\' are incompatible.\\n Type \'{ type: \\"line\\"; scaleID: string; display: boolean | undefined; value: null; drawTime: \\"afterDatasetsDraw\\"; label: { display: true; position: \\"start\\"; yAdjust: number; padding: number; color: string; backgroundColor: string; }; }\' is not assignable to type \'_DeepPartialObject<{ type: \\"label\\"; } & LabelAnnotationOptions> | _DeepPartialObject<{ type: \\"ellipse\\"; } & EllipseAnnotationOptions> | ... 4 more ... | undefined\'.\\n Types of property \'value\' are incompatible.\\n Type \'null\' is not assignable to type \'string | number | ((ctx: PartialEventContext, options: AnnotationOptions) => string | number) | undefined\'.", "2422304113"], - [418, 18, 19, "Object is possibly \'null\'.", "1734867054"], - [418, 54, 19, "Object is possibly \'null\'.", "1734867054"], - [419, 15, 19, "Object is possibly \'null\'.", "1734867054"], - [420, 19, 18, "Object is possibly \'null\'.", "3639984181"], - [420, 70, 18, "Object is possibly \'null\'.", "3639984181"], - [420, 122, 18, "Object is possibly \'null\'.", "3639984181"], - [558, 6, 9, "Type \'null\' is not assignable to type \'string\'.", "384690336"], - [559, 6, 9, "Type \'null\' is not assignable to type \'string\'.", "384749729"], - [560, 6, 11, "Type \'null\' is not assignable to type \'string\'.", "936435848"], - [561, 6, 11, "Type \'null\' is not assignable to type \'string\'.", "935954057"], - [562, 6, 16, "Type \'null\' is not assignable to type \'string\'.", "505309252"], - [563, 6, 10, "Type \'null\' is not assignable to type \'string\'.", "1290121711"], - [564, 6, 10, "Type \'null\' is not assignable to type \'string\'.", "1316340194"], - [570, 29, 12, "Object is possibly \'null\'.", "765689629"], - [571, 29, 12, "Object is possibly \'null\'.", "765689629"], - [572, 31, 12, "Object is possibly \'null\'.", "765689629"], - [573, 31, 12, "Object is possibly \'null\'.", "765689629"], - [576, 29, 12, "Object is possibly \'null\'.", "765689629"], - [577, 29, 12, "Object is possibly \'null\'.", "765689629"], - [578, 31, 12, "Object is possibly \'null\'.", "765689629"], - [579, 31, 12, "Object is possibly \'null\'.", "765689629"], - [580, 30, 12, "Object is possibly \'null\'.", "765689629"], - [582, 34, 12, "Object is possibly \'null\'.", "765689629"], - [583, 28, 12, "Object is possibly \'null\'.", "765689629"], - [588, 29, 12, "Object is possibly \'null\'.", "765689629"], - [589, 29, 12, "Object is possibly \'null\'.", "765689629"], - [590, 31, 12, "Object is possibly \'null\'.", "765689629"], - [591, 31, 12, "Object is possibly \'null\'.", "765689629"], - [594, 29, 12, "Object is possibly \'null\'.", "765689629"], - [595, 29, 12, "Object is possibly \'null\'.", "765689629"], - [596, 31, 12, "Object is possibly \'null\'.", "765689629"], - [597, 31, 12, "Object is possibly \'null\'.", "765689629"], - [600, 34, 12, "Object is possibly \'null\'.", "765689629"], - [601, 28, 12, "Object is possibly \'null\'.", "765689629"], - [606, 29, 12, "Object is possibly \'null\'.", "765689629"], - [607, 29, 12, "Object is possibly \'null\'.", "765689629"], - [608, 31, 12, "Object is possibly \'null\'.", "765689629"], - [609, 31, 12, "Object is possibly \'null\'.", "765689629"], - [612, 29, 12, "Object is possibly \'null\'.", "765689629"], - [613, 29, 12, "Object is possibly \'null\'.", "765689629"], - [614, 31, 12, "Object is possibly \'null\'.", "765689629"], - [615, 31, 12, "Object is possibly \'null\'.", "765689629"], - [618, 34, 12, "Object is possibly \'null\'.", "765689629"], - [619, 28, 12, "Object is possibly \'null\'.", "765689629"], - [624, 29, 12, "Object is possibly \'null\'.", "765689629"], - [625, 29, 12, "Object is possibly \'null\'.", "765689629"], - [626, 31, 12, "Object is possibly \'null\'.", "765689629"], - [627, 31, 12, "Object is possibly \'null\'.", "765689629"], - [630, 29, 12, "Object is possibly \'null\'.", "765689629"], - [631, 29, 12, "Object is possibly \'null\'.", "765689629"], - [632, 31, 12, "Object is possibly \'null\'.", "765689629"], - [633, 31, 12, "Object is possibly \'null\'.", "765689629"], - [636, 34, 12, "Object is possibly \'null\'.", "765689629"], - [637, 28, 12, "Object is possibly \'null\'.", "765689629"], - [642, 29, 12, "Object is possibly \'null\'.", "765689629"], - [643, 29, 12, "Object is possibly \'null\'.", "765689629"], - [644, 31, 12, "Object is possibly \'null\'.", "765689629"], - [645, 31, 12, "Object is possibly \'null\'.", "765689629"], - [648, 29, 12, "Object is possibly \'null\'.", "765689629"], - [649, 29, 12, "Object is possibly \'null\'.", "765689629"], - [650, 31, 12, "Object is possibly \'null\'.", "765689629"], - [651, 31, 12, "Object is possibly \'null\'.", "765689629"], - [654, 34, 12, "Object is possibly \'null\'.", "765689629"], - [655, 28, 12, "Object is possibly \'null\'.", "765689629"], - [660, 29, 12, "Object is possibly \'null\'.", "765689629"], - [661, 29, 12, "Object is possibly \'null\'.", "765689629"], - [662, 31, 12, "Object is possibly \'null\'.", "765689629"], - [663, 31, 12, "Object is possibly \'null\'.", "765689629"], - [666, 29, 12, "Object is possibly \'null\'.", "765689629"], - [667, 29, 12, "Object is possibly \'null\'.", "765689629"], - [668, 31, 12, "Object is possibly \'null\'.", "765689629"], - [669, 31, 12, "Object is possibly \'null\'.", "765689629"], - [672, 34, 12, "Object is possibly \'null\'.", "765689629"], - [673, 28, 12, "Object is possibly \'null\'.", "765689629"], - [678, 29, 12, "Object is possibly \'null\'.", "765689629"], - [679, 29, 12, "Object is possibly \'null\'.", "765689629"], - [680, 31, 12, "Object is possibly \'null\'.", "765689629"], - [681, 31, 12, "Object is possibly \'null\'.", "765689629"], - [684, 29, 12, "Object is possibly \'null\'.", "765689629"], - [685, 29, 12, "Object is possibly \'null\'.", "765689629"], - [686, 31, 12, "Object is possibly \'null\'.", "765689629"], - [687, 31, 12, "Object is possibly \'null\'.", "765689629"], - [690, 34, 12, "Object is possibly \'null\'.", "765689629"], - [691, 28, 12, "Object is possibly \'null\'.", "765689629"], - [696, 29, 12, "Object is possibly \'null\'.", "765689629"], - [697, 29, 12, "Object is possibly \'null\'.", "765689629"], - [698, 31, 12, "Object is possibly \'null\'.", "765689629"], - [699, 31, 12, "Object is possibly \'null\'.", "765689629"], - [702, 29, 12, "Object is possibly \'null\'.", "765689629"], - [703, 29, 12, "Object is possibly \'null\'.", "765689629"], - [704, 31, 12, "Object is possibly \'null\'.", "765689629"], - [705, 31, 12, "Object is possibly \'null\'.", "765689629"], - [708, 34, 12, "Object is possibly \'null\'.", "765689629"], - [709, 28, 12, "Object is possibly \'null\'.", "765689629"], - [716, 8, 5, "Type \'null\' is not assignable to type \'string\'.", "173467459"], - [742, 8, 5, "Type \'string | null | undefined\' is not assignable to type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "173467459"], - [761, 66, 10, "Argument of type \'number | undefined\' is not assignable to parameter of type \'number\'.\\n Type \'undefined\' is not assignable to type \'number\'.", "3461732844"], - [762, 18, 4, "\'info\' is possibly \'null\'.", "2087826411"], - [763, 21, 4, "\'info\' is possibly \'null\'.", "2087826411"], - [764, 23, 4, "\'info\' is possibly \'null\'.", "2087826411"], - [804, 59, 17, "Argument of type \'string | null | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "1101890339"], - [806, 6, 26, "Object is possibly \'undefined\'.", "3315132651"], - [806, 6, 32, "Object is possibly \'undefined\'.", "1289477637"], - [806, 49, 14, "\'convertedTrack\' is possibly \'null\'.", "2974017500"], - [809, 56, 17, "Argument of type \'string | null | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "1101890339"], - [810, 56, 17, "Argument of type \'string | null | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "1101890339"], - [811, 56, 17, "Argument of type \'string | null | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "1101890339"], - [818, 6, 21, "Type \'number | null\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "869002562"], - [819, 6, 21, "\'ann.averageLine.label\' is possibly \'undefined\'.", "833584207"], - [819, 41, 11, "\'lastAverage\' is possibly \'null\'.", "3212571500"], - [823, 6, 21, "Type \'number | null\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "3047629975"], - [824, 6, 21, "\'ann.minimumLine.label\' is possibly \'undefined\'.", "3059351706"], - [824, 41, 11, "\'lastMinimum\' is possibly \'null\'.", "622014457"], - [828, 6, 21, "Type \'number | null\' is not assignable to type \'number | undefined\'.\\n Type \'null\' is not assignable to type \'number | undefined\'.", "1387076873"], - [829, 6, 21, "\'ann.maximumLine.label\' is possibly \'undefined\'.", "1375355140"], - [829, 41, 11, "\'lastMaximum\' is possibly \'null\'.", "348562919"], - [835, 67, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], - [835, 67, 17, "Argument of type \'string | null | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "1101890339"], - [836, 26, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], - [837, 19, 3, "\'cfg\' is possibly \'undefined\'.", "193415815"], - [839, 4, 6, "Type \'({ x: number | null; y: number; } | { x: number; y: number | null; })[]\' is not assignable to type \'IDataSetRow[]\'.\\n Type \'{ x: number | null; y: number; } | { x: number; y: number | null; }\' is not assignable to type \'IDataSetRow\'.\\n Type \'{ x: number | null; y: number; }\' is not assignable to type \'IDataSetRow\'.\\n Types of property \'x\' are incompatible.\\n Type \'number | null\' is not assignable to type \'number\'.\\n Type \'null\' is not assignable to type \'number\'.", "2123913871"], - [850, 37, 6, "Type \'undefined\' cannot be used as an index type.", "1320645186"], - [862, 55, 6, "Type \'undefined\' cannot be used as an index type.", "1320645186"] - ], "src/app/widgets/widget-datetime/widget-datetime.component.ts:3033260432": [ [60, 40, 9, "Argument of type \'undefined\' is not assignable to parameter of type \'string\'.", "2620553983"], [61, 10, 10, "Type \'undefined\' is not assignable to type \'string\'.", "4110290259"], @@ -570,108 +277,6 @@ exports[`strictNullChecks`] = { [355, 33, 22, "Object is possibly \'undefined\'.", "576852750"], [355, 33, 46, "Object is possibly \'undefined\'.", "1732614759"] ], - "src/app/widgets/widget-windtrends-chart/widget-windtrends-chart.component.ts:3621756652": [ - [86, 10, 15, "Type \'null\' is not assignable to type \'Subscription\'.", "2233656204"], - [87, 10, 11, "Type \'null\' is not assignable to type \'Subscription\'.", "3429848846"], - [90, 10, 9, "Type \'null\' is not assignable to type \'TimeScaleFormat\'.", "2592770440"], - [91, 10, 14, "Type \'null\' is not assignable to type \'IChartDataSourceInfo\'.", "2985720646"], - [127, 22, 5, "\'theme\' is possibly \'null\'.", "183206964"], - [162, 26, 5, "\'theme\' is possibly \'null\'.", "183206964"], - [170, 24, 5, "\'theme\' is possibly \'null\'.", "183206964"], - [204, 28, 5, "\'theme\' is possibly \'null\'.", "183206964"], - [287, 24, 5, "\'theme\' is possibly \'null\'.", "183206964"], - [292, 26, 5, "\'theme\' is possibly \'null\'.", "183206964"], - [309, 4, 20, "Type \'Signal\' is not assignable to type \'Signal\'.\\n Type \'Signal\' is not assignable to type \'() => BreakpointState\'.\\n Type \'BreakpointState | undefined\' is not assignable to type \'BreakpointState\'.\\n Type \'undefined\' is not assignable to type \'BreakpointState\'.", "1507091964"], - [348, 29, 22, "Object is possibly \'undefined\'.", "1240567228"], - [362, 4, 21, "Object is possibly \'undefined\'.", "406043295"], - [363, 4, 21, "Object is possibly \'undefined\'.", "406043295"], - [365, 4, 21, "Object is possibly \'undefined\'.", "406043295"], - [376, 4, 21, "Object is possibly \'undefined\'.", "406043295"], - [414, 17, 12, "Object is possibly \'null\'.", "765689629"], - [457, 30, 12, "Object is possibly \'null\'.", "765689629"], - [469, 45, 12, "Object is possibly \'null\'.", "765689629"], - [514, 30, 12, "Object is possibly \'null\'.", "765689629"], - [526, 45, 12, "Object is possibly \'null\'.", "765689629"], - [533, 4, 21, "Object is possibly \'undefined\'.", "406043295"], - [595, 8, 11, "Type \'(context: ScriptableContext<\\"line\\">) => CanvasGradient | null\' is not assignable to type \'string | ((ctx: ScriptableContext<\\"line\\">, options: AnyObject) => Color | undefined) | _DeepPartialObject | _DeepPartialObject<...> | readonly (string | ... 2 more ... | undefined)[] | undefined\'.\\n Type \'(context: ScriptableContext<\\"line\\">) => CanvasGradient | null\' is not assignable to type \'(ctx: ScriptableContext<\\"line\\">, options: AnyObject) => Color | undefined\'.\\n Type \'CanvasGradient | null\' is not assignable to type \'Color | undefined\'.\\n Type \'null\' is not assignable to type \'Color | undefined\'.", "354928724"], - [764, 8, 4, "Type \'number\' is not assignable to type \'null\'.", "2088075444"], - [771, 6, 4, "Type \'number\' is not assignable to type \'null\'.", "2088075444"], - [904, 6, 25, "Object is possibly \'undefined\'.", "1481738554"], - [904, 6, 27, "Object is possibly \'undefined\'.", "3000548012"], - [905, 6, 25, "Object is possibly \'undefined\'.", "1481738554"], - [905, 6, 27, "Object is possibly \'undefined\'.", "3000548012"], - [957, 6, 25, "Object is possibly \'undefined\'.", "1481738554"], - [957, 6, 27, "Object is possibly \'undefined\'.", "3000548013"], - [958, 6, 25, "Object is possibly \'undefined\'.", "1481738554"], - [958, 6, 27, "Object is possibly \'undefined\'.", "3000548013"], - [1012, 29, 12, "Object is possibly \'null\'.", "765689629"], - [1013, 34, 12, "Object is possibly \'null\'.", "765689629"], - [1014, 34, 12, "Object is possibly \'null\'.", "765689629"], - [1015, 29, 12, "Object is possibly \'null\'.", "765689629"], - [1035, 6, 9, "Type \'null\' is not assignable to type \'string\'.", "384690336"], - [1036, 6, 9, "Type \'null\' is not assignable to type \'string\'.", "384749729"], - [1037, 6, 11, "Type \'null\' is not assignable to type \'string\'.", "936435848"], - [1038, 6, 11, "Type \'null\' is not assignable to type \'string\'.", "935954057"], - [1039, 6, 16, "Type \'null\' is not assignable to type \'string\'.", "505309252"], - [1040, 6, 10, "Type \'null\' is not assignable to type \'string\'.", "1290121711"], - [1041, 6, 10, "Type \'null\' is not assignable to type \'string\'.", "1316340194"], - [1046, 27, 12, "Object is possibly \'null\'.", "765689629"], - [1047, 27, 12, "Object is possibly \'null\'.", "765689629"], - [1048, 29, 12, "Object is possibly \'null\'.", "765689629"], - [1049, 29, 12, "Object is possibly \'null\'.", "765689629"], - [1050, 28, 12, "Object is possibly \'null\'.", "765689629"], - [1051, 34, 12, "Object is possibly \'null\'.", "765689629"], - [1052, 28, 12, "Object is possibly \'null\'.", "765689629"], - [1055, 27, 12, "Object is possibly \'null\'.", "765689629"], - [1056, 27, 12, "Object is possibly \'null\'.", "765689629"], - [1057, 29, 12, "Object is possibly \'null\'.", "765689629"], - [1058, 29, 12, "Object is possibly \'null\'.", "765689629"], - [1059, 28, 12, "Object is possibly \'null\'.", "765689629"], - [1060, 34, 12, "Object is possibly \'null\'.", "765689629"], - [1061, 28, 12, "Object is possibly \'null\'.", "765689629"], - [1064, 27, 12, "Object is possibly \'null\'.", "765689629"], - [1065, 27, 12, "Object is possibly \'null\'.", "765689629"], - [1066, 29, 12, "Object is possibly \'null\'.", "765689629"], - [1067, 29, 12, "Object is possibly \'null\'.", "765689629"], - [1068, 28, 12, "Object is possibly \'null\'.", "765689629"], - [1069, 34, 12, "Object is possibly \'null\'.", "765689629"], - [1070, 28, 12, "Object is possibly \'null\'.", "765689629"], - [1073, 27, 12, "Object is possibly \'null\'.", "765689629"], - [1074, 27, 12, "Object is possibly \'null\'.", "765689629"], - [1075, 29, 12, "Object is possibly \'null\'.", "765689629"], - [1076, 29, 12, "Object is possibly \'null\'.", "765689629"], - [1077, 28, 12, "Object is possibly \'null\'.", "765689629"], - [1078, 34, 12, "Object is possibly \'null\'.", "765689629"], - [1079, 28, 12, "Object is possibly \'null\'.", "765689629"], - [1082, 27, 12, "Object is possibly \'null\'.", "765689629"], - [1083, 27, 12, "Object is possibly \'null\'.", "765689629"], - [1084, 29, 12, "Object is possibly \'null\'.", "765689629"], - [1085, 29, 12, "Object is possibly \'null\'.", "765689629"], - [1086, 28, 12, "Object is possibly \'null\'.", "765689629"], - [1087, 34, 12, "Object is possibly \'null\'.", "765689629"], - [1088, 28, 12, "Object is possibly \'null\'.", "765689629"], - [1091, 27, 12, "Object is possibly \'null\'.", "765689629"], - [1092, 27, 12, "Object is possibly \'null\'.", "765689629"], - [1093, 29, 12, "Object is possibly \'null\'.", "765689629"], - [1094, 29, 12, "Object is possibly \'null\'.", "765689629"], - [1095, 28, 12, "Object is possibly \'null\'.", "765689629"], - [1096, 34, 12, "Object is possibly \'null\'.", "765689629"], - [1097, 28, 12, "Object is possibly \'null\'.", "765689629"], - [1100, 27, 12, "Object is possibly \'null\'.", "765689629"], - [1101, 27, 12, "Object is possibly \'null\'.", "765689629"], - [1102, 29, 12, "Object is possibly \'null\'.", "765689629"], - [1103, 29, 12, "Object is possibly \'null\'.", "765689629"], - [1104, 28, 12, "Object is possibly \'null\'.", "765689629"], - [1105, 34, 12, "Object is possibly \'null\'.", "765689629"], - [1106, 28, 12, "Object is possibly \'null\'.", "765689629"], - [1109, 27, 12, "Object is possibly \'null\'.", "765689629"], - [1110, 27, 12, "Object is possibly \'null\'.", "765689629"], - [1111, 29, 12, "Object is possibly \'null\'.", "765689629"], - [1112, 29, 12, "Object is possibly \'null\'.", "765689629"], - [1113, 28, 12, "Object is possibly \'null\'.", "765689629"], - [1114, 34, 12, "Object is possibly \'null\'.", "765689629"], - [1115, 28, 12, "Object is possibly \'null\'.", "765689629"] - ], "src/app/widgets/widget-zones-state-panel/widget-zones-state-panel.component.ts:2501103102": [ [78, 38, 9, "Argument of type \'string | undefined\' is not assignable to parameter of type \'string\'.\\n Type \'undefined\' is not assignable to type \'string\'.", "4119212820"] ], diff --git a/src/app/core/components/widget-history-chart-dialog/widget-history-chart-dialog.component.ts b/src/app/core/components/widget-history-chart-dialog/widget-history-chart-dialog.component.ts index e75a14eb..5038d232 100644 --- a/src/app/core/components/widget-history-chart-dialog/widget-history-chart-dialog.component.ts +++ b/src/app/core/components/widget-history-chart-dialog/widget-history-chart-dialog.component.ts @@ -6,7 +6,7 @@ import { Chart, ChartConfiguration, ChartDataset, Color, LegendItem, LineControl import 'chartjs-adapter-date-fns'; import { IWidget } from '../../interfaces/widgets-interface'; import { IKipSeriesDefinition } from '../../services/kip-series-api-client.service'; -import { isKipTemplateSeriesDefinition, type IKipTemplateSeriesDefinition } from '../../contracts/kip-series-contract'; +import { isKipTemplateSeriesDefinition, type IKipConcreteSeriesDefinition, type IKipTemplateSeriesDefinition } from '../../contracts/kip-series-contract'; import { describeElectricalDualAxisSeries, getAxisConfigsForWidget, @@ -19,7 +19,7 @@ import { transformDualAxisMetricValue } from '../../contracts/electrical-history-chart.contract'; import { HistoryToChartMapperService } from '../../services/history-to-chart-mapper.service'; -import { AppService } from '../../services/app-service'; +import { AppService, ITheme } from '../../services/app-service'; import { UnitsService } from '../../services/units.service'; import { HistoryApiClientService } from '../../services/history-api-client.service'; import { MatIconModule } from '@angular/material/icon'; @@ -30,7 +30,8 @@ Chart.register(LineController, LineElement, LinearScale, PointElement, TimeScale interface ChartPoint { x: number; - y: number; + /** A gap in the source history (no value at this timestamp) is a normal, chart.js-renderable state. */ + y: number | null; } type HistoryChartDataset = ChartDataset<'line', ChartPoint[]>; @@ -214,7 +215,7 @@ export class WidgetHistoryChartDialogComponent implements OnInit, AfterViewInit, // Apply conversion based on dual-axis metric metadata first, then optional widget-level override. chartPoints = datapoints.map(point => { - let y = point.data.value as number; + let y: number | null = point.data.value; if (typeof y === 'number' && Number.isFinite(y)) { if (dualAxisSeries) { y = transformDualAxisMetricValue(dualAxisSeries, y); @@ -279,17 +280,20 @@ export class WidgetHistoryChartDialogComponent implements OnInit, AfterViewInit, } private getSeriesPalette(): string[] { + const theme = this.theme(); + if (!theme) return []; + const configColorKey = this.data.widget?.config?.color || 'blue'; const preferredColor = this.resolveThemeColor(configColorKey); let palette = [ - this.theme().green, - this.theme().orange, - this.theme().pink, - this.theme().purple, - this.theme().yellow, - this.theme().grey, - this.theme().blue, - this.theme().contrast + theme.green, + theme.orange, + theme.pink, + theme.purple, + theme.yellow, + theme.grey, + theme.blue, + theme.contrast ]; const existingIndex = palette.findIndex(color => color?.toLowerCase() === preferredColor.toLowerCase()); @@ -303,7 +307,9 @@ export class WidgetHistoryChartDialogComponent implements OnInit, AfterViewInit, } private resolveThemeColor(colorKey: string): string { - const themeColor = this.theme()[colorKey as keyof ReturnType]; + const theme = this.theme(); + if (!theme) return colorKey; + const themeColor = theme[colorKey as keyof ITheme]; return typeof themeColor === 'string' && themeColor.length > 0 ? themeColor : colorKey; } @@ -416,14 +422,16 @@ export class WidgetHistoryChartDialogComponent implements OnInit, AfterViewInit, return [series]; } - return matchedPaths.map((path, index) => ({ + return matchedPaths.map((path, index): IKipConcreteSeriesDefinition => ({ ...series, seriesId: `${series.seriesId}:resolved:${index}`, datasetUuid: `${series.datasetUuid}:resolved:${index}`, path, + // A resolved concrete path is no longer a template: clear all template-only bookkeeping fields. expansionMode: null, familyKey: null, - allowedIds: null + allowedIds: null, + trackedDevices: null })); } @@ -521,6 +529,11 @@ export class WidgetHistoryChartDialogComponent implements OnInit, AfterViewInit, return; } + const theme = this.theme(); + if (!theme) { + return; + } + this.chart?.destroy(); const unitLabel = this.getPrimaryAxisUnitLabel(); @@ -558,10 +571,10 @@ export class WidgetHistoryChartDialogComponent implements OnInit, AfterViewInit, }, ticks: { - color: this.theme().contrastDim, + color: theme.contrastDim, }, grid: { - color: this.theme().contrastDimmer + color: theme.contrastDimmer } }, ...this.buildYScales(unitLabel) @@ -569,7 +582,7 @@ export class WidgetHistoryChartDialogComponent implements OnInit, AfterViewInit, plugins: { legend: { labels: { - color: this.theme().contrastDim, + color: theme.contrastDim, generateLabels: chart => this.buildLegendLabels(chart) } }, @@ -588,7 +601,7 @@ export class WidgetHistoryChartDialogComponent implements OnInit, AfterViewInit, : undefined; return { - borderColor: (legendItem?.strokeStyle ?? this.theme().contrast) as string, + borderColor: (legendItem?.strokeStyle ?? theme.contrast) as string, backgroundColor: 'rgba(0, 0, 0, 0)', borderWidth: 2, borderRadius: 0, @@ -636,20 +649,23 @@ export class WidgetHistoryChartDialogComponent implements OnInit, AfterViewInit, } private buildYScales(unitLabel: string): NonNullable['options']>['scales']> { + const theme = this.theme(); + if (!theme) return {}; + const dualAxisWidgetType = this.resolveDualAxisWidgetType(); if (!dualAxisWidgetType) { return { y: { ticks: { - color: this.theme().contrastDim + color: theme.contrastDim }, grid: { - color: this.theme().contrastDimmer + color: theme.contrastDimmer }, title: { display: !!unitLabel, text: unitLabel ? `${unitLabel}` : '', - color: this.theme().contrastDim + color: theme.contrastDim } } }; @@ -662,7 +678,7 @@ export class WidgetHistoryChartDialogComponent implements OnInit, AfterViewInit, const axisMetric = this.resolvePrimaryMetricForAxis(dualAxisWidgetType, axisConfig.axisId); const axisColor = axisMetric ? this.getDualAxisMetricColor(dualAxisWidgetType, axisMetric) - : this.theme().contrastDim; + : theme.contrastDim; scales[axisConfig.axisId] = { type: 'linear', @@ -670,11 +686,11 @@ export class WidgetHistoryChartDialogComponent implements OnInit, AfterViewInit, ...(axisConfig.min != null ? { min: axisConfig.min } : {}), ...(axisConfig.max != null ? { max: axisConfig.max } : {}), ticks: { - color: this.theme().contrastDim, + color: theme.contrastDim, callback: (value: unknown) => this.formatAxisTick(value, axisConfig.tickSuffix) }, grid: { - color: this.theme().contrastDimmer, + color: theme.contrastDimmer, ...(axisConfig.drawOnChartArea === false ? { drawOnChartArea: false } : {}) }, title: { @@ -707,7 +723,9 @@ export class WidgetHistoryChartDialogComponent implements OnInit, AfterViewInit, const dataset = label.datasetIndex != null ? chart.data.datasets[label.datasetIndex] as HistoryChartDataset : null; - const borderDash = Array.isArray(dataset?.borderDash) ? dataset.borderDash : []; + const borderDash: number[] = Array.isArray(dataset?.borderDash) + ? dataset.borderDash.filter((v): v is number => typeof v === 'number') + : []; return { ...label, fillStyle: 'transparent' as Color, diff --git a/src/app/core/interfaces/widgets-interface.ts b/src/app/core/interfaces/widgets-interface.ts index 287503e8..e69898c6 100644 --- a/src/app/core/interfaces/widgets-interface.ts +++ b/src/app/core/interfaces/widgets-interface.ts @@ -501,7 +501,7 @@ export interface IAutopilotConfig { /** Set heading direction type is Magnetic or True */ headingDirectionTrue: boolean, /** Set API version of the autopilot. v1 only supports Signal K autopilot plugin (also known as Raymarine autopilot) */ - apiVersion: "v1" | "v2", + apiVersion: "v1" | "v2" | null, /** The autopilot provider supports multiple autopilot * operating simultaneously. Set the autopilot instance ID to control * ('_default', 'pypilot-1', 'pypilot-2', 'raymarine-1', etc.). Use diff --git a/src/app/widgets/minichart/minichart.component.ts b/src/app/widgets/minichart/minichart.component.ts index 7aca8fc5..8a3eafc6 100644 --- a/src/app/widgets/minichart/minichart.component.ts +++ b/src/app/widgets/minichart/minichart.component.ts @@ -15,17 +15,17 @@ import ChartStreaming from '@aziham/chartjs-plugin-streaming'; Chart.register(ChartStreaming, TimeScale, LinearScale, LineController, PointElement, LineElement, Filler, CategoryScale); interface IChartColors { - valueLine: string, - valueFill: string, - averageLine: string, - averageFill: string, - averageChartLine: string, - chartLabel: string, - chartValue: string + valueLine: string | undefined, + valueFill: string | undefined, + averageLine: string | undefined, + averageFill: string | undefined, + averageChartLine: string | undefined, + chartLabel: string | undefined, + chartValue: string | undefined } interface IDataSetRow { - x: number, - y: number + x: number | null, + y: number | null } /** The numeric widget's background sparkline always shows the same short fixed window. */ @@ -41,13 +41,14 @@ const MINICHART_PERIOD = 0.2; }) export class MinichartComponent implements OnDestroy { protected readonly theme = input.required(); - public color: string = null; - public dataPath: string = null; - public dataSource: string = null; - public convertUnitTo: string = null; - public numDecimal: number = null; - public yScaleMin: number = null; - public yScaleMax: number = null; + public color = ''; + public dataPath: string | null = null; + public dataSource = ''; + /** No unit conversion configured is a normal state (parent passes this through unfallbacked). */ + public convertUnitTo: string | null | undefined = null; + public numDecimal = 0; + public yScaleMin = 0; + public yScaleMax = 0; public inverseYAxis = false; public verticalChart: boolean | null = null; protected unitsService = inject(UnitsService); @@ -58,7 +59,7 @@ export class MinichartComponent implements OnDestroy { public lineChartData: ChartData<'line', { x: number, y: number }[]> = { datasets: [] }; - public lineChartOptions: ChartConfiguration['options'] = { + public lineChartOptions: NonNullable = { parsing: false, datasets: { line: { @@ -75,8 +76,8 @@ export class MinichartComponent implements OnDestroy { } public lineChartType: ChartType = 'line'; private chart; - private streamSub: Subscription = null; - private dataSourceInfo: IChartDataSourceInfo = null; + private streamSub: Subscription | null = null; + private dataSourceInfo: IChartDataSourceInfo | null = null; private isDestroyed = false; private lastChartSignature: string | null = null; @@ -150,6 +151,9 @@ export class MinichartComponent implements OnDestroy { } private setChartOptions() { + const dataSourceInfo = this.dataSourceInfo; + if (!dataSourceInfo) return; + this.lineChartOptions.maintainAspectRatio = false; this.lineChartOptions.animation = false; @@ -160,10 +164,10 @@ export class MinichartComponent implements OnDestroy { x: { display: false, position: "right", - suggestedMin: this.config.enableMinMaxScaleLimit ? null : this.yScaleMin, - suggestedMax: this.config.enableMinMaxScaleLimit ? null : this.yScaleMax, - min: this.config.enableMinMaxScaleLimit ? this.yScaleMin : null, - max: this.config.enableMinMaxScaleLimit ? this.yScaleMax : null, + suggestedMin: this.config.enableMinMaxScaleLimit ? undefined : this.yScaleMin, + suggestedMax: this.config.enableMinMaxScaleLimit ? undefined : this.yScaleMax, + min: this.config.enableMinMaxScaleLimit ? this.yScaleMin : undefined, + max: this.config.enableMinMaxScaleLimit ? this.yScaleMax : undefined, beginAtZero: this.config.startScaleAtZero, reverse: this.inverseYAxis, title: { @@ -252,10 +256,10 @@ export class MinichartComponent implements OnDestroy { y: { display: false, position: "right", - suggestedMin: this.config.enableMinMaxScaleLimit ? null : this.yScaleMin, - suggestedMax: this.config.enableMinMaxScaleLimit ? null : this.yScaleMax, - min: this.config.enableMinMaxScaleLimit ? this.yScaleMin : null, - max: this.config.enableMinMaxScaleLimit ? this.yScaleMax : null, + suggestedMin: this.config.enableMinMaxScaleLimit ? undefined : this.yScaleMin, + suggestedMax: this.config.enableMinMaxScaleLimit ? undefined : this.yScaleMax, + min: this.config.enableMinMaxScaleLimit ? this.yScaleMin : undefined, + max: this.config.enableMinMaxScaleLimit ? this.yScaleMax : undefined, beginAtZero: this.config.startScaleAtZero, reverse: this.inverseYAxis, title: { @@ -284,8 +288,8 @@ export class MinichartComponent implements OnDestroy { display: false }, streaming: { - duration: this.dataSourceInfo.maxDataPoints * this.dataSourceInfo.sampleTime, - delay: this.dataSourceInfo.sampleTime, + duration: dataSourceInfo.maxDataPoints * dataSourceInfo.sampleTime, + delay: dataSourceInfo.sampleTime, frameRate: MINICHART_TIME_SCALE === "day" ? 5 : MINICHART_TIME_SCALE === "hour" ? 8 : MINICHART_TIME_SCALE === "minute" ? 15 : 30, } } @@ -337,13 +341,13 @@ export class MinichartComponent implements OnDestroy { private getThemeColors(): IChartColors { const widgetColor = this.color; const colors: IChartColors = { - valueLine: null, - valueFill: null, - averageLine: null, - averageFill: null, - averageChartLine: null, - chartLabel: null, - chartValue: null + valueLine: undefined, + valueFill: undefined, + averageLine: undefined, + averageFill: undefined, + averageChartLine: undefined, + chartLabel: undefined, + chartValue: undefined }; switch (widgetColor) { @@ -498,8 +502,11 @@ export class MinichartComponent implements OnDestroy { this.streamSub?.unsubscribe(); const info = this.dataSourceInfo; + const dataPath = this.dataPath; + if (!info || !dataPath) return; + const params: IHistoryChartStreamParams = { - path: this.dataPath, + path: dataPath, source: this.dataSource ?? 'default', windowMs: resolveWindowMs(MINICHART_TIME_SCALE, MINICHART_PERIOD), sampleTime: info.sampleTime, @@ -552,7 +559,7 @@ export class MinichartComponent implements OnDestroy { private transformDatasetRows(rows: IDatasetServiceDatapoint[], datasetType): IDataSetRow[] { const convert = (v: number) => - this.unitsService.convertToUnit(this.convertUnitTo, v); + this.unitsService.convertToUnit(this.convertUnitTo ?? '', v); const verticalChart = this.verticalChart; const avgKey = this.config.datasetAverageArray; diff --git a/src/app/widgets/widget-autopilot/widget-autopilot.component.ts b/src/app/widgets/widget-autopilot/widget-autopilot.component.ts index 62dd8969..89fe340e 100644 --- a/src/app/widgets/widget-autopilot/widget-autopilot.component.ts +++ b/src/app/widgets/widget-autopilot/widget-autopilot.component.ts @@ -322,7 +322,7 @@ export class WidgetAutopilotComponent implements OnInit, OnDestroy { // Keypad buttons & layout protected apGrid = computed(() => this.apMode() ? 'grid' : 'none'); protected readonly standbyButtonLabel = computed(() => { - const apiVersion = this.runtime.options().autopilot.apiVersion; + const apiVersion = this.runtime.options()?.autopilot?.apiVersion; if (apiVersion === "v2" && !this.apEngaged()) { return "Engage"; @@ -334,12 +334,12 @@ export class WidgetAutopilotComponent implements OnInit, OnDestroy { const state = this.apState(); // eslint-disable-next-line @typescript-eslint/no-unused-vars const engaged = this.apEngaged(); - const apiVersion = this.runtime.options().autopilot.apiVersion; + const apiVersion = this.runtime.options()?.autopilot?.apiVersion; if (!apiVersion) return true; if (apiVersion === "v1") { - return (['standby', 'off-line'].includes(state)) ? true : false; + return (state !== null && ['standby', 'off-line'].includes(state)) ? true : false; } if (apiVersion === "v2") { @@ -349,7 +349,7 @@ export class WidgetAutopilotComponent implements OnInit, OnDestroy { }); protected readonly apBtnDisabled = computed(() => { const engaged = this.apEngaged(); - const apiVersion = this.runtime.options().autopilot.apiVersion; + const apiVersion = this.runtime.options()?.autopilot?.apiVersion; if (apiVersion === "v1") { return this.apMode() === 'standby' ? true : false; @@ -361,14 +361,14 @@ export class WidgetAutopilotComponent implements OnInit, OnDestroy { }); protected readonly adjustHdgBtnVisibility = computed(() => { const mode = this.apMode(); - if ( ['auto', 'compass', 'gps', 'wind', 'true wind', 'standby'].includes(mode)) { + if ( mode !== null && ['auto', 'compass', 'gps', 'wind', 'true wind', 'standby'].includes(mode)) { return true; } return false; }); protected readonly tackBtnVisibility = computed(() => { const mode = this.apMode(); - if ( ['auto', 'compass', 'gps', 'wind', 'true wind', 'standby'].includes(mode)) { + if ( mode !== null && ['auto', 'compass', 'gps', 'wind', 'true wind', 'standby'].includes(mode)) { return true; } return false; @@ -399,7 +399,7 @@ export class WidgetAutopilotComponent implements OnInit, OnDestroy { // Mode Menu protected menuItems = computed(() => { const mode = this.apMode(); - const apiVersion = this.runtime.options().autopilot.apiVersion; + const apiVersion = this.runtime.options()?.autopilot?.apiVersion; let menuItems: MenuItem[] = []; untracked(() => { @@ -610,9 +610,9 @@ export class WidgetAutopilotComponent implements OnInit, OnDestroy { state: '' }; - const apiVersion = this.runtime.options().autopilot.apiVersion; - const instanceId = this.runtime.options().autopilot.instanceId; - const pluginId = this.runtime.options().autopilot.pluginId; + const apiVersion = this.runtime.options()?.autopilot?.apiVersion; + const instanceId = this.runtime.options()?.autopilot?.instanceId; + const pluginId = this.runtime.options()?.autopilot?.pluginId; // Helper for persistent error state const setPersistentError = (message: string) => { @@ -672,7 +672,7 @@ export class WidgetAutopilotComponent implements OnInit, OnDestroy { } private autopilotModes(): string[] { - const modes = this.runtime.options().autopilot.modes; + const modes = this.runtime.options()?.autopilot?.modes; if (Array.isArray(modes)) { return modes; @@ -686,6 +686,7 @@ export class WidgetAutopilotComponent implements OnInit, OnDestroy { } private startV2Subscriptions(): void { + if (!this.streams) return; this.streams.observe('autopilotState', newValue => { if (newValue.data?.value) { this.apState.set(newValue.data.value); @@ -723,8 +724,15 @@ export class WidgetAutopilotComponent implements OnInit, OnDestroy { } private startV1Subscriptions(): void { + if (!this.streams) return; // For V1, we use this single legacy path - this.runtime.options().paths['autopilotMode'].path = API_PATHS.V1_MODE_PATH; + const cfg = this.runtime.options(); + const autopilotModePathCfg = cfg && !Array.isArray(cfg.paths) ? cfg.paths?.['autopilotMode'] : undefined; + if (autopilotModePathCfg) { + autopilotModePathCfg.path = API_PATHS.V1_MODE_PATH; + } else { + console.warn('[Autopilot Widget] Missing autopilotMode path config; V1 mode subscription will use its configured path as-is'); + } this.streams.observe('autopilotMode', newValue => { if (newValue.data?.value) { this.apMode.set(newValue.data.value); @@ -745,17 +753,19 @@ export class WidgetAutopilotComponent implements OnInit, OnDestroy { } private startDataSubscription(): void { + if (!this.streams) return; this.streams.observe('courseXte', newValue => this.crossTrackError.set(newValue.data.value != null ? newValue.data.value : 0)); this.streams.observe('rudderAngle', newValue => { if (newValue.data.value === null) { this.rudder.set(null); } else { - this.rudder.set(this.runtime.options().autopilot.invertRudder ? -newValue.data.value : newValue.data.value); + const invertRudder = this.runtime.options()?.autopilot?.invertRudder ?? true; + this.rudder.set(invertRudder ? -newValue.data.value : newValue.data.value); } } ); - if (this.runtime.options().autopilot.headingDirectionTrue) { + if (this.runtime.options()?.autopilot?.headingDirectionTrue ?? false) { this.streams.observe('headingTrue', newValue => this.heading.set(newValue.data.value != null ? newValue.data.value : 0)); } else { this.streams.observe('headingMag', newValue => this.heading.set(newValue.data.value != null ? newValue.data.value : 0)); @@ -799,7 +809,7 @@ export class WidgetAutopilotComponent implements OnInit, OnDestroy { if (typeof cmdAction === 'undefined') { // V2 autopilot modes are provider-defined free-form strings, so a reported mode outside the // built-in COMMANDS table is still commandable via its raw mode string. - if (this.runtime.options().autopilot.apiVersion === 'v2' && this.autopilotModes().includes(cmd)) { + if (this.runtime.options()?.autopilot?.apiVersion === 'v2' && this.autopilotModes().includes(cmd)) { this.sendV2Command(cmd); return; } @@ -832,7 +842,7 @@ export class WidgetAutopilotComponent implements OnInit, OnDestroy { } private routeCommand(cmd: string, cmdAction: IV1CommandDefinition): void { - const apiVersion = this.runtime.options().autopilot.apiVersion; + const apiVersion = this.runtime.options()?.autopilot?.apiVersion; if (apiVersion === 'v2') { if (cmd === 'route' && this.apMode() === 'nav') { cmd = 'advanceWaypoint'; @@ -1012,6 +1022,7 @@ export class WidgetAutopilotComponent implements OnInit, OnDestroy { break; default: console.error('[Autopilot Widget] Unsupported REST method:', method); + return { statusCode: 0, message: 'Unsupported REST method', state: null }; } if (response && response.statusCode === 200) { @@ -1057,7 +1068,7 @@ export class WidgetAutopilotComponent implements OnInit, OnDestroy { } private performTackOrGybe(operation: 'tack' | 'gybe', direction: 'port' | 'starboard'): void { - if (this.runtime.options().autopilot.apiVersion === 'v2') { + if (this.runtime.options()?.autopilot?.apiVersion === 'v2') { this.sendV2Command(operation, {value: direction}); } else { // Fall back to V1 @@ -1070,7 +1081,7 @@ export class WidgetAutopilotComponent implements OnInit, OnDestroy { // V2 Absolute Target Method (class only - no UI yet) protected setAbsoluteTarget(heading: number): void { - if (this.runtime.options().autopilot.apiVersion === 'v2') { + if (this.runtime.options()?.autopilot?.apiVersion === 'v2') { this.sendV2Command('target_heading', {"value": heading, "units": "deg"}); } else { console.error('[Autopilot Widget] Absolute target only available in V2 API'); @@ -1079,7 +1090,7 @@ export class WidgetAutopilotComponent implements OnInit, OnDestroy { // V2 Dodge Method (class only - no UI yet) protected toggleDodge(): void { - if (this.runtime.options().autopilot.apiVersion === 'v2') { + if (this.runtime.options()?.autopilot?.apiVersion === 'v2') { this.sendV2Command('dodge'); } else { console.warn('[Autopilot Widget] Dodge mode only available in V2 API'); @@ -1131,7 +1142,7 @@ export class WidgetAutopilotComponent implements OnInit, OnDestroy { this.updateCountDownCounter(message); - clearTimeout(this.handleConfirmActionTimeout); + clearTimeout(this.handleConfirmActionTimeout ?? undefined); this.handleConfirmActionTimeout = setTimeout(() => { this.countdownOverlayVisibility.set("hidden"); @@ -1141,8 +1152,8 @@ export class WidgetAutopilotComponent implements OnInit, OnDestroy { } private clearConfirmCmd(): void { - clearTimeout(this.handleConfirmActionTimeout); - clearTimeout(this.handleCountDownCounterTimeout); + clearTimeout(this.handleConfirmActionTimeout ?? undefined); + clearTimeout(this.handleCountDownCounterTimeout ?? undefined); this.countDownValue = -1; this.countdownOverlayVisibility.set("hidden"); this.countdownOverlayText.set(""); @@ -1151,7 +1162,7 @@ export class WidgetAutopilotComponent implements OnInit, OnDestroy { private updateCountDownCounter(message: string): void { if (this.countDownValue > 0) { - clearTimeout(this.handleCountDownCounterTimeout); + clearTimeout(this.handleCountDownCounterTimeout ?? undefined); this.countdownOverlayText.set(message); this.countDownValue -= 1; this.handleCountDownCounterTimeout = setTimeout(() => { @@ -1159,7 +1170,7 @@ export class WidgetAutopilotComponent implements OnInit, OnDestroy { }, 1000); } else { this.countDownValue = -1; - clearTimeout(this.handleCountDownCounterTimeout); + clearTimeout(this.handleCountDownCounterTimeout ?? undefined); } } @@ -1177,7 +1188,7 @@ export class WidgetAutopilotComponent implements OnInit, OnDestroy { this.errorOverlayText.set(errMsg); this.errorOverlayVisibility.set("visible"); - clearTimeout(this.handleDisplayErrorTimeout); + clearTimeout(this.handleDisplayErrorTimeout ?? undefined); this.handleDisplayErrorTimeout = setTimeout(() => { // Only hide if it's not a persistent error if (!this.isPersistentError) { @@ -1200,9 +1211,9 @@ export class WidgetAutopilotComponent implements OnInit, OnDestroy { this.menuOpen.set(false); } - private parseMenuItems(menuItems: MenuItem[], mode: string): MenuItem[] { + private parseMenuItems(menuItems: MenuItem[], mode: string | null): MenuItem[] { // Set enabled/disabled state for each mode menu item based - const apiVersion = this.runtime.options().autopilot.apiVersion; + const apiVersion = this.runtime.options()?.autopilot?.apiVersion; const parsedMenuItems = menuItems.map(item => { if (item.isCancel) return { ...item, current: false, disabled: false }; @@ -1242,9 +1253,9 @@ export class WidgetAutopilotComponent implements OnInit, OnDestroy { this.cancelAllHttpRequests(); // Clear any pending timeouts - clearTimeout(this.handleCountDownCounterTimeout); - clearTimeout(this.handleConfirmActionTimeout); - clearTimeout(this.handleDisplayErrorTimeout); - clearTimeout(this.handleMessageTimeout); + clearTimeout(this.handleCountDownCounterTimeout ?? undefined); + clearTimeout(this.handleConfirmActionTimeout ?? undefined); + clearTimeout(this.handleDisplayErrorTimeout ?? undefined); + clearTimeout(this.handleMessageTimeout ?? undefined); } } diff --git a/src/app/widgets/widget-data-chart/widget-data-chart.component.ts b/src/app/widgets/widget-data-chart/widget-data-chart.component.ts index fd5ceb09..e7e824c9 100644 --- a/src/app/widgets/widget-data-chart/widget-data-chart.component.ts +++ b/src/app/widgets/widget-data-chart/widget-data-chart.component.ts @@ -19,7 +19,7 @@ Chart.register(annotationPlugin, ChartStreaming, Filler, Title, SubTitle, TimeSc interface AnnPlugin { annotation?: { - annotations?: Record + annotations?: Record } } interface IChartColors { @@ -31,7 +31,7 @@ interface IChartColors { chartLabel: string, chartValue: string } -interface IDataSetRow { x: number, y: number } +interface IDataSetRow { x: number | null, y: number | null } @Component({ selector: 'widget-data-chart', @@ -78,14 +78,14 @@ export class WidgetDataChartComponent implements OnDestroy { startScaleAtZero: false, verticalChart: false, showYScale: false, - yScaleSuggestedMin: null, - yScaleSuggestedMax: null, + yScaleSuggestedMin: undefined, + yScaleSuggestedMax: undefined, enableMinMaxScaleLimit: false, - yScaleMin: null, - yScaleMax: null, + yScaleMin: undefined, + yScaleMax: undefined, }; public lineChartData: ChartData<'line', { x: number, y: number }[]> = { datasets: [] }; - public lineChartOptions: ChartConfiguration['options'] = { + public lineChartOptions: NonNullable = { parsing: false, datasets: { line: { pointRadius: 0, pointHoverRadius: 0, tension: 0.4 } }, animations: { tension: { easing: 'easeInOutCubic' } } @@ -95,7 +95,7 @@ export class WidgetDataChartComponent implements OnDestroy { private streamSub: Subscription | null = null; private datasetConfig: IDatasetServiceDatasetConfig | null = null; private dataSourceInfo: IDatasetServiceDataSourceInfo | null = null; - private lastVerticalChart: boolean | null = null; + private lastVerticalChart: boolean | null | undefined = null; protected hasPath = computed(() => { const cfg = this.runtime.options(); return !!cfg?.datachartPath; @@ -105,7 +105,7 @@ export class WidgetDataChartComponent implements OnDestroy { protected historyUnavailable = signal(false); private pathSignature = computed(() => { const cfg = this.runtime.options(); - if (!cfg.datachartPath) { + if (!cfg?.datachartPath) { return undefined; } return [cfg.datachartPath, cfg.convertUnitTo, cfg.datachartSource, cfg.timeScale, cfg.period, cfg.datachartAngleRange].join('|'); @@ -119,9 +119,11 @@ export class WidgetDataChartComponent implements OnDestroy { untracked(() => { if (sig !== this.previousPathSignature) { + const cfg = this.runtime.options(); + if (!cfg) return; untracked(() => { this.previousPathSignature = sig; - this.rebuildForDataset(this.runtime.options()); + this.rebuildForDataset(cfg); }); } }); @@ -161,7 +163,8 @@ export class WidgetDataChartComponent implements OnDestroy { private rebuildForDataset(cfg: IWidgetSvcConfig): void { if (!cfg.datachartPath) return; // Widget not yet configured - if (!this.widgetDataChart()) return; // View not ready yet + const canvasRef = this.widgetDataChart(); + if (!canvasRef) return; // View not ready yet this.streamSub?.unsubscribe(); // Cleanup old subscription & chart data this.lineChartData.datasets = []; @@ -169,7 +172,8 @@ export class WidgetDataChartComponent implements OnDestroy { // Synthesize the config + cadence the axis/streaming options expect, derived from the widget's // display window. - const windowMs = resolveWindowMs(cfg.timeScale as TimeScaleFormat, cfg.period); + const period = cfg.period ?? 10; + const windowMs = resolveWindowMs(cfg.timeScale as TimeScaleFormat, period); this.dataSourceInfo = deriveDataSourceInfo(windowMs); this.datasetConfig = { uuid: this.id(), @@ -177,7 +181,7 @@ export class WidgetDataChartComponent implements OnDestroy { pathSource: cfg.datachartSource ?? 'default', baseUnit: '', timeScaleFormat: cfg.timeScale as TimeScaleFormat, - period: cfg.period, + period: period, label: '', angleDomainOverride: cfg.datachartAngleRange ?? undefined }; @@ -185,7 +189,7 @@ export class WidgetDataChartComponent implements OnDestroy { this.setChartOptions(cfg); // Always recreate chart instance on rebuild to ensure orientation/scale axis changes apply this.chart?.destroy(); - this.chart = new Chart(this.widgetDataChart().nativeElement.getContext('2d'), { + this.chart = new Chart(canvasRef.nativeElement.getContext('2d'), { type: this.lineChartType, data: this.lineChartData, options: this.lineChartOptions @@ -195,6 +199,14 @@ export class WidgetDataChartComponent implements OnDestroy { } private setChartOptions(cfg: IWidgetSvcConfig): void { + // Both fields are always populated by rebuildForDataset() before a chart exists, and + // setChartOptions() is only ever called once a chart does; theme() can only be transiently + // null during the app's very first render tick, before AppService publishes the real palette. + const theme = this.theme(); + const datasetConfig = this.datasetConfig; + const dataSourceInfo = this.dataSourceInfo; + if (!theme || !datasetConfig || !dataSourceInfo) return; + this.lineChartOptions.maintainAspectRatio = false; this.lineChartOptions.animation = false; this.lineChartOptions.indexAxis = cfg.verticalChart ? 'y' : 'x'; @@ -209,12 +221,12 @@ export class WidgetDataChartComponent implements OnDestroy { suggestedMax: "", title: { display: true, - text: `Last ${this.datasetConfig.period} ${this.datasetConfig.timeScaleFormat}`, + text: `Last ${datasetConfig.period} ${datasetConfig.timeScaleFormat}`, align: "center", color: this.getThemeColors().averageChartLine }, time: { - unit: this.datasetConfig.timeScaleFormat as TimeUnit, + unit: datasetConfig.timeScaleFormat as TimeUnit, minUnit: "second", round: "second", displayFormats: { @@ -235,17 +247,17 @@ export class WidgetDataChartComponent implements OnDestroy { }, grid: { display: true, - color: this.theme().contrastDimmer + color: theme.contrastDimmer } }, x: { type: "linear", display: cfg.showYScale, position: cfg.verticalChart ? "top" : "bottom", - suggestedMin: cfg.enableMinMaxScaleLimit ? null : cfg.yScaleSuggestedMin, - suggestedMax: cfg.enableMinMaxScaleLimit ? null : cfg.yScaleSuggestedMax, - min: cfg.enableMinMaxScaleLimit ? cfg.yScaleMin : null, - max: cfg.enableMinMaxScaleLimit ? cfg.yScaleMax : null, + suggestedMin: cfg.enableMinMaxScaleLimit ? undefined : cfg.yScaleSuggestedMin, + suggestedMax: cfg.enableMinMaxScaleLimit ? undefined : cfg.yScaleSuggestedMax, + min: cfg.enableMinMaxScaleLimit ? cfg.yScaleMin : undefined, + max: cfg.enableMinMaxScaleLimit ? cfg.yScaleMax : undefined, beginAtZero: cfg.startScaleAtZero, reverse: cfg.inverseYAxis, title: { @@ -264,7 +276,7 @@ export class WidgetDataChartComponent implements OnDestroy { }, grid: { display: true, - color: this.theme().contrastDimmer, + color: theme.contrastDimmer, } } } @@ -275,12 +287,12 @@ export class WidgetDataChartComponent implements OnDestroy { display: cfg.showTimeScale, title: { display: true, - text: `Last ${this.datasetConfig.period} ${this.datasetConfig.timeScaleFormat}`, + text: `Last ${datasetConfig.period} ${datasetConfig.timeScaleFormat}`, align: "center", color: this.getThemeColors().averageChartLine }, time: { - unit: this.datasetConfig.timeScaleFormat as TimeUnit, + unit: datasetConfig.timeScaleFormat as TimeUnit, minUnit: "second", round: "second", displayFormats: { @@ -301,16 +313,16 @@ export class WidgetDataChartComponent implements OnDestroy { }, grid: { display: true, - color: this.theme().contrastDimmer + color: theme.contrastDimmer } }, y: { display: cfg.showYScale, position: "right", - suggestedMin: cfg.enableMinMaxScaleLimit ? null : cfg.yScaleSuggestedMin, - suggestedMax: cfg.enableMinMaxScaleLimit ? null : cfg.yScaleSuggestedMax, - min: cfg.enableMinMaxScaleLimit ? cfg.yScaleMin : null, - max: cfg.enableMinMaxScaleLimit ? cfg.yScaleMax : null, + suggestedMin: cfg.enableMinMaxScaleLimit ? undefined : cfg.yScaleSuggestedMin, + suggestedMax: cfg.enableMinMaxScaleLimit ? undefined : cfg.yScaleSuggestedMax, + min: cfg.enableMinMaxScaleLimit ? cfg.yScaleMin : undefined, + max: cfg.enableMinMaxScaleLimit ? cfg.yScaleMax : undefined, beginAtZero: cfg.startScaleAtZero, reverse: cfg.inverseYAxis, title: { @@ -329,7 +341,7 @@ export class WidgetDataChartComponent implements OnDestroy { }, grid: { display: true, - color: this.theme().contrastDimmer, + color: theme.contrastDimmer, } } } @@ -368,7 +380,7 @@ export class WidgetDataChartComponent implements OnDestroy { type: 'line', scaleID: cfg.verticalChart ? 'x' : 'y', display: cfg.showDatasetMinimumValueLine, - value: null, + value: undefined, drawTime: 'afterDatasetsDraw', label: { display: true, @@ -383,7 +395,7 @@ export class WidgetDataChartComponent implements OnDestroy { type: 'line', scaleID: cfg.verticalChart ? 'x' : 'y', display: cfg.showDatasetMaximumValueLine, - value: null, + value: undefined, drawTime: 'afterDatasetsDraw', label: { display: true, @@ -398,7 +410,7 @@ export class WidgetDataChartComponent implements OnDestroy { type: 'line', scaleID: cfg.verticalChart ? 'x' : 'y', display: cfg.showDatasetAverageValueLine, - value: null, + value: undefined, borderDash: [6, 6], borderColor: this.getThemeColors().averageChartLine, drawTime: 'afterDatasetsDraw', @@ -416,9 +428,9 @@ export class WidgetDataChartComponent implements OnDestroy { display: false }, streaming: { - duration: this.dataSourceInfo.maxDataPoints * this.dataSourceInfo.sampleTime, - delay: this.dataSourceInfo.sampleTime, - frameRate: this.datasetConfig.timeScaleFormat === "day" ? 5 : this.datasetConfig.timeScaleFormat === "hour" ? 8 : this.datasetConfig.timeScaleFormat === "minute" ? 15 : 30, + duration: dataSourceInfo.maxDataPoints * dataSourceInfo.sampleTime, + delay: dataSourceInfo.sampleTime, + frameRate: datasetConfig.timeScaleFormat === "day" ? 5 : datasetConfig.timeScaleFormat === "hour" ? 8 : datasetConfig.timeScaleFormat === "minute" ? 15 : 30, } } } @@ -554,167 +566,174 @@ export class WidgetDataChartComponent implements OnDestroy { } private getThemeColors(): IChartColors { + // Fallback (transparent) sentinel: theme() is only transiently null during the app's very + // first render tick, and widgetColor only escapes the switch below for a corrupted/legacy + // config value outside the fixed palette offered by the widget's color picker. + const NO_COLOR = 'rgba(0,0,0,0)'; const widgetColor = this.runtime.options()?.color; const colors: IChartColors = { - valueLine: null, - valueFill: null, - averageLine: null, - averageFill: null, - averageChartLine: null, - chartLabel: null, - chartValue: null + valueLine: NO_COLOR, + valueFill: NO_COLOR, + averageLine: NO_COLOR, + averageFill: NO_COLOR, + averageChartLine: NO_COLOR, + chartLabel: NO_COLOR, + chartValue: NO_COLOR }; + const theme = this.theme(); + if (!theme) return colors; + switch (widgetColor) { case "contrast": if (this.runtime.options()?.trackAgainstAverage) { - colors.valueLine = this.theme().contrastDimmer; - colors.valueFill = this.theme().contrastDimmer; - colors.averageLine = this.theme().contrast; - colors.averageFill = this.theme().contrast; + colors.valueLine = theme.contrastDimmer; + colors.valueFill = theme.contrastDimmer; + colors.averageLine = theme.contrast; + colors.averageFill = theme.contrast; colors.chartValue = colors.averageLine; } else { - colors.valueLine = this.theme().contrast; - colors.valueFill = this.theme().contrast; - colors.averageLine = this.theme().contrastDimmer; - colors.averageFill = this.theme().contrastDimmer; - colors.chartValue = this.theme().contrast; + colors.valueLine = theme.contrast; + colors.valueFill = theme.contrast; + colors.averageLine = theme.contrastDimmer; + colors.averageFill = theme.contrastDimmer; + colors.chartValue = theme.contrast; } - colors.averageChartLine = this.theme().contrastDim; - colors.chartLabel = this.theme().contrastDim; + colors.averageChartLine = theme.contrastDim; + colors.chartLabel = theme.contrastDim; break; case "blue": if (this.runtime.options()?.trackAgainstAverage) { - colors.valueLine = this.theme().blueDimmer; - colors.valueFill = this.theme().blueDimmer; - colors.averageLine = this.theme().blue; - colors.averageFill = this.theme().blue; + colors.valueLine = theme.blueDimmer; + colors.valueFill = theme.blueDimmer; + colors.averageLine = theme.blue; + colors.averageFill = theme.blue; colors.chartValue = colors.averageLine; } else { - colors.valueLine = this.theme().blue; - colors.valueFill = this.theme().blue; - colors.averageLine = this.theme().blueDimmer; - colors.averageFill = this.theme().blueDimmer; + colors.valueLine = theme.blue; + colors.valueFill = theme.blue; + colors.averageLine = theme.blueDimmer; + colors.averageFill = theme.blueDimmer; colors.chartValue = colors.valueFill; } - colors.averageChartLine = this.theme().blueDim; - colors.chartLabel = this.theme().contrastDim; + colors.averageChartLine = theme.blueDim; + colors.chartLabel = theme.contrastDim; break; case "green": if (this.runtime.options()?.trackAgainstAverage) { - colors.valueLine = this.theme().greenDimmer; - colors.valueFill = this.theme().greenDimmer; - colors.averageLine = this.theme().green; - colors.averageFill = this.theme().green; + colors.valueLine = theme.greenDimmer; + colors.valueFill = theme.greenDimmer; + colors.averageLine = theme.green; + colors.averageFill = theme.green; colors.chartValue = colors.averageLine; } else { - colors.valueLine = this.theme().green; - colors.valueFill = this.theme().green; - colors.averageLine = this.theme().greenDimmer; - colors.averageFill = this.theme().greenDimmer; + colors.valueLine = theme.green; + colors.valueFill = theme.green; + colors.averageLine = theme.greenDimmer; + colors.averageFill = theme.greenDimmer; colors.chartValue = colors.valueFill; } - colors.averageChartLine = this.theme().greenDim; - colors.chartLabel = this.theme().contrastDim; + colors.averageChartLine = theme.greenDim; + colors.chartLabel = theme.contrastDim; break; case "pink": if (this.runtime.options()?.trackAgainstAverage) { - colors.valueLine = this.theme().pinkDimmer; - colors.valueFill = this.theme().pinkDimmer; - colors.averageLine = this.theme().pink; - colors.averageFill = this.theme().pink; + colors.valueLine = theme.pinkDimmer; + colors.valueFill = theme.pinkDimmer; + colors.averageLine = theme.pink; + colors.averageFill = theme.pink; colors.chartValue = colors.averageLine; } else { - colors.valueLine = this.theme().pink; - colors.valueFill = this.theme().pink; - colors.averageLine = this.theme().pinkDimmer; - colors.averageFill = this.theme().pinkDimmer; + colors.valueLine = theme.pink; + colors.valueFill = theme.pink; + colors.averageLine = theme.pinkDimmer; + colors.averageFill = theme.pinkDimmer; colors.chartValue = colors.valueFill; } - colors.averageChartLine = this.theme().pinkDim; - colors.chartLabel = this.theme().contrastDim; + colors.averageChartLine = theme.pinkDim; + colors.chartLabel = theme.contrastDim; break; case "orange": if (this.runtime.options()?.trackAgainstAverage) { - colors.valueLine = this.theme().orangeDimmer; - colors.valueFill = this.theme().orangeDimmer; - colors.averageLine = this.theme().orange; - colors.averageFill = this.theme().orange; + colors.valueLine = theme.orangeDimmer; + colors.valueFill = theme.orangeDimmer; + colors.averageLine = theme.orange; + colors.averageFill = theme.orange; colors.chartValue = colors.averageLine; } else { - colors.valueLine = this.theme().orange; - colors.valueFill = this.theme().orange; - colors.averageLine = this.theme().orangeDimmer; - colors.averageFill = this.theme().orangeDimmer; + colors.valueLine = theme.orange; + colors.valueFill = theme.orange; + colors.averageLine = theme.orangeDimmer; + colors.averageFill = theme.orangeDimmer; colors.chartValue = colors.valueFill; } - colors.averageChartLine = this.theme().orangeDim; - colors.chartLabel = this.theme().contrastDim; + colors.averageChartLine = theme.orangeDim; + colors.chartLabel = theme.contrastDim; break; case "purple": if (this.runtime.options()?.trackAgainstAverage) { - colors.valueLine = this.theme().purpleDimmer; - colors.valueFill = this.theme().purpleDimmer; - colors.averageLine = this.theme().purple; - colors.averageFill = this.theme().purple; + colors.valueLine = theme.purpleDimmer; + colors.valueFill = theme.purpleDimmer; + colors.averageLine = theme.purple; + colors.averageFill = theme.purple; colors.chartValue = colors.averageLine; } else { - colors.valueLine = this.theme().purple; - colors.valueFill = this.theme().purple; - colors.averageLine = this.theme().purpleDimmer; - colors.averageFill = this.theme().purpleDimmer; + colors.valueLine = theme.purple; + colors.valueFill = theme.purple; + colors.averageLine = theme.purpleDimmer; + colors.averageFill = theme.purpleDimmer; colors.chartValue = colors.valueFill; } - colors.averageChartLine = this.theme().purpleDim; - colors.chartLabel = this.theme().contrastDim; + colors.averageChartLine = theme.purpleDim; + colors.chartLabel = theme.contrastDim; break; case "grey": if (this.runtime.options()?.trackAgainstAverage) { - colors.valueLine = this.theme().greyDimmer; - colors.valueFill = this.theme().greyDimmer; - colors.averageLine = this.theme().grey; - colors.averageFill = this.theme().grey; + colors.valueLine = theme.greyDimmer; + colors.valueFill = theme.greyDimmer; + colors.averageLine = theme.grey; + colors.averageFill = theme.grey; colors.chartValue = colors.averageLine; } else { - colors.valueLine = this.theme().grey; - colors.valueFill = this.theme().grey; - colors.averageLine = this.theme().greyDimmer; - colors.averageFill = this.theme().greyDimmer; + colors.valueLine = theme.grey; + colors.valueFill = theme.grey; + colors.averageLine = theme.greyDimmer; + colors.averageFill = theme.greyDimmer; colors.chartValue = colors.valueFill; } - colors.averageChartLine = this.theme().greyDim; - colors.chartLabel = this.theme().contrastDim; + colors.averageChartLine = theme.greyDim; + colors.chartLabel = theme.contrastDim; break; case "yellow": if (this.runtime.options()?.trackAgainstAverage) { - colors.valueLine = this.theme().yellowDimmer; - colors.valueFill = this.theme().yellowDimmer; - colors.averageLine = this.theme().yellow; - colors.averageFill = this.theme().yellow; + colors.valueLine = theme.yellowDimmer; + colors.valueFill = theme.yellowDimmer; + colors.averageLine = theme.yellow; + colors.averageFill = theme.yellow; colors.chartValue = colors.averageLine; } else { - colors.valueLine = this.theme().yellow; - colors.valueFill = this.theme().yellow; - colors.averageLine = this.theme().yellowDimmer; - colors.averageFill = this.theme().yellowDimmer; + colors.valueLine = theme.yellow; + colors.valueFill = theme.yellow; + colors.averageLine = theme.yellowDimmer; + colors.averageFill = theme.yellowDimmer; colors.chartValue = colors.valueFill; } - colors.averageChartLine = this.theme().yellowDim; - colors.chartLabel = this.theme().contrastDim; + colors.averageChartLine = theme.yellowDim; + colors.chartLabel = theme.contrastDim; break; } return colors; } - private getUnitsLabel(): string { - let label: string = null; + private getUnitsLabel(): string | null | undefined { + let label: string | null | undefined = null; const unit = this.runtime.options()?.convertUnitTo; switch (unit) { @@ -752,14 +771,16 @@ export class WidgetDataChartComponent implements OnDestroy { if (!cfg?.datachartPath) return; this.streamSub?.unsubscribe(); + // Always set by rebuildForDataset() just before startStreaming() is called (its only caller). const info = this.dataSourceInfo; + if (!info) return; const params: IHistoryChartStreamParams = { path: cfg.datachartPath, source: cfg.datachartSource ?? 'default', angleDomainOverride: cfg.datachartAngleRange === 'signed' || cfg.datachartAngleRange === 'direction' ? cfg.datachartAngleRange : undefined, - windowMs: resolveWindowMs(cfg.timeScale as TimeScaleFormat, cfg.period), + windowMs: resolveWindowMs(cfg.timeScale as TimeScaleFormat, cfg.period ?? 10), sampleTime: info.sampleTime, maxDataPoints: info.maxDataPoints, smoothingPeriod: info.smoothingPeriod @@ -801,31 +822,37 @@ export class WidgetDataChartComponent implements OnDestroy { } private applyTitleAndAnnotationValues(point: IDatasetServiceDatapoint, cfg: IWidgetSvcConfig): void { + const unit = cfg.convertUnitTo ?? ''; const trackValue: number = cfg.trackAgainstAverage ? (point.data.sma ?? point.data.value) : point.data.value; - const convertedTrack = this.unitsService.convertToUnit(cfg.convertUnitTo, trackValue); - if (Number.isFinite(convertedTrack)) { - this.chart.options.plugins.title.text = `${convertedTrack.toFixed(cfg.numDecimal)} ${this.getUnitsLabel()} `; + const convertedTrack = this.unitsService.convertToUnit(unit, trackValue); + if (convertedTrack !== null && Number.isFinite(convertedTrack)) { + const titlePlugin = this.chart.options.plugins?.title; + if (titlePlugin) { + titlePlugin.text = `${convertedTrack.toFixed(cfg.numDecimal)} ${this.getUnitsLabel()} `; + } } - const lastAverage = this.unitsService.convertToUnit(cfg.convertUnitTo, point.data.lastAverage); - const lastMinimum = this.unitsService.convertToUnit(cfg.convertUnitTo, point.data.lastMinimum); - const lastMaximum = this.unitsService.convertToUnit(cfg.convertUnitTo, point.data.lastMaximum); + // A missing rolling stat (insufficient history yet) converts like the pre-existing code's + // implicit `+undefined` did: NaN in, NaN out, so the finite-checks below skip it exactly as before. + const lastAverage = this.unitsService.convertToUnit(unit, point.data.lastAverage ?? NaN); + const lastMinimum = this.unitsService.convertToUnit(unit, point.data.lastMinimum ?? NaN); + const lastMaximum = this.unitsService.convertToUnit(unit, point.data.lastMaximum ?? NaN); const plugins = this.chart.options.plugins as unknown as AnnPlugin; const ann = plugins.annotation?.annotations; if (!ann) return; - if (Number.isFinite(lastAverage) && ann.averageLine?.value !== lastAverage) { + if (lastAverage !== null && Number.isFinite(lastAverage) && ann.averageLine?.value !== lastAverage) { ann.averageLine.value = lastAverage; ann.averageLine.label.content = `${lastAverage.toFixed(cfg.numDecimal)}`; } - if (Number.isFinite(lastMinimum) && ann.minimumLine?.value !== lastMinimum) { + if (lastMinimum !== null && Number.isFinite(lastMinimum) && ann.minimumLine?.value !== lastMinimum) { ann.minimumLine.value = lastMinimum; ann.minimumLine.label.content = `${lastMinimum.toFixed(cfg.numDecimal)}`; } - if (Number.isFinite(lastMaximum) && ann.maximumLine?.value !== lastMaximum) { + if (lastMaximum !== null && Number.isFinite(lastMaximum) && ann.maximumLine?.value !== lastMaximum) { ann.maximumLine.value = lastMaximum; ann.maximumLine.label.content = `${lastMaximum.toFixed(cfg.numDecimal)}`; } @@ -833,9 +860,10 @@ export class WidgetDataChartComponent implements OnDestroy { private transformDatasetRows(rows: IDatasetServiceDatapoint[], datasetType): IDataSetRow[] { const cfg = this.runtime.options(); - const convert = (v: number) => this.unitsService.convertToUnit(cfg.convertUnitTo, v); + if (!cfg) return []; + const convert = (v: number) => this.unitsService.convertToUnit(cfg.convertUnitTo ?? '', v); const verticalChart = cfg.verticalChart; - const avgKey = cfg.datasetAverageArray; + const avgKey = cfg.datasetAverageArray ?? 'sma'; return rows.map(row => { if (verticalChart) { diff --git a/src/app/widgets/widget-windtrends-chart/widget-windtrends-chart.component.ts b/src/app/widgets/widget-windtrends-chart/widget-windtrends-chart.component.ts index 0576cba2..055ee87b 100644 --- a/src/app/widgets/widget-windtrends-chart/widget-windtrends-chart.component.ts +++ b/src/app/widgets/widget-windtrends-chart/widget-windtrends-chart.component.ts @@ -24,13 +24,13 @@ const WINDTRENDS_TWD_PATH = 'self.environment.wind.directionTrue'; const WINDTRENDS_TWS_PATH = 'self.environment.wind.speedTrue'; interface IChartColors { - valueLine: string, - valueFill: string, - averageLine: string, - averageFill: string, - averageChartLine: string, - chartLabel: string, - chartValue: string + valueLine: string | null, + valueFill: string | null, + averageLine: string | null, + averageFill: string | null, + averageChartLine: string | null, + chartLabel: string | null, + chartValue: string | null } interface IDataSetRow { x: number, @@ -67,7 +67,7 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { public lineChartData: ChartData<'line', { x: number, y: number }[]> = { datasets: [] }; - public lineChartOptions: ChartConfiguration['options'] = { + public lineChartOptions: NonNullable = { parsing: false, datasets: { line: { @@ -84,12 +84,12 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { } public lineChartType: ChartType = 'line'; private chart: Chart; - private _dsDirectionSub: Subscription = null; - private _dsSpeedSub: Subscription = null; + private _dsDirectionSub: Subscription | null = null; + private _dsSpeedSub: Subscription | null = null; /** Pending coalesced chart recompute+repaint frame id (one per animation frame across both streams). */ private _chartUpdateRafId: number | null = null; - private timeScale: TimeScaleFormat = null; - private dataSourceInfo: IChartDataSourceInfo = null; + private timeScale: TimeScaleFormat | null = null; + private dataSourceInfo: IChartDataSourceInfo | null = null; private xCenter: number | null = null; private xStep: number | null = null; private xCenterSpeed: number | null = null; @@ -123,6 +123,7 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { if (!area) return; const ctx = chart.ctx as CanvasRenderingContext2D; const theme = this.theme(); + if (!theme) return; ctx.save(); ctx.globalCompositeOperation = 'destination-over'; ctx.fillStyle = theme.background; @@ -137,6 +138,7 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { const ctx = chart.ctx as CanvasRenderingContext2D; const area = chart.chartArea as ChartArea; const theme = this.theme(); + if (!theme) return; const chartColors = this.getThemeColors(); const def = Chart.defaults.font; const scaleMap = chart.scales as Record; @@ -242,7 +244,7 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { if (typeof lastSpeed === 'number' && isFinite(lastSpeed)) { ctx.save(); - ctx.fillStyle = chartColors.chartValue; + ctx.fillStyle = chartColors.chartValue ?? ctx.fillStyle; // Draw speed value centered ctx.font = this.isPhonePortrait().matches ? `bold ${this.SPEED_VALUE_PHONE_PORTRAIT_FONT_SIZE}px ${def.family}` : `bold ${this.SPEED_VALUE_FONT_SIZE}px ${def.family}`; ctx.textAlign = 'center'; @@ -268,7 +270,7 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { if (typeof lastDir === 'number' && isFinite(lastDir)) { const dir = this.normalizeAngle(lastDir); ctx.save(); - ctx.fillStyle = chartColors.chartValue; + ctx.fillStyle = chartColors.chartValue ?? ctx.fillStyle; ctx.font = this.isPhonePortrait().matches ? `bold ${this.SPEED_VALUE_PHONE_PORTRAIT_FONT_SIZE}px ${def.family}` : `bold ${this.SPEED_VALUE_FONT_SIZE}px ${def.family}`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; @@ -296,7 +298,7 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { ctx.restore(); // Text over everything ctx.save(); - ctx.fillStyle = this.getThemeColors().chartLabel; + ctx.fillStyle = this.getThemeColors().chartLabel ?? ctx.fillStyle; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.font = `bold 18px ${def.family}`; @@ -307,7 +309,7 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { }; constructor() { - this.isPhonePortrait = toSignal(this.responsive.observe(Breakpoints.HandsetPortrait)); + this.isPhonePortrait = toSignal(this.responsive.observe(Breakpoints.HandsetPortrait), { initialValue: { matches: false, breakpoints: {} } }); // Theme or config color changes -> restyle chart effect(() => { const theme = this.theme(); @@ -338,15 +340,17 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { private startWidget(): void { // Guard until canvas view is ready - if (!this.widgetDataChart()) return; + const widgetDataChartRef = this.widgetDataChart(); + if (!widgetDataChartRef) return; const cfg = this.runtime?.options(); if (!cfg || !cfg.timeScale) return; - this.timeScale = cfg.timeScale as TimeScaleFormat; - this.dataSourceInfo = deriveDataSourceInfo(resolveWindowMs(this.timeScale, WINDTRENDS_PERIOD)); + const timeScale = cfg.timeScale as TimeScaleFormat; + this.timeScale = timeScale; + this.dataSourceInfo = deriveDataSourceInfo(resolveWindowMs(timeScale, WINDTRENDS_PERIOD)); this.createDatasets(); this.setChartOptions(); if (!this.chart) { - this.chart = new Chart(this.widgetDataChart().nativeElement.getContext('2d'), { + this.chart = new Chart(widgetDataChartRef.nativeElement.getContext('2d'), { type: this.lineChartType, data: this.lineChartData, options: this.lineChartOptions, @@ -360,6 +364,10 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { } private setChartOptions() { + const theme = this.theme(); + const dataSourceInfo = this.dataSourceInfo; + if (!theme || !dataSourceInfo) return; + this.lineChartOptions.maintainAspectRatio = false; this.lineChartOptions.animation = false; @@ -412,7 +420,7 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { }, grid: { display: true, - color: this.theme().contrastDimmer + color: theme.contrastDimmer } }, x: { @@ -455,7 +463,7 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { const tickVal = (ctx as unknown as { tick?: { value: number } }).tick?.value ?? Number.NaN; const center = this.xCenter ?? Number.NaN; const isCenter = this.nearlyEqual(tickVal, center); - return isCenter ? this.theme().contrast : undefined; + return isCenter ? this.theme()?.contrast : undefined; }, }, grid: { @@ -467,7 +475,7 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { const sAny = scale as unknown as { min?: number }; const min = (scales?.x?.min as number | undefined) ?? sAny.min; const isMin = this.nearlyEqual(tickVal, min as number); - return isMin ? 'rgba(0,0,0,0)' : this.theme().contrastDimmer; + return isMin ? 'rgba(0,0,0,0)' : this.theme()?.contrastDimmer; }, lineWidth: 1 } @@ -512,7 +520,7 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { const tickVal = (ctx as unknown as { tick?: { value: number } }).tick?.value ?? Number.NaN; const center = this.xCenterSpeed ?? Number.NaN; const isCenter = this.nearlyEqual(tickVal, center); - return isCenter ? this.theme().contrast : undefined; + return isCenter ? this.theme()?.contrast : undefined; }, }, grid: { @@ -524,7 +532,7 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { const sAny = scale as unknown as { max?: number }; const max = (scales?.xSpeed?.max as number | undefined) ?? sAny.max; const isMax = this.nearlyEqual(tickVal, max as number); - return isMax ? 'rgba(0,0,0,0)' : this.theme().contrastDimmer; + return isMax ? 'rgba(0,0,0,0)' : this.theme()?.contrastDimmer; }, lineWidth: 1 } @@ -536,7 +544,7 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { display: true, align: "end", text: `TWD `, - color: this.getThemeColors().chartLabel, + color: this.getThemeColors().chartLabel ?? undefined, padding: this.isPhonePortrait().matches ? { top: 3, bottom: 0 } : { top: 3, bottom: 0 }, font: this.isPhonePortrait().matches ? { size: 16, weight: 'normal' } : { size: 35, weight: 'normal' } }, @@ -544,15 +552,15 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { display: true, align: "start", text: ` TWS`, - color: this.getThemeColors().chartLabel, + color: this.getThemeColors().chartLabel ?? undefined, padding: this.isPhonePortrait().matches ? { top: -18, bottom: 12 } : { top: -41, bottom: 12 }, font: this.isPhonePortrait().matches ? { size: 16 } : { size: 35 } }, legend: { display: false }, streaming: { - duration: this.dataSourceInfo.maxDataPoints * this.dataSourceInfo.sampleTime, - delay: this.dataSourceInfo.sampleTime, + duration: dataSourceInfo.maxDataPoints * dataSourceInfo.sampleTime, + delay: dataSourceInfo.sampleTime, frameRate: this.timeScale === "day" ? 5 : this.timeScale === "hour" ? 8 : this.timeScale === "minute" ? 15 : 30, } } @@ -596,7 +604,7 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { borderColor: (context) => { const chart = context.chart as Chart; const { ctx } = chart; - return this.lineGradientForAxis(ctx, chart, 'x'); + return this.lineGradientForAxis(ctx, chart, 'x') ?? undefined; }, backgroundColor: 'red', xAxisID: 'x' @@ -665,7 +673,7 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { borderColor: (context) => { const chart = context.chart as Chart; const { ctx } = chart; - return this.lineGradientForAxis(ctx, chart, 'xSpeed'); + return this.lineGradientForAxis(ctx, chart, 'xSpeed') ?? undefined; }, xAxisID: 'xSpeed' }, @@ -712,10 +720,12 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { this._dsDirectionSub?.unsubscribe(); this._dsSpeedSub?.unsubscribe(); + const timeScale = this.timeScale; const info = this.dataSourceInfo; + if (!timeScale || !info) return; const baseParams = { source: 'default', - windowMs: resolveWindowMs(this.timeScale, WINDTRENDS_PERIOD), + windowMs: resolveWindowMs(timeScale, WINDTRENDS_PERIOD), sampleTime: info.sampleTime, maxDataPoints: info.maxDataPoints, smoothingPeriod: info.smoothingPeriod @@ -730,7 +740,7 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { this.pushRowsToDatasets(emission); } else { this.pushRowsToDatasets([emission]); - if (this.chart.data.datasets[0].data.length > this.dataSourceInfo.maxDataPoints) { + if (this.chart.data.datasets[0].data.length > info.maxDataPoints) { for (let i = 0; i <= 4; i++) this.chart.data.datasets[i].data.shift(); } } @@ -743,7 +753,7 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { this.pushRowsToSpeedDatasets(emission); } else { this.pushRowsToSpeedDatasets([emission]); - if (this.chart.data.datasets[5].data.length > this.dataSourceInfo.maxDataPoints) { + if (this.chart.data.datasets[5].data.length > info.maxDataPoints) { for (let i = 5; i <= 9; i++) this.chart.data.datasets[i].data.shift(); } } @@ -754,21 +764,25 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { private unwrapAngles(degrees: (number | null)[]): (number | null)[] { if (degrees.length === 0) return []; const unwrapped: (number | null)[] = []; - let prev = null; + let prev: number | null = null; + let lastUnwrapped: number | null = null; for (const val of degrees) { if (val == null) { unwrapped.push(null); continue; } - if (prev == null) { + if (prev == null || lastUnwrapped == null) { unwrapped.push(val); prev = val; + lastUnwrapped = val; continue; } let delta = val - prev; if (delta > 180) delta -= 360; if (delta < -180) delta += 360; - unwrapped.push(unwrapped[unwrapped.length - 1]! + delta); + const next = lastUnwrapped + delta; + unwrapped.push(next); + lastUnwrapped = next; prev = val; } return unwrapped; @@ -859,16 +873,13 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { const dirAvgArr = this.chart.data.datasets[2]?.data as IDataSetRow[] | undefined; const dirSmaArr = this.chart.data.datasets[1]?.data as IDataSetRow[] | undefined; const dirValArr = this.chart.data.datasets[0]?.data as IDataSetRow[] | undefined; - const hasAvg = !!dirAvgArr?.length; - const hasSma = !!dirSmaArr?.length; - const hasVal = !!dirValArr?.length; // Center MUST be the latest lastAverage value when available, else fall back to SMA, else Value - const centerVal = hasAvg - ? dirAvgArr![dirAvgArr!.length - 1].x - : hasSma - ? dirSmaArr![dirSmaArr!.length - 1].x - : hasVal - ? dirValArr![dirValArr!.length - 1].x + const centerVal = dirAvgArr?.length + ? dirAvgArr[dirAvgArr.length - 1].x + : dirSmaArr?.length + ? dirSmaArr[dirSmaArr.length - 1].x + : dirValArr?.length + ? dirValArr[dirValArr.length - 1].x : undefined; if (typeof centerVal === 'number' && isFinite(centerVal)) { // Try to use lastMinimum/Maximum if available; else derive half-range from recent window using angular distance @@ -882,10 +893,10 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { ? Math.max(Number.isFinite(minDiff) ? minDiff : 0, Number.isFinite(maxDiff) ? maxDiff : 0) : Number.NaN; if (!Number.isFinite(halfRange)) { - const src = hasAvg ? dirAvgArr! : hasSma ? dirSmaArr! : dirValArr!; - const take = Math.min(30, src?.length ?? 0); - if (take > 1) { - const slice = src!.slice(src!.length - take); + const src = dirAvgArr?.length ? dirAvgArr : dirSmaArr?.length ? dirSmaArr : dirValArr; + if (src && src.length > 1) { + const take = Math.min(30, src.length); + const slice = src.slice(src.length - take); const xs = slice.map(p => p.x).filter(v => typeof v === 'number' && isFinite(v)) as number[]; if (xs.length) { halfRange = xs.reduce((m, v) => Math.max(m, this.angularDiff(centerVal, v)), 0); @@ -902,9 +913,9 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { // Place center exactly at lastAverage const xMin = centerVal - 2 * dStep; const xMax = centerVal + 2 * dStep; - this.chart.options.scales.x.min = xMin; - this.chart.options.scales.x.max = xMax; - const xScale = this.chart.options.scales as unknown as { x: { ticks?: { stepSize?: number } } }; + const xScale = this.chart.options.scales as unknown as { x: { min?: number; max?: number; ticks?: { stepSize?: number } } }; + xScale.x.min = xMin; + xScale.x.max = xMax; xScale.x.ticks = { ...(xScale.x.ticks ?? {}), stepSize: dStep }; // Cache for tick styling @@ -954,9 +965,6 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { p.y = Math.max(0, Math.min(windowMs, nowTs - ts)); }); }); - // Lock y scale to [0, window] - this.chart.options.scales.y.min = 0; - this.chart.options.scales.y.max = windowMs; // Explicit step per selected window let step: number; const fmt = this.timeScale; @@ -975,7 +983,10 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { step = windowMs / 5; break; } - const yScale = this.chart.options.scales as unknown as { y: { ticks?: { stepSize?: number; count?: number } } }; + const yScale = this.chart.options.scales as unknown as { y: { min?: number; max?: number; ticks?: { stepSize?: number; count?: number } } }; + // Lock y scale to [0, window] + yScale.y.min = 0; + yScale.y.max = windowMs; const ticksCopy = { ...(yScale.y.ticks ?? {}) } as { stepSize?: number; count?: number }; delete ticksCopy.count; ticksCopy.stepSize = step; @@ -983,7 +994,7 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { } } - private getWindowMs(fmt: TimeScaleFormat | undefined): number { + private getWindowMs(fmt: TimeScaleFormat | null | undefined): number { switch (fmt) { case 'Last 30 Minutes': return 30 * 60_000; @@ -1001,6 +1012,8 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { if (!chartArea) return null; const scale = chart.scales?.[axisKey] as (Scale | undefined); if (!scale) return null; + const theme = this.theme(); + if (!theme) return null; const scales = chart.options?.scales as Record | undefined; const sAny = scale as unknown as { min?: number; max?: number }; const min = (scales?.[axisKey]?.min as number | undefined) ?? sAny.min; @@ -1010,28 +1023,28 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { const centerPx = scale.getPixelForValue(center); const offset = Math.max(0, Math.min(1, (centerPx - chartArea.left) / chartArea.width)); const gradient = ctx.createLinearGradient(chartArea.left, 0, chartArea.right, 0); - gradient.addColorStop(0, this.theme().port); - gradient.addColorStop(offset, this.theme().port); - gradient.addColorStop(offset, this.theme().starboard); - gradient.addColorStop(1, this.theme().starboard); + gradient.addColorStop(0, theme.port); + gradient.addColorStop(offset, theme.port); + gradient.addColorStop(offset, theme.starboard); + gradient.addColorStop(1, theme.starboard); return gradient; } private setDatasetsColors(): void { this.lineChartData.datasets.forEach((dataset) => { if (dataset.label === 'Value') { - dataset.borderColor = this.getThemeColors().valueLine; - dataset.backgroundColor = this.getThemeColors().valueFill; + dataset.borderColor = this.getThemeColors().valueLine ?? undefined; + dataset.backgroundColor = this.getThemeColors().valueFill ?? undefined; } if (dataset.label === 'Value Speed') { - dataset.borderColor = this.getThemeColors().valueLine; - dataset.backgroundColor = this.getThemeColors().valueFill; + dataset.borderColor = this.getThemeColors().valueLine ?? undefined; + dataset.backgroundColor = this.getThemeColors().valueFill ?? undefined; } }); } private getThemeColors(): IChartColors { - const widgetColor = this.runtime?.options()?.color; + const widgetColor = this.runtime?.options()?.color; const colors: IChartColors = { valueLine: null, valueFill: null, @@ -1042,78 +1055,81 @@ export class WidgetWindTrendsChartComponent implements OnDestroy { chartValue: null }; + const theme = this.theme(); + if (!theme) return colors; + switch (widgetColor) { case "contrast": - colors.valueLine = this.theme().contrastDim; - colors.valueFill = this.theme().contrastDimmer; - colors.averageLine = this.theme().contrast; - colors.averageFill = this.theme().contrast; - colors.chartValue = this.theme().contrast; - colors.averageChartLine = this.theme().contrast; - colors.chartLabel = this.theme().contrastDim; + colors.valueLine = theme.contrastDim; + colors.valueFill = theme.contrastDimmer; + colors.averageLine = theme.contrast; + colors.averageFill = theme.contrast; + colors.chartValue = theme.contrast; + colors.averageChartLine = theme.contrast; + colors.chartLabel = theme.contrastDim; break; case "blue": - colors.valueLine = this.theme().blueDim; - colors.valueFill = this.theme().blueDimmer; - colors.averageLine = this.theme().blue; - colors.averageFill = this.theme().blue; - colors.chartValue = this.theme().blue; - colors.averageChartLine = this.theme().blueDim; - colors.chartLabel = this.theme().blueDim; + colors.valueLine = theme.blueDim; + colors.valueFill = theme.blueDimmer; + colors.averageLine = theme.blue; + colors.averageFill = theme.blue; + colors.chartValue = theme.blue; + colors.averageChartLine = theme.blueDim; + colors.chartLabel = theme.blueDim; break; case "green": - colors.valueLine = this.theme().greenDim; - colors.valueFill = this.theme().greenDimmer; - colors.averageLine = this.theme().green; - colors.averageFill = this.theme().green; - colors.chartValue = this.theme().green; - colors.averageChartLine = this.theme().greenDim; - colors.chartLabel = this.theme().greenDim; + colors.valueLine = theme.greenDim; + colors.valueFill = theme.greenDimmer; + colors.averageLine = theme.green; + colors.averageFill = theme.green; + colors.chartValue = theme.green; + colors.averageChartLine = theme.greenDim; + colors.chartLabel = theme.greenDim; break; case "pink": - colors.valueLine = this.theme().pinkDim; - colors.valueFill = this.theme().pinkDimmer; - colors.averageLine = this.theme().pink; - colors.averageFill = this.theme().pink; - colors.chartValue = this.theme().pink; - colors.averageChartLine = this.theme().pinkDim; - colors.chartLabel = this.theme().pinkDim; + colors.valueLine = theme.pinkDim; + colors.valueFill = theme.pinkDimmer; + colors.averageLine = theme.pink; + colors.averageFill = theme.pink; + colors.chartValue = theme.pink; + colors.averageChartLine = theme.pinkDim; + colors.chartLabel = theme.pinkDim; break; case "orange": - colors.valueLine = this.theme().orangeDim; - colors.valueFill = this.theme().orangeDimmer; - colors.averageLine = this.theme().orange; - colors.averageFill = this.theme().orange; - colors.chartValue = this.theme().orange; - colors.averageChartLine = this.theme().orangeDim; - colors.chartLabel = this.theme().orangeDim; + colors.valueLine = theme.orangeDim; + colors.valueFill = theme.orangeDimmer; + colors.averageLine = theme.orange; + colors.averageFill = theme.orange; + colors.chartValue = theme.orange; + colors.averageChartLine = theme.orangeDim; + colors.chartLabel = theme.orangeDim; break; case "purple": - colors.valueLine = this.theme().purpleDim; - colors.valueFill = this.theme().purpleDimmer; - colors.averageLine = this.theme().purple; - colors.averageFill = this.theme().purple; - colors.chartValue = this.theme().purple; - colors.averageChartLine = this.theme().purpleDim; - colors.chartLabel = this.theme().purpleDim; + colors.valueLine = theme.purpleDim; + colors.valueFill = theme.purpleDimmer; + colors.averageLine = theme.purple; + colors.averageFill = theme.purple; + colors.chartValue = theme.purple; + colors.averageChartLine = theme.purpleDim; + colors.chartLabel = theme.purpleDim; break; case "grey": - colors.valueLine = this.theme().greyDim; - colors.valueFill = this.theme().greyDimmer; - colors.averageLine = this.theme().grey; - colors.averageFill = this.theme().grey; - colors.chartValue = this.theme().grey; - colors.averageChartLine = this.theme().greyDim; - colors.chartLabel = this.theme().greyDim; + colors.valueLine = theme.greyDim; + colors.valueFill = theme.greyDimmer; + colors.averageLine = theme.grey; + colors.averageFill = theme.grey; + colors.chartValue = theme.grey; + colors.averageChartLine = theme.greyDim; + colors.chartLabel = theme.greyDim; break; case "yellow": - colors.valueLine = this.theme().yellowDim; - colors.valueFill = this.theme().yellowDimmer; - colors.averageLine = this.theme().yellow; - colors.averageFill = this.theme().yellow; - colors.chartValue = this.theme().yellow; - colors.averageChartLine = this.theme().yellowDim; - colors.chartLabel = this.theme().yellowDim; + colors.valueLine = theme.yellowDim; + colors.valueFill = theme.yellowDimmer; + colors.averageLine = theme.yellow; + colors.averageFill = theme.yellow; + colors.chartValue = theme.yellow; + colors.averageChartLine = theme.yellowDim; + colors.chartLabel = theme.yellowDim; break; } return colors;