@firestitch/report 18.6.1 → 18.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/app/reports/components/component-chart/component-chart.component.d.ts +6 -7
  2. package/app/reports/components/component-kpi/component-kpi.component.d.ts +17 -9
  3. package/app/reports/components/component-kpi/components/sparkline/sparkline.component.d.ts +8 -0
  4. package/app/reports/components/component-list/component-list.component.d.ts +39 -10
  5. package/app/reports/components/component-note/component-note.component.d.ts +12 -0
  6. package/app/reports/components/report-canvas/report-canvas.component.d.ts +0 -10
  7. package/app/reports/components/report-component/report-component.component.d.ts +5 -12
  8. package/app/reports/data/report.data.d.ts +2 -3
  9. package/app/reports/dialogs/component-settings/component-settings.component.d.ts +34 -2
  10. package/app/reports/dialogs/list-columns/list-columns.component.d.ts +23 -0
  11. package/app/reports/dialogs/report-settings/report-locale-patch.d.ts +5 -0
  12. package/app/reports/dialogs/report-settings/report-settings.component.d.ts +20 -2
  13. package/app/reports/enums/report-change-polarity.enum.d.ts +5 -0
  14. package/app/reports/enums/report-chart-color.enum.d.ts +12 -0
  15. package/app/reports/enums/report-chart-data-labels.enum.d.ts +6 -0
  16. package/app/reports/enums/report-chart-date-label.enum.d.ts +5 -0
  17. package/app/reports/enums/report-chart-line-style.enum.d.ts +5 -0
  18. package/app/reports/enums/report-chart-mark-stat.enum.d.ts +6 -0
  19. package/app/reports/enums/report-kpi-trend.enum.d.ts +5 -0
  20. package/app/reports/enums/report-list-column-format.enum.d.ts +6 -0
  21. package/app/reports/enums/report-number-units.enum.d.ts +6 -0
  22. package/app/reports/enums/report-value-format.enum.d.ts +5 -0
  23. package/app/reports/export/offscreen-chart.d.ts +2 -2
  24. package/app/reports/export/report-export-collector.service.d.ts +37 -8
  25. package/app/reports/export/report-pdf.service.d.ts +6 -0
  26. package/app/reports/export/report-pptx.service.d.ts +24 -0
  27. package/app/reports/format.d.ts +64 -3
  28. package/app/reports/interfaces/report-chat-viewer-filter.interface.d.ts +2 -0
  29. package/app/reports/interfaces/report.interface.d.ts +205 -14
  30. package/app/reports/kpi-card.d.ts +48 -0
  31. package/app/reports/layout.d.ts +0 -9
  32. package/app/reports/list-config.d.ts +16 -0
  33. package/app/reports/option-builder.d.ts +76 -9
  34. package/app/reports/output-notes.d.ts +14 -0
  35. package/app/reports/pipes/report-date.pipe.d.ts +2 -1
  36. package/app/reports/pipes/report-number.pipe.d.ts +8 -0
  37. package/app/reports/pipes/report-output.pipe.d.ts +10 -0
  38. package/app/reports/report-filter-items.d.ts +5 -2
  39. package/app/reports/report-url-state.d.ts +4 -0
  40. package/app/reports/services/report-filter-state.service.d.ts +27 -2
  41. package/app/reports/theme/palette.d.ts +6 -0
  42. package/app/reports/theme/typography.d.ts +2 -0
  43. package/esm2022/app/reports/components/component-chart/component-chart.component.mjs +23 -20
  44. package/esm2022/app/reports/components/component-kpi/component-kpi.component.mjs +56 -29
  45. package/esm2022/app/reports/components/component-kpi/components/sparkline/sparkline.component.mjs +19 -0
  46. package/esm2022/app/reports/components/component-list/component-list.component.mjs +218 -32
  47. package/esm2022/app/reports/components/component-note/component-note.component.mjs +30 -0
  48. package/esm2022/app/reports/components/report-canvas/report-canvas.component.mjs +7 -61
  49. package/esm2022/app/reports/components/report-component/report-component.component.mjs +99 -100
  50. package/esm2022/app/reports/data/report.data.mjs +20 -7
  51. package/esm2022/app/reports/dialogs/component-settings/component-settings.component.mjs +133 -13
  52. package/esm2022/app/reports/dialogs/list-columns/list-columns.component.mjs +56 -0
  53. package/esm2022/app/reports/dialogs/report-settings/report-locale-patch.mjs +13 -0
  54. package/esm2022/app/reports/dialogs/report-settings/report-settings.component.mjs +138 -20
  55. package/esm2022/app/reports/enums/report-change-polarity.enum.mjs +11 -0
  56. package/esm2022/app/reports/enums/report-chart-color.enum.mjs +19 -0
  57. package/esm2022/app/reports/enums/report-chart-data-labels.enum.mjs +13 -0
  58. package/esm2022/app/reports/enums/report-chart-date-label.enum.mjs +13 -0
  59. package/esm2022/app/reports/enums/report-chart-line-style.enum.mjs +12 -0
  60. package/esm2022/app/reports/enums/report-chart-mark-stat.enum.mjs +12 -0
  61. package/esm2022/app/reports/enums/report-kpi-trend.enum.mjs +10 -0
  62. package/esm2022/app/reports/enums/report-list-column-format.enum.mjs +14 -0
  63. package/esm2022/app/reports/enums/report-number-units.enum.mjs +14 -0
  64. package/esm2022/app/reports/enums/report-value-format.enum.mjs +13 -0
  65. package/esm2022/app/reports/export/offscreen-chart.mjs +5 -4
  66. package/esm2022/app/reports/export/report-export-collector.service.mjs +186 -40
  67. package/esm2022/app/reports/export/report-pdf.service.mjs +332 -67
  68. package/esm2022/app/reports/export/report-pptx.service.mjs +765 -106
  69. package/esm2022/app/reports/format.mjs +917 -57
  70. package/esm2022/app/reports/interfaces/report-chat-viewer-filter.interface.mjs +1 -1
  71. package/esm2022/app/reports/interfaces/report.interface.mjs +1 -1
  72. package/esm2022/app/reports/kpi-card.mjs +174 -0
  73. package/esm2022/app/reports/layout.mjs +1 -46
  74. package/esm2022/app/reports/list-config.mjs +106 -0
  75. package/esm2022/app/reports/option-builder.mjs +1457 -117
  76. package/esm2022/app/reports/output-notes.mjs +158 -0
  77. package/esm2022/app/reports/pipes/report-date.pipe.mjs +7 -6
  78. package/esm2022/app/reports/pipes/report-number.pipe.mjs +25 -0
  79. package/esm2022/app/reports/pipes/report-output.pipe.mjs +28 -0
  80. package/esm2022/app/reports/report-filter-items.mjs +28 -3
  81. package/esm2022/app/reports/report-url-state.mjs +56 -15
  82. package/esm2022/app/reports/services/report-filter-state.service.mjs +274 -22
  83. package/esm2022/app/reports/theme/echarts.mjs +8 -2
  84. package/esm2022/app/reports/theme/palette.mjs +35 -1
  85. package/esm2022/app/reports/theme/typography.mjs +8 -1
  86. package/esm2022/app/reports/views/report/fs-ai-report.component.mjs +2 -2
  87. package/esm2022/app/reports/views/report/report.component.mjs +3 -4
  88. package/esm2022/public_api.mjs +10 -1
  89. package/fesm2022/{firestitch-report-echarts-Djtmaxj1.mjs → firestitch-report-echarts-qCBE3LqM.mjs} +9 -3
  90. package/fesm2022/firestitch-report-echarts-qCBE3LqM.mjs.map +1 -0
  91. package/fesm2022/{firestitch-report-firestitch-report-D3zseBZc.mjs → firestitch-report-firestitch-report-DbsWbE-j.mjs} +5523 -875
  92. package/fesm2022/firestitch-report-firestitch-report-DbsWbE-j.mjs.map +1 -0
  93. package/fesm2022/firestitch-report.mjs +1 -1
  94. package/package.json +1 -1
  95. package/public_api.d.ts +9 -0
  96. package/fesm2022/firestitch-report-echarts-Djtmaxj1.mjs.map +0 -1
  97. package/fesm2022/firestitch-report-firestitch-report-D3zseBZc.mjs.map +0 -1
@@ -0,0 +1,10 @@
1
+ import { PipeTransform } from '@angular/core';
2
+ import { FormattedOutputValue } from '../format';
3
+ import { ListColumn, ReportFormatContext } from '../interfaces/report.interface';
4
+ import { ListOutputColumn } from '../list-config';
5
+ import * as i0 from "@angular/core";
6
+ export declare class ReportOutputPipe implements PipeTransform {
7
+ transform(row: Record<string, unknown> | null | undefined, column: ListColumn, output: ListOutputColumn, context: ReportFormatContext): FormattedOutputValue;
8
+ static ɵfac: i0.ɵɵFactoryDeclaration<ReportOutputPipe, never>;
9
+ static ɵpipe: i0.ɵɵPipeDeclaration<ReportOutputPipe, "fsReportOutput", true>;
10
+ }
@@ -1,9 +1,9 @@
1
1
  import { IFilterConfigItem } from '@firestitch/filter';
2
2
  import { Observable } from 'rxjs';
3
3
  import { ReportData } from './data/report.data';
4
- import { ChartGranularity, FilterGroupValue, Report, ReportFilterGroup } from './interfaces/report.interface';
4
+ import { ChartGranularity, FilterGroupValue, Report, ReportFilterGroup, ReportLocaleTag } from './interfaces/report.interface';
5
5
  export declare function dateBoundString(value: Date | string | null | undefined): string | undefined;
6
- export declare function dateBoundLabel(value: Date | string | null | undefined): string | null;
6
+ export declare function dateBoundLabel(value: Date | string | null | undefined, locale: ReportLocaleTag): string | null;
7
7
  export declare function dateFromBound(value: string | null | undefined): Date | null;
8
8
  export declare function dateToBound(value: Date | null | undefined): string | null;
9
9
  export declare function relativeLabel(relative: string): string;
@@ -29,6 +29,9 @@ export declare function matchOptions(options$: Observable<{
29
29
  value: unknown;
30
30
  }[]>;
31
31
  export declare function filterItemForGroup(group: ReportFilterGroup, reportData: ReportData, reportId: number, initial?: FilterGroupValue): IFilterConfigItem;
32
+ export declare const LIST_SEARCH_ITEM = "listSearch";
33
+ export declare const LIST_SEARCH_MAX_LENGTH = 100;
34
+ export declare function listSearchItem(value: string | null): IFilterConfigItem;
32
35
  export declare function hasTimeSeriesChart(report: Report | null): boolean;
33
36
  export declare function isGranularityOffered(report: Report, granularity: ChartGranularity | null): boolean;
34
37
  export declare function groupByFilterItem(report: Report, initial?: ChartGranularity | null): IFilterConfigItem | null;
@@ -3,8 +3,12 @@ import { FilterGroupValue, ReportFilterType, ReportSort, ReportUrlParams, Report
3
3
  export { GROUP_PARAM_PREFIX } from './report-filter-items';
4
4
  export declare const LIST_SORT_PARAM_PREFIX = "s";
5
5
  export declare const DATE_RANGE_SEPARATOR = "..";
6
+ export declare const LIST_SEARCH_PARAM_PREFIX = "q";
7
+ export declare const LIST_HIDDEN_PARAM_PREFIX = "h";
6
8
  export declare const OWNED_PARAM: RegExp;
7
9
  export declare function readReportUrlParams(params: ParamMap): ReportUrlParams;
10
+ export declare function encodeColumnList(names: readonly string[]): string;
11
+ export declare function decodeColumnList(raw: string): string[];
8
12
  export declare function decodeFilterValue(type: ReportFilterType, raw: string, resolveRelative: (name: string) => {
9
13
  start: Date;
10
14
  end: Date;
@@ -1,11 +1,22 @@
1
1
  import { Observable } from 'rxjs';
2
2
  import { ReportChatViewerFilter } from '../interfaces/report-chat-viewer-filter.interface';
3
- import { ChartGranularity, FilterGroupDefault, FilterGroupValue, Report, ReportComponent, ReportFilterGroup, ReportSort, ReportUrlParams, ReportUrlSnapshot, ResolvedFilterValue } from '../interfaces/report.interface';
3
+ import { ChartGranularity, FilterGroupDefault, FilterGroupValue, Report, ReportComponent, ReportFilterGroup, ReportFormatContext, ReportSort, ReportUrlParams, ReportUrlSnapshot, ResolvedFilterValue } from '../interfaces/report.interface';
4
4
  import * as i0 from "@angular/core";
5
5
  export declare function sameDefault(a: FilterGroupDefault | null, b: FilterGroupDefault | null): boolean;
6
+ export declare function sameFilterValue(a: FilterGroupValue | null | undefined, b: FilterGroupValue | null | undefined): boolean;
7
+ export declare function movedGroupValues(lastReported: Map<number, FilterGroupValue | null>, reported: {
8
+ groupId: number;
9
+ value: FilterGroupValue | null;
10
+ }[]): {
11
+ groupId: number;
12
+ value: FilterGroupValue | null;
13
+ }[];
14
+ export declare function listSearchText(text: string): string;
6
15
  export declare function defaultSignature(group: ReportFilterGroup): string;
7
16
  export declare class ReportFilterStateService {
8
17
  readonly timezone: import("@angular/core").WritableSignal<string>;
18
+ readonly formatContext: import("@angular/core").WritableSignal<ReportFormatContext>;
19
+ readonly listHidden: import("@angular/core").WritableSignal<ReadonlyMap<number, readonly string[]>>;
9
20
  private _values;
10
21
  private _groups;
11
22
  private _sessionDisabled;
@@ -13,6 +24,10 @@ export declare class ReportFilterStateService {
13
24
  private _frequency;
14
25
  private _frequencyChanged$;
15
26
  private _listSorts;
27
+ private _listSearches;
28
+ private _lists;
29
+ private _serverSearches;
30
+ private _listSearchChanged$;
16
31
  private _changed$;
17
32
  private _urlChanged$;
18
33
  init(report: Report, restore?: ReportUrlParams | null): void;
@@ -26,7 +41,13 @@ export declare class ReportFilterStateService {
26
41
  setFrequency(frequency: ChartGranularity | null): void;
27
42
  frequencyChanges(): Observable<void>;
28
43
  listSort(componentId: number): ReportSort | null;
29
- setListSort(componentId: number, sort: ReportSort | null): void;
44
+ setListSort(component: ReportComponent, sort: ReportSort | null): void;
45
+ listSearch(componentId: number): string | null;
46
+ listSearchable(component: ReportComponent): boolean;
47
+ setListSearch(componentId: number, text: string): void;
48
+ hiddenColumns(componentId: number): readonly string[];
49
+ setHiddenColumns(componentId: number, names: readonly string[]): void;
50
+ listSearchChanges(componentId: number): Observable<void>;
30
51
  urlChanges(): Observable<void>;
31
52
  urlSnapshot(report: Report): ReportUrlSnapshot;
32
53
  isFilterDisabled(filterId: number): boolean;
@@ -36,10 +57,14 @@ export declare class ReportFilterStateService {
36
57
  viewerFilters(): ReportChatViewerFilter[];
37
58
  resolveForReport(report: Report, pageId?: number | null): ResolvedFilterValue[];
38
59
  private _resolvedFields;
60
+ private _livePreset;
39
61
  private _seed;
40
62
  private _restore;
41
63
  private _restoreGroups;
42
64
  private _restoreListSorts;
65
+ private _restoreListSearches;
66
+ private _restoreListHidden;
67
+ private _dropUnsearchable;
43
68
  private _prune;
44
69
  private _withPreset;
45
70
  private _isDefault;
@@ -1,4 +1,10 @@
1
+ import { ReportChangePolarity } from '../enums/report-change-polarity.enum';
2
+ import { ReportChartColor } from '../enums/report-chart-color.enum';
1
3
  import { WaterfallStepKind } from '../interfaces/report.interface';
2
4
  export declare const REPORT_CHART_COLORS: string[];
3
5
  export declare const REPORT_CHART_COLORS_CSS: string[];
6
+ export declare const REPORT_CHART_COLOR_BY_NAME: Record<ReportChartColor, string>;
7
+ export declare const REPORT_REFERENCE_COLOR = "52606d";
4
8
  export declare const REPORT_WATERFALL_COLORS: Record<WaterfallStepKind, string>;
9
+ export declare const REPORT_KPI_CHANGE_COLORS: Record<ReportChangePolarity, string>;
10
+ export declare const REPORT_KPI_SPARKLINE_COLOR: string;
@@ -1,2 +1,4 @@
1
1
  export declare const REPORT_CHART_FONT_FAMILY = "Roboto, \"Helvetica Neue\", sans-serif";
2
2
  export declare const REPORT_AXIS_LABEL_FONT_SIZE = 11;
3
+ export declare const REPORT_DATA_LABEL_FONT_SIZE = 10;
4
+ export declare const REPORT_KPI_VALUE_FONT_SIZE = 34;
@@ -1,21 +1,27 @@
1
- import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
1
+ import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';
2
2
  import { NgxEchartsDirective } from 'ngx-echarts';
3
3
  import { buildChartOption } from '../../option-builder';
4
+ import { ReportFilterStateService } from '../../services/report-filter-state.service';
5
+ import { ComponentNoteComponent } from '../component-note/component-note.component';
4
6
  import * as i0 from "@angular/core";
5
7
  // ECharts host for a chart component. Pure rendering: the parent owns the
6
- // data fetch; this maps (component config, rows) → option and exposes the
7
- // chart instance for resize + the future image exports (getDataURL needs the
8
- // canvas renderer registered in theme/echarts.ts).
8
+ // data fetch; this maps (component config, rows, the open report's format
9
+ // context) → option and exposes the chart instance for resize + the future
10
+ // image exports (getDataURL needs the canvas renderer registered in
11
+ // theme/echarts.ts). The shared note under the plot names the days a
12
+ // comparison compares and why windowed series read as they do; the series'
13
+ // tooltips describe the windows themselves.
9
14
  export class ComponentChartComponent {
10
- component;
11
- data = null;
12
- options = null;
13
- _chart = null;
14
- ngOnChanges() {
15
- this.options = this.component && this.data
16
- ? buildChartOption(this.component, this.data)
15
+ component = input.required();
16
+ data = input(null);
17
+ options = computed(() => {
18
+ const data = this.data();
19
+ return data
20
+ ? buildChartOption(this.component(), data, this._filterState.formatContext())
17
21
  : null;
18
- }
22
+ });
23
+ _chart = null;
24
+ _filterState = inject(ReportFilterStateService);
19
25
  onChartInit(chart) {
20
26
  this._chart = chart;
21
27
  }
@@ -32,16 +38,13 @@ export class ComponentChartComponent {
32
38
  }) ?? null;
33
39
  }
34
40
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ComponentChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
35
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: ComponentChartComponent, isStandalone: true, selector: "app-report-component-chart", inputs: { component: "component", data: "data" }, usesOnChanges: true, ngImport: i0, template: "<div\n class=\"chart-host\"\n echarts\n [options]=\"options ?? {}\"\n [theme]=\"'report'\"\n [autoResize]=\"true\"\n (chartInit)=\"onChartInit($event)\">\n</div>\n", styles: [":host{display:block;width:100%;height:100%}.chart-host{width:100%;height:100%}\n"], dependencies: [{ kind: "directive", type: NgxEchartsDirective, selector: "echarts, [echarts]", inputs: ["options", "theme", "initOpts", "merge", "autoResize", "loading", "loadingType", "loadingOpts"], outputs: ["chartInit", "optionsError", "chartClick", "chartDblClick", "chartMouseDown", "chartMouseMove", "chartMouseUp", "chartMouseOver", "chartMouseOut", "chartGlobalOut", "chartContextMenu", "chartHighlight", "chartDownplay", "chartSelectChanged", "chartLegendSelectChanged", "chartLegendSelected", "chartLegendUnselected", "chartLegendLegendSelectAll", "chartLegendLegendInverseSelect", "chartLegendScroll", "chartDataZoom", "chartDataRangeSelected", "chartGraphRoam", "chartGeoRoam", "chartTreeRoam", "chartTimelineChanged", "chartTimelinePlayChanged", "chartRestore", "chartDataViewChanged", "chartMagicTypeChanged", "chartGeoSelectChanged", "chartGeoSelected", "chartGeoUnselected", "chartAxisAreaSelected", "chartBrush", "chartBrushEnd", "chartBrushSelected", "chartGlobalCursorTaken", "chartRendered", "chartFinished"], exportAs: ["echarts"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
41
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.14", type: ComponentChartComponent, isStandalone: true, selector: "app-report-component-chart", inputs: { component: { classPropertyName: "component", publicName: "component", isSignal: true, isRequired: true, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div\n class=\"chart-host\"\n echarts\n [options]=\"options() ?? {}\"\n [theme]=\"'report'\"\n [autoResize]=\"true\"\n (chartInit)=\"onChartInit($event)\">\n</div>\n<app-report-component-note [data]=\"data()\"></app-report-component-note>\n", styles: [":host{display:flex;flex-direction:column;width:100%;height:100%}.chart-host{flex:1 1 0;width:100%;min-height:0}\n"], dependencies: [{ kind: "component", type: ComponentNoteComponent, selector: "app-report-component-note", inputs: ["data", "windows", "windowDescriptions", "windowNotes"] }, { kind: "directive", type: NgxEchartsDirective, selector: "echarts, [echarts]", inputs: ["options", "theme", "initOpts", "merge", "autoResize", "loading", "loadingType", "loadingOpts"], outputs: ["chartInit", "optionsError", "chartClick", "chartDblClick", "chartMouseDown", "chartMouseMove", "chartMouseUp", "chartMouseOver", "chartMouseOut", "chartGlobalOut", "chartContextMenu", "chartHighlight", "chartDownplay", "chartSelectChanged", "chartLegendSelectChanged", "chartLegendSelected", "chartLegendUnselected", "chartLegendLegendSelectAll", "chartLegendLegendInverseSelect", "chartLegendScroll", "chartDataZoom", "chartDataRangeSelected", "chartGraphRoam", "chartGeoRoam", "chartTreeRoam", "chartTimelineChanged", "chartTimelinePlayChanged", "chartRestore", "chartDataViewChanged", "chartMagicTypeChanged", "chartGeoSelectChanged", "chartGeoSelected", "chartGeoUnselected", "chartAxisAreaSelected", "chartBrush", "chartBrushEnd", "chartBrushSelected", "chartGlobalCursorTaken", "chartRendered", "chartFinished"], exportAs: ["echarts"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
36
42
  }
37
43
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ComponentChartComponent, decorators: [{
38
44
  type: Component,
39
45
  args: [{ selector: 'app-report-component-chart', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
46
+ ComponentNoteComponent,
40
47
  NgxEchartsDirective,
41
- ], template: "<div\n class=\"chart-host\"\n echarts\n [options]=\"options ?? {}\"\n [theme]=\"'report'\"\n [autoResize]=\"true\"\n (chartInit)=\"onChartInit($event)\">\n</div>\n", styles: [":host{display:block;width:100%;height:100%}.chart-host{width:100%;height:100%}\n"] }]
42
- }], propDecorators: { component: [{
43
- type: Input
44
- }], data: [{
45
- type: Input
46
- }] } });
47
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcG9uZW50LWNoYXJ0LmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3NyYy9hcHAvcmVwb3J0cy9jb21wb25lbnRzL2NvbXBvbmVudC1jaGFydC9jb21wb25lbnQtY2hhcnQuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2FwcC9yZXBvcnRzL2NvbXBvbmVudHMvY29tcG9uZW50LWNoYXJ0L2NvbXBvbmVudC1jaGFydC5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBYSxNQUFNLGVBQWUsQ0FBQztBQUdyRixPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFHbEQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7O0FBR3hELDBFQUEwRTtBQUMxRSwwRUFBMEU7QUFDMUUsNkVBQTZFO0FBQzdFLG1EQUFtRDtBQVduRCxNQUFNLE9BQU8sdUJBQXVCO0lBRWxCLFNBQVMsQ0FBa0I7SUFDM0IsSUFBSSxHQUF5QixJQUFJLENBQUM7SUFFM0MsT0FBTyxHQUF5QixJQUFJLENBQUM7SUFFcEMsTUFBTSxHQUFtQixJQUFJLENBQUM7SUFFL0IsV0FBVztRQUNoQixJQUFJLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQyxTQUFTLElBQUksSUFBSSxDQUFDLElBQUk7WUFDeEMsQ0FBQyxDQUFDLGdCQUFnQixDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQztZQUM3QyxDQUFDLENBQUMsSUFBSSxDQUFDO0lBQ1gsQ0FBQztJQUVNLFdBQVcsQ0FBQyxLQUFjO1FBQy9CLElBQUksQ0FBQyxNQUFNLEdBQUcsS0FBSyxDQUFDO0lBQ3RCLENBQUM7SUFFRCw2REFBNkQ7SUFDdEQsTUFBTTtRQUNYLElBQUksQ0FBQyxNQUFNLEVBQUUsTUFBTSxFQUFFLENBQUM7SUFDeEIsQ0FBQztJQUVELHdEQUF3RDtJQUNqRCxVQUFVLENBQUMsVUFBVSxHQUFHLENBQUM7UUFDOUIsT0FBTyxJQUFJLENBQUMsTUFBTSxFQUFFLFVBQVUsQ0FBQztZQUM3QixJQUFJLEVBQUUsS0FBSztZQUNYLFVBQVU7WUFDVixlQUFlLEVBQUUsU0FBUztTQUMzQixDQUFDLElBQUksSUFBSSxDQUFDO0lBQ2IsQ0FBQzt3R0EvQlUsdUJBQXVCOzRGQUF2Qix1QkFBdUIsNkpDdkJwQywyS0FRQSwwSURZSSxtQkFBbUI7OzRGQUdWLHVCQUF1QjtrQkFWbkMsU0FBUzsrQkFDRSw0QkFBNEIsbUJBR3JCLHVCQUF1QixDQUFDLE1BQU0sY0FDbkMsSUFBSSxXQUNQO3dCQUNQLG1CQUFtQjtxQkFDcEI7OEJBSWUsU0FBUztzQkFBeEIsS0FBSztnQkFDVSxJQUFJO3NCQUFuQixLQUFLIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksIENvbXBvbmVudCwgSW5wdXQsIE9uQ2hhbmdlcyB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuXG5pbXBvcnQgdHlwZSB7IEVDaGFydHNPcHRpb24sIEVDaGFydHMgfSBmcm9tICdlY2hhcnRzJztcbmltcG9ydCB7IE5neEVjaGFydHNEaXJlY3RpdmUgfSBmcm9tICduZ3gtZWNoYXJ0cyc7XG5cbmltcG9ydCB7IENvbXBvbmVudERhdGEsIFJlcG9ydENvbXBvbmVudCB9IGZyb20gJy4uLy4uL2ludGVyZmFjZXMvcmVwb3J0LmludGVyZmFjZSc7XG5pbXBvcnQgeyBidWlsZENoYXJ0T3B0aW9uIH0gZnJvbSAnLi4vLi4vb3B0aW9uLWJ1aWxkZXInO1xuXG5cbi8vIEVDaGFydHMgaG9zdCBmb3IgYSBjaGFydCBjb21wb25lbnQuIFB1cmUgcmVuZGVyaW5nOiB0aGUgcGFyZW50IG93bnMgdGhlXG4vLyBkYXRhIGZldGNoOyB0aGlzIG1hcHMgKGNvbXBvbmVudCBjb25maWcsIHJvd3MpIOKGkiBvcHRpb24gYW5kIGV4cG9zZXMgdGhlXG4vLyBjaGFydCBpbnN0YW5jZSBmb3IgcmVzaXplICsgdGhlIGZ1dHVyZSBpbWFnZSBleHBvcnRzIChnZXREYXRhVVJMIG5lZWRzIHRoZVxuLy8gY2FudmFzIHJlbmRlcmVyIHJlZ2lzdGVyZWQgaW4gdGhlbWUvZWNoYXJ0cy50cykuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdhcHAtcmVwb3J0LWNvbXBvbmVudC1jaGFydCcsXG4gIHRlbXBsYXRlVXJsOiAnLi9jb21wb25lbnQtY2hhcnQuY29tcG9uZW50Lmh0bWwnLFxuICBzdHlsZVVybHM6IFsnLi9jb21wb25lbnQtY2hhcnQuY29tcG9uZW50LnNjc3MnXSxcbiAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2gsXG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIGltcG9ydHM6IFtcbiAgICBOZ3hFY2hhcnRzRGlyZWN0aXZlLFxuICBdLFxufSlcbmV4cG9ydCBjbGFzcyBDb21wb25lbnRDaGFydENvbXBvbmVudCBpbXBsZW1lbnRzIE9uQ2hhbmdlcyB7XG5cbiAgQElucHV0KCkgcHVibGljIGNvbXBvbmVudDogUmVwb3J0Q29tcG9uZW50O1xuICBASW5wdXQoKSBwdWJsaWMgZGF0YTogQ29tcG9uZW50RGF0YSB8IG51bGwgPSBudWxsO1xuXG4gIHB1YmxpYyBvcHRpb25zOiBFQ2hhcnRzT3B0aW9uIHwgbnVsbCA9IG51bGw7XG5cbiAgcHJpdmF0ZSBfY2hhcnQ6IEVDaGFydHMgfCBudWxsID0gbnVsbDtcblxuICBwdWJsaWMgbmdPbkNoYW5nZXMoKTogdm9pZCB7XG4gICAgdGhpcy5vcHRpb25zID0gdGhpcy5jb21wb25lbnQgJiYgdGhpcy5kYXRhXG4gICAgICA/IGJ1aWxkQ2hhcnRPcHRpb24odGhpcy5jb21wb25lbnQsIHRoaXMuZGF0YSlcbiAgICAgIDogbnVsbDtcbiAgfVxuXG4gIHB1YmxpYyBvbkNoYXJ0SW5pdChjaGFydDogRUNoYXJ0cyk6IHZvaWQge1xuICAgIHRoaXMuX2NoYXJ0ID0gY2hhcnQ7XG4gIH1cblxuICAvLyBDYWxsZWQgYnkgdGhlIGNhbnZhcyB3aGVuIHRoZSBib3ggaXMgcmVzaXplZCBpbiBlZGl0IG1vZGUuXG4gIHB1YmxpYyByZXNpemUoKTogdm9pZCB7XG4gICAgdGhpcy5fY2hhcnQ/LnJlc2l6ZSgpO1xuICB9XG5cbiAgLy8gSGlnaC1yZXMgaW1hZ2UgZm9yIHRoZSAobGF0ZXItcGhhc2UpIFBQVC9QREYgZXhwb3J0cy5cbiAgcHVibGljIGdldERhdGFVUkwocGl4ZWxSYXRpbyA9IDMpOiBzdHJpbmcgfCBudWxsIHtcbiAgICByZXR1cm4gdGhpcy5fY2hhcnQ/LmdldERhdGFVUkwoe1xuICAgICAgdHlwZTogJ3BuZycsXG4gICAgICBwaXhlbFJhdGlvLFxuICAgICAgYmFja2dyb3VuZENvbG9yOiAnI2ZmZmZmZicsXG4gICAgfSkgPz8gbnVsbDtcbiAgfVxufVxuIiwiPGRpdlxuICBjbGFzcz1cImNoYXJ0LWhvc3RcIlxuICBlY2hhcnRzXG4gIFtvcHRpb25zXT1cIm9wdGlvbnMgPz8ge31cIlxuICBbdGhlbWVdPVwiJ3JlcG9ydCdcIlxuICBbYXV0b1Jlc2l6ZV09XCJ0cnVlXCJcbiAgKGNoYXJ0SW5pdCk9XCJvbkNoYXJ0SW5pdCgkZXZlbnQpXCI+XG48L2Rpdj5cbiJdfQ==
48
+ ], template: "<div\n class=\"chart-host\"\n echarts\n [options]=\"options() ?? {}\"\n [theme]=\"'report'\"\n [autoResize]=\"true\"\n (chartInit)=\"onChartInit($event)\">\n</div>\n<app-report-component-note [data]=\"data()\"></app-report-component-note>\n", styles: [":host{display:flex;flex-direction:column;width:100%;height:100%}.chart-host{flex:1 1 0;width:100%;min-height:0}\n"] }]
49
+ }] });
50
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcG9uZW50LWNoYXJ0LmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3NyYy9hcHAvcmVwb3J0cy9jb21wb25lbnRzL2NvbXBvbmVudC1jaGFydC9jb21wb25lbnQtY2hhcnQuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2FwcC9yZXBvcnRzL2NvbXBvbmVudHMvY29tcG9uZW50LWNoYXJ0L2NvbXBvbmVudC1jaGFydC5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsU0FBUyxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRzVGLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUdsRCxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUN4RCxPQUFPLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSw0Q0FBNEMsQ0FBQztBQUN0RixPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSw0Q0FBNEMsQ0FBQzs7QUFHcEYsMEVBQTBFO0FBQzFFLDBFQUEwRTtBQUMxRSwyRUFBMkU7QUFDM0Usb0VBQW9FO0FBQ3BFLHFFQUFxRTtBQUNyRSwyRUFBMkU7QUFDM0UsNENBQTRDO0FBWTVDLE1BQU0sT0FBTyx1QkFBdUI7SUFFbEIsU0FBUyxHQUFHLEtBQUssQ0FBQyxRQUFRLEVBQW1CLENBQUM7SUFDOUMsSUFBSSxHQUFHLEtBQUssQ0FBdUIsSUFBSSxDQUFDLENBQUM7SUFFekMsT0FBTyxHQUFHLFFBQVEsQ0FBdUIsR0FBRyxFQUFFO1FBQzVELE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUV6QixPQUFPLElBQUk7WUFDVCxDQUFDLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsWUFBWSxDQUFDLGFBQWEsRUFBRSxDQUFDO1lBQzdFLENBQUMsQ0FBQyxJQUFJLENBQUM7SUFDWCxDQUFDLENBQUMsQ0FBQztJQUVLLE1BQU0sR0FBbUIsSUFBSSxDQUFDO0lBQ3JCLFlBQVksR0FBRyxNQUFNLENBQUMsd0JBQXdCLENBQUMsQ0FBQztJQUUxRCxXQUFXLENBQUMsS0FBYztRQUMvQixJQUFJLENBQUMsTUFBTSxHQUFHLEtBQUssQ0FBQztJQUN0QixDQUFDO0lBRUQsNkRBQTZEO0lBQ3RELE1BQU07UUFDWCxJQUFJLENBQUMsTUFBTSxFQUFFLE1BQU0sRUFBRSxDQUFDO0lBQ3hCLENBQUM7SUFFRCx3REFBd0Q7SUFDakQsVUFBVSxDQUFDLFVBQVUsR0FBRyxDQUFDO1FBQzlCLE9BQU8sSUFBSSxDQUFDLE1BQU0sRUFBRSxVQUFVLENBQUM7WUFDN0IsSUFBSSxFQUFFLEtBQUs7WUFDWCxVQUFVO1lBQ1YsZUFBZSxFQUFFLFNBQVM7U0FDM0IsQ0FBQyxJQUFJLElBQUksQ0FBQztJQUNiLENBQUM7d0dBaENVLHVCQUF1Qjs0RkFBdkIsdUJBQXVCLDBWQzdCcEMsd1BBU0EsMktEZ0JJLHNCQUFzQix3SUFDdEIsbUJBQW1COzs0RkFHVix1QkFBdUI7a0JBWG5DLFNBQVM7K0JBQ0UsNEJBQTRCLG1CQUdyQix1QkFBdUIsQ0FBQyxNQUFNLGNBQ25DLElBQUksV0FDUDt3QkFDUCxzQkFBc0I7d0JBQ3RCLG1CQUFtQjtxQkFDcEIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSwgQ29tcG9uZW50LCBjb21wdXRlZCwgaW5qZWN0LCBpbnB1dCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuXG5pbXBvcnQgdHlwZSB7IEVDaGFydHNPcHRpb24sIEVDaGFydHMgfSBmcm9tICdlY2hhcnRzJztcbmltcG9ydCB7IE5neEVjaGFydHNEaXJlY3RpdmUgfSBmcm9tICduZ3gtZWNoYXJ0cyc7XG5cbmltcG9ydCB7IENvbXBvbmVudERhdGEsIFJlcG9ydENvbXBvbmVudCB9IGZyb20gJy4uLy4uL2ludGVyZmFjZXMvcmVwb3J0LmludGVyZmFjZSc7XG5pbXBvcnQgeyBidWlsZENoYXJ0T3B0aW9uIH0gZnJvbSAnLi4vLi4vb3B0aW9uLWJ1aWxkZXInO1xuaW1wb3J0IHsgUmVwb3J0RmlsdGVyU3RhdGVTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vc2VydmljZXMvcmVwb3J0LWZpbHRlci1zdGF0ZS5zZXJ2aWNlJztcbmltcG9ydCB7IENvbXBvbmVudE5vdGVDb21wb25lbnQgfSBmcm9tICcuLi9jb21wb25lbnQtbm90ZS9jb21wb25lbnQtbm90ZS5jb21wb25lbnQnO1xuXG5cbi8vIEVDaGFydHMgaG9zdCBmb3IgYSBjaGFydCBjb21wb25lbnQuIFB1cmUgcmVuZGVyaW5nOiB0aGUgcGFyZW50IG93bnMgdGhlXG4vLyBkYXRhIGZldGNoOyB0aGlzIG1hcHMgKGNvbXBvbmVudCBjb25maWcsIHJvd3MsIHRoZSBvcGVuIHJlcG9ydCdzIGZvcm1hdFxuLy8gY29udGV4dCkg4oaSIG9wdGlvbiBhbmQgZXhwb3NlcyB0aGUgY2hhcnQgaW5zdGFuY2UgZm9yIHJlc2l6ZSArIHRoZSBmdXR1cmVcbi8vIGltYWdlIGV4cG9ydHMgKGdldERhdGFVUkwgbmVlZHMgdGhlIGNhbnZhcyByZW5kZXJlciByZWdpc3RlcmVkIGluXG4vLyB0aGVtZS9lY2hhcnRzLnRzKS4gVGhlIHNoYXJlZCBub3RlIHVuZGVyIHRoZSBwbG90IG5hbWVzIHRoZSBkYXlzIGFcbi8vIGNvbXBhcmlzb24gY29tcGFyZXMgYW5kIHdoeSB3aW5kb3dlZCBzZXJpZXMgcmVhZCBhcyB0aGV5IGRvOyB0aGUgc2VyaWVzJ1xuLy8gdG9vbHRpcHMgZGVzY3JpYmUgdGhlIHdpbmRvd3MgdGhlbXNlbHZlcy5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2FwcC1yZXBvcnQtY29tcG9uZW50LWNoYXJ0JyxcbiAgdGVtcGxhdGVVcmw6ICcuL2NvbXBvbmVudC1jaGFydC5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogWycuL2NvbXBvbmVudC1jaGFydC5jb21wb25lbnQuc2NzcyddLFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgaW1wb3J0czogW1xuICAgIENvbXBvbmVudE5vdGVDb21wb25lbnQsXG4gICAgTmd4RWNoYXJ0c0RpcmVjdGl2ZSxcbiAgXSxcbn0pXG5leHBvcnQgY2xhc3MgQ29tcG9uZW50Q2hhcnRDb21wb25lbnQge1xuXG4gIHB1YmxpYyByZWFkb25seSBjb21wb25lbnQgPSBpbnB1dC5yZXF1aXJlZDxSZXBvcnRDb21wb25lbnQ+KCk7XG4gIHB1YmxpYyByZWFkb25seSBkYXRhID0gaW5wdXQ8Q29tcG9uZW50RGF0YSB8IG51bGw+KG51bGwpO1xuXG4gIHB1YmxpYyByZWFkb25seSBvcHRpb25zID0gY29tcHV0ZWQ8RUNoYXJ0c09wdGlvbiB8IG51bGw+KCgpID0+IHtcbiAgICBjb25zdCBkYXRhID0gdGhpcy5kYXRhKCk7XG5cbiAgICByZXR1cm4gZGF0YVxuICAgICAgPyBidWlsZENoYXJ0T3B0aW9uKHRoaXMuY29tcG9uZW50KCksIGRhdGEsIHRoaXMuX2ZpbHRlclN0YXRlLmZvcm1hdENvbnRleHQoKSlcbiAgICAgIDogbnVsbDtcbiAgfSk7XG5cbiAgcHJpdmF0ZSBfY2hhcnQ6IEVDaGFydHMgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSByZWFkb25seSBfZmlsdGVyU3RhdGUgPSBpbmplY3QoUmVwb3J0RmlsdGVyU3RhdGVTZXJ2aWNlKTtcblxuICBwdWJsaWMgb25DaGFydEluaXQoY2hhcnQ6IEVDaGFydHMpOiB2b2lkIHtcbiAgICB0aGlzLl9jaGFydCA9IGNoYXJ0O1xuICB9XG5cbiAgLy8gQ2FsbGVkIGJ5IHRoZSBjYW52YXMgd2hlbiB0aGUgYm94IGlzIHJlc2l6ZWQgaW4gZWRpdCBtb2RlLlxuICBwdWJsaWMgcmVzaXplKCk6IHZvaWQge1xuICAgIHRoaXMuX2NoYXJ0Py5yZXNpemUoKTtcbiAgfVxuXG4gIC8vIEhpZ2gtcmVzIGltYWdlIGZvciB0aGUgKGxhdGVyLXBoYXNlKSBQUFQvUERGIGV4cG9ydHMuXG4gIHB1YmxpYyBnZXREYXRhVVJMKHBpeGVsUmF0aW8gPSAzKTogc3RyaW5nIHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMuX2NoYXJ0Py5nZXREYXRhVVJMKHtcbiAgICAgIHR5cGU6ICdwbmcnLFxuICAgICAgcGl4ZWxSYXRpbyxcbiAgICAgIGJhY2tncm91bmRDb2xvcjogJyNmZmZmZmYnLFxuICAgIH0pID8/IG51bGw7XG4gIH1cbn1cbiIsIjxkaXZcbiAgY2xhc3M9XCJjaGFydC1ob3N0XCJcbiAgZWNoYXJ0c1xuICBbb3B0aW9uc109XCJvcHRpb25zKCkgPz8ge31cIlxuICBbdGhlbWVdPVwiJ3JlcG9ydCdcIlxuICBbYXV0b1Jlc2l6ZV09XCJ0cnVlXCJcbiAgKGNoYXJ0SW5pdCk9XCJvbkNoYXJ0SW5pdCgkZXZlbnQpXCI+XG48L2Rpdj5cbjxhcHAtcmVwb3J0LWNvbXBvbmVudC1ub3RlIFtkYXRhXT1cImRhdGEoKVwiPjwvYXBwLXJlcG9ydC1jb21wb25lbnQtbm90ZT5cbiJdfQ==
@@ -1,36 +1,63 @@
1
- import { ChangeDetectionStrategy, Component, Input, input } from '@angular/core';
2
- import { formatMeasureValue } from '../../format';
1
+ import { ChangeDetectionStrategy, Component, computed, inject, input } from '@angular/core';
2
+ import { ReportChangePolarity } from '../../enums/report-change-polarity.enum';
3
+ import { ReportKpiTrend } from '../../enums/report-kpi-trend.enum';
4
+ import { kpiCardView } from '../../kpi-card';
5
+ import { componentNoteLines } from '../../output-notes';
6
+ import { ReportFilterStateService } from '../../services/report-filter-state.service';
7
+ import { REPORT_KPI_CHANGE_COLORS, REPORT_KPI_SPARKLINE_COLOR } from '../../theme/palette';
8
+ import { REPORT_KPI_VALUE_FONT_SIZE } from '../../theme/typography';
9
+ import { DEFAULT_TEXT_COLOR } from '../../text';
10
+ import { ComponentNoteComponent } from '../component-note/component-note.component';
11
+ import { SparklineComponent } from './components/sparkline/sparkline.component';
3
12
  import * as i0 from "@angular/core";
4
13
  // A single big number: the component's measure aggregated over all (filtered)
5
- // rows server-side; this just formats the one cell.
14
+ // rows server-side, in the open report's format context (its locale, its
15
+ // currency, and whether a percent arrives as a 0-1 ratio or, from an older
16
+ // server, already x100). With a comparison, a target or a sparkline the server
17
+ // sends every other number the card shows too (ComponentData.kpi); kpi-card.ts
18
+ // decides what each line says, and this only lays them out, in order: the
19
+ // value and its label, the delta, the days, the target and the trend, then the
20
+ // note.
21
+ //
22
+ // The palette's colors and the value's size arrive as custom properties from
23
+ // the same constants the exports draw with. A host page overrides a color by
24
+ // setting --report-kpi-good-color, --report-kpi-bad-color,
25
+ // --report-kpi-neutral-color or --report-kpi-sparkline-color around the report.
6
26
  export class ComponentKpiComponent {
7
- component;
8
- data = null;
9
- // Whether a percent value is a 0-1 ratio (the report's percentFormat). An
10
- // older server sends it already x100, and it is printed as it came.
11
- ratioPercent = input(true);
12
- value = '—';
13
- label = '';
14
- ngOnChanges() {
15
- const config = (this.component?.config ?? {});
16
- this.label = config.measure?.label ?? '';
17
- const row = this.data?.rows?.[0];
18
- if (!row) {
19
- this.value = '—';
20
- return;
21
- }
22
- const column = config.measure?.column ?? Object.keys(row)[0];
23
- this.value = formatMeasureValue(row[column], config.measure?.format, config.measure?.precision, this.ratioPercent());
24
- }
27
+ component = input.required();
28
+ data = input(null);
29
+ ReportKpiTrend = ReportKpiTrend;
30
+ tokens = {
31
+ valueSize: `${REPORT_KPI_VALUE_FONT_SIZE}px`,
32
+ text: DEFAULT_TEXT_COLOR,
33
+ good: `#${REPORT_KPI_CHANGE_COLORS[ReportChangePolarity.Good]}`,
34
+ bad: `#${REPORT_KPI_CHANGE_COLORS[ReportChangePolarity.Bad]}`,
35
+ neutral: `#${REPORT_KPI_CHANGE_COLORS[ReportChangePolarity.Neutral]}`,
36
+ sparkline: `#${REPORT_KPI_SPARKLINE_COLOR}`,
37
+ };
38
+ // Read off the component on every data change rather than through a memo of
39
+ // the config: a settings save repaints the same component object with new
40
+ // KPI keys, then reloads its data.
41
+ view = computed(() => kpiCardView((this.component().config ?? {}), this.data(), { format: this._filterState.formatContext(), timezone: this._filterState.timezone() }));
42
+ // The note sits under the card only when it has something to say, so an
43
+ // empty one takes no room.
44
+ hasNote = computed(() => componentNoteLines(this.data(), { locale: this._filterState.formatContext().locale, timezone: this._filterState.timezone() }, { windows: false, windowDescriptions: true }).length > 0);
45
+ _filterState = inject(ReportFilterStateService);
25
46
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ComponentKpiComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
26
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ComponentKpiComponent, isStandalone: true, selector: "app-report-component-kpi", inputs: { component: { classPropertyName: "component", publicName: "component", isSignal: false, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: false, isRequired: false, transformFunction: null }, ratioPercent: { classPropertyName: "ratioPercent", publicName: "ratioPercent", isSignal: true, isRequired: false, transformFunction: null } }, usesOnChanges: true, ngImport: i0, template: "<div class=\"kpi\">\n <div class=\"kpi-value\">{{ value }}</div>\n @if (label) {\n <div class=\"kpi-label\">{{ label }}</div>\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%}.kpi{width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px}.kpi .kpi-value{font-size:34px;font-weight:600;color:#1f2933;line-height:1}.kpi .kpi-label{font-size:12px;color:#7b8794}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
47
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ComponentKpiComponent, isStandalone: true, selector: "app-report-component-kpi", inputs: { component: { classPropertyName: "component", publicName: "component", isSignal: true, isRequired: true, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.--report-kpi-value-size": "tokens.valueSize", "style.--report-kpi-text-default": "tokens.text", "style.--report-kpi-good-default": "tokens.good", "style.--report-kpi-bad-default": "tokens.bad", "style.--report-kpi-neutral-default": "tokens.neutral", "style.--report-kpi-sparkline-default": "tokens.sparkline" } }, ngImport: i0, template: "@let card = view();\n<div class=\"kpi fs-column.gap-xs.align-center.justify-center\">\n <div class=\"kpi-value\">{{ card.value }}</div>\n @if (card.label) {\n <div class=\"kpi-label small\">{{ card.label }}</div>\n }\n @if (card.delta; as delta) {\n <div class=\"kpi-delta fs-row.gap-xs.align-baseline.justify-center.wrap\">\n <span\n class=\"kpi-change\"\n [attr.data-tone]=\"delta.tone\">\n @if (delta.trend !== ReportKpiTrend.Flat) {\n <svg\n class=\"kpi-arrow\"\n [class.down]=\"delta.trend === ReportKpiTrend.Down\"\n viewBox=\"0 0 10 10\">\n <polygon points=\"5,1 9.5,9 0.5,9\"></polygon>\n </svg>\n }\n {{ delta.text }}\n </span>\n @if (delta.compared) {\n <span class=\"small\">{{ delta.compared }}</span>\n }\n </div>\n }\n @if (card.window) {\n <div class=\"kpi-line small\">{{ card.window }}</div>\n }\n @if (card.target; as target) {\n <div class=\"kpi-target fs-column.gap-xs.align-center\">\n <div class=\"kpi-line small\">\n {{ target.text }}\n @if (target.progress) {\n \u00B7 {{ target.progress }}\n }\n </div>\n @if (target.fill !== null) {\n <div\n class=\"kpi-progress\"\n [attr.data-tone]=\"target.tone\">\n <div\n class=\"kpi-progress-fill\"\n [style.width.%]=\"target.fill * 100\">\n </div>\n </div>\n }\n </div>\n }\n @if (card.sparkline; as sparkline) {\n <app-component-kpi-sparkline\n class=\"kpi-sparkline\"\n [values]=\"sparkline.values\"\n [partial]=\"sparkline.partial\">\n </app-component-kpi-sparkline>\n }\n <!-- The card prints its own days, so the note leaves them out. A KPI has no\n tooltip, so its note also says what a windowed value is. -->\n @if (hasNote()) {\n <app-report-component-note\n [data]=\"data()\"\n [windows]=\"false\"\n [windowDescriptions]=\"true\">\n </app-report-component-note>\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%;--report-kpi-good: var(--report-kpi-good-color, var(--report-kpi-good-default));--report-kpi-bad: var(--report-kpi-bad-color, var(--report-kpi-bad-default));--report-kpi-neutral: var(--report-kpi-neutral-color, var(--report-kpi-neutral-default));--report-kpi-sparkline: var(--report-kpi-sparkline-color, var(--report-kpi-sparkline-default))}.kpi{width:100%;height:100%;overflow:hidden;text-align:center}.kpi .kpi-value{font-size:var(--report-kpi-value-size);font-weight:600;color:var(--report-kpi-text-default);line-height:1}.kpi .kpi-line{max-width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.kpi-change{font-weight:600;white-space:nowrap}.kpi-change[data-tone=good]{color:var(--report-kpi-good)}.kpi-change[data-tone=bad]{color:var(--report-kpi-bad)}.kpi-change[data-tone=neutral]{color:var(--report-kpi-neutral)}.kpi-arrow{width:.7em;height:.7em;fill:currentColor}.kpi-arrow.down{transform:rotate(180deg)}.kpi-delta,.kpi-target{max-width:100%}.kpi-progress{position:relative;width:120px;max-width:100%;height:4px;border-radius:2px;overflow:hidden;color:var(--report-kpi-neutral)}.kpi-progress[data-tone=good]{color:var(--report-kpi-good)}.kpi-progress:before{content:\"\";position:absolute;inset:0;background:currentColor;opacity:.2}.kpi-progress .kpi-progress-fill{position:relative;height:100%;background:currentColor}.kpi-sparkline{max-width:240px;flex:0 0 auto}\n"], dependencies: [{ kind: "component", type: ComponentNoteComponent, selector: "app-report-component-note", inputs: ["data", "windows", "windowDescriptions", "windowNotes"] }, { kind: "component", type: SparklineComponent, selector: "app-component-kpi-sparkline", inputs: ["values", "partial"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
27
48
  }
28
49
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ComponentKpiComponent, decorators: [{
29
50
  type: Component,
30
- args: [{ selector: 'app-report-component-kpi', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, template: "<div class=\"kpi\">\n <div class=\"kpi-value\">{{ value }}</div>\n @if (label) {\n <div class=\"kpi-label\">{{ label }}</div>\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%}.kpi{width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px}.kpi .kpi-value{font-size:34px;font-weight:600;color:#1f2933;line-height:1}.kpi .kpi-label{font-size:12px;color:#7b8794}\n"] }]
31
- }], propDecorators: { component: [{
32
- type: Input
33
- }], data: [{
34
- type: Input
35
- }] } });
36
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcG9uZW50LWtwaS5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBwL3JlcG9ydHMvY29tcG9uZW50cy9jb21wb25lbnQta3BpL2NvbXBvbmVudC1rcGkuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2FwcC9yZXBvcnRzL2NvbXBvbmVudHMvY29tcG9uZW50LWtwaS9jb21wb25lbnQta3BpLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFhLEtBQUssRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUU1RixPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxjQUFjLENBQUM7O0FBSWxELDhFQUE4RTtBQUM5RSxvREFBb0Q7QUFRcEQsTUFBTSxPQUFPLHFCQUFxQjtJQUVoQixTQUFTLENBQWtCO0lBQzNCLElBQUksR0FBeUIsSUFBSSxDQUFDO0lBRWxELDBFQUEwRTtJQUMxRSxvRUFBb0U7SUFDcEQsWUFBWSxHQUFHLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUVwQyxLQUFLLEdBQUcsR0FBRyxDQUFDO0lBQ1osS0FBSyxHQUFHLEVBQUUsQ0FBQztJQUVYLFdBQVc7UUFDaEIsTUFBTSxNQUFNLEdBQUcsQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLE1BQU0sSUFBSSxFQUFFLENBQWMsQ0FBQztRQUMzRCxJQUFJLENBQUMsS0FBSyxHQUFHLE1BQU0sQ0FBQyxPQUFPLEVBQUUsS0FBSyxJQUFJLEVBQUUsQ0FBQztRQUV6QyxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ2pDLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztZQUNULElBQUksQ0FBQyxLQUFLLEdBQUcsR0FBRyxDQUFDO1lBRWpCLE9BQU87UUFDVCxDQUFDO1FBRUQsTUFBTSxNQUFNLEdBQUcsTUFBTSxDQUFDLE9BQU8sRUFBRSxNQUFNLElBQUksTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUM3RCxJQUFJLENBQUMsS0FBSyxHQUFHLGtCQUFrQixDQUM3QixHQUFHLENBQUMsTUFBTSxDQUFDLEVBQ1gsTUFBTSxDQUFDLE9BQU8sRUFBRSxNQUFNLEVBQ3RCLE1BQU0sQ0FBQyxPQUFPLEVBQUUsU0FBUyxFQUN6QixJQUFJLENBQUMsWUFBWSxFQUFFLENBQ3BCLENBQUM7SUFDSixDQUFDO3dHQTlCVSxxQkFBcUI7NEZBQXJCLHFCQUFxQiw2ZkNmbEMsbUpBTUE7OzRGRFNhLHFCQUFxQjtrQkFQakMsU0FBUzsrQkFDRSwwQkFBMEIsbUJBR25CLHVCQUF1QixDQUFDLE1BQU0sY0FDbkMsSUFBSTs4QkFJQSxTQUFTO3NCQUF4QixLQUFLO2dCQUNVLElBQUk7c0JBQW5CLEtBQUsiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSwgQ29tcG9uZW50LCBJbnB1dCwgT25DaGFuZ2VzLCBpbnB1dCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuXG5pbXBvcnQgeyBmb3JtYXRNZWFzdXJlVmFsdWUgfSBmcm9tICcuLi8uLi9mb3JtYXQnO1xuaW1wb3J0IHsgQ29tcG9uZW50RGF0YSwgS3BpQ29uZmlnLCBSZXBvcnRDb21wb25lbnQgfSBmcm9tICcuLi8uLi9pbnRlcmZhY2VzL3JlcG9ydC5pbnRlcmZhY2UnO1xuXG5cbi8vIEEgc2luZ2xlIGJpZyBudW1iZXI6IHRoZSBjb21wb25lbnQncyBtZWFzdXJlIGFnZ3JlZ2F0ZWQgb3ZlciBhbGwgKGZpbHRlcmVkKVxuLy8gcm93cyBzZXJ2ZXItc2lkZTsgdGhpcyBqdXN0IGZvcm1hdHMgdGhlIG9uZSBjZWxsLlxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnYXBwLXJlcG9ydC1jb21wb25lbnQta3BpJyxcbiAgdGVtcGxhdGVVcmw6ICcuL2NvbXBvbmVudC1rcGkuY29tcG9uZW50Lmh0bWwnLFxuICBzdHlsZVVybHM6IFsnLi9jb21wb25lbnQta3BpLmNvbXBvbmVudC5zY3NzJ10sXG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxuICBzdGFuZGFsb25lOiB0cnVlLFxufSlcbmV4cG9ydCBjbGFzcyBDb21wb25lbnRLcGlDb21wb25lbnQgaW1wbGVtZW50cyBPbkNoYW5nZXMge1xuXG4gIEBJbnB1dCgpIHB1YmxpYyBjb21wb25lbnQ6IFJlcG9ydENvbXBvbmVudDtcbiAgQElucHV0KCkgcHVibGljIGRhdGE6IENvbXBvbmVudERhdGEgfCBudWxsID0gbnVsbDtcblxuICAvLyBXaGV0aGVyIGEgcGVyY2VudCB2YWx1ZSBpcyBhIDAtMSByYXRpbyAodGhlIHJlcG9ydCdzIHBlcmNlbnRGb3JtYXQpLiBBblxuICAvLyBvbGRlciBzZXJ2ZXIgc2VuZHMgaXQgYWxyZWFkeSB4MTAwLCBhbmQgaXQgaXMgcHJpbnRlZCBhcyBpdCBjYW1lLlxuICBwdWJsaWMgcmVhZG9ubHkgcmF0aW9QZXJjZW50ID0gaW5wdXQodHJ1ZSk7XG5cbiAgcHVibGljIHZhbHVlID0gJ+KAlCc7XG4gIHB1YmxpYyBsYWJlbCA9ICcnO1xuXG4gIHB1YmxpYyBuZ09uQ2hhbmdlcygpOiB2b2lkIHtcbiAgICBjb25zdCBjb25maWcgPSAodGhpcy5jb21wb25lbnQ/LmNvbmZpZyA/PyB7fSkgYXMgS3BpQ29uZmlnO1xuICAgIHRoaXMubGFiZWwgPSBjb25maWcubWVhc3VyZT8ubGFiZWwgPz8gJyc7XG5cbiAgICBjb25zdCByb3cgPSB0aGlzLmRhdGE/LnJvd3M/LlswXTtcbiAgICBpZiAoIXJvdykge1xuICAgICAgdGhpcy52YWx1ZSA9ICfigJQnO1xuXG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgY29sdW1uID0gY29uZmlnLm1lYXN1cmU/LmNvbHVtbiA/PyBPYmplY3Qua2V5cyhyb3cpWzBdO1xuICAgIHRoaXMudmFsdWUgPSBmb3JtYXRNZWFzdXJlVmFsdWUoXG4gICAgICByb3dbY29sdW1uXSxcbiAgICAgIGNvbmZpZy5tZWFzdXJlPy5mb3JtYXQsXG4gICAgICBjb25maWcubWVhc3VyZT8ucHJlY2lzaW9uLFxuICAgICAgdGhpcy5yYXRpb1BlcmNlbnQoKSxcbiAgICApO1xuICB9XG59XG4iLCI8ZGl2IGNsYXNzPVwia3BpXCI+XG4gIDxkaXYgY2xhc3M9XCJrcGktdmFsdWVcIj57eyB2YWx1ZSB9fTwvZGl2PlxuICBAaWYgKGxhYmVsKSB7XG4gICAgPGRpdiBjbGFzcz1cImtwaS1sYWJlbFwiPnt7IGxhYmVsIH19PC9kaXY+XG4gIH1cbjwvZGl2PlxuIl19
51
+ args: [{ selector: 'app-report-component-kpi', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
52
+ ComponentNoteComponent,
53
+ SparklineComponent,
54
+ ], host: {
55
+ '[style.--report-kpi-value-size]': 'tokens.valueSize',
56
+ '[style.--report-kpi-text-default]': 'tokens.text',
57
+ '[style.--report-kpi-good-default]': 'tokens.good',
58
+ '[style.--report-kpi-bad-default]': 'tokens.bad',
59
+ '[style.--report-kpi-neutral-default]': 'tokens.neutral',
60
+ '[style.--report-kpi-sparkline-default]': 'tokens.sparkline',
61
+ }, template: "@let card = view();\n<div class=\"kpi fs-column.gap-xs.align-center.justify-center\">\n <div class=\"kpi-value\">{{ card.value }}</div>\n @if (card.label) {\n <div class=\"kpi-label small\">{{ card.label }}</div>\n }\n @if (card.delta; as delta) {\n <div class=\"kpi-delta fs-row.gap-xs.align-baseline.justify-center.wrap\">\n <span\n class=\"kpi-change\"\n [attr.data-tone]=\"delta.tone\">\n @if (delta.trend !== ReportKpiTrend.Flat) {\n <svg\n class=\"kpi-arrow\"\n [class.down]=\"delta.trend === ReportKpiTrend.Down\"\n viewBox=\"0 0 10 10\">\n <polygon points=\"5,1 9.5,9 0.5,9\"></polygon>\n </svg>\n }\n {{ delta.text }}\n </span>\n @if (delta.compared) {\n <span class=\"small\">{{ delta.compared }}</span>\n }\n </div>\n }\n @if (card.window) {\n <div class=\"kpi-line small\">{{ card.window }}</div>\n }\n @if (card.target; as target) {\n <div class=\"kpi-target fs-column.gap-xs.align-center\">\n <div class=\"kpi-line small\">\n {{ target.text }}\n @if (target.progress) {\n \u00B7 {{ target.progress }}\n }\n </div>\n @if (target.fill !== null) {\n <div\n class=\"kpi-progress\"\n [attr.data-tone]=\"target.tone\">\n <div\n class=\"kpi-progress-fill\"\n [style.width.%]=\"target.fill * 100\">\n </div>\n </div>\n }\n </div>\n }\n @if (card.sparkline; as sparkline) {\n <app-component-kpi-sparkline\n class=\"kpi-sparkline\"\n [values]=\"sparkline.values\"\n [partial]=\"sparkline.partial\">\n </app-component-kpi-sparkline>\n }\n <!-- The card prints its own days, so the note leaves them out. A KPI has no\n tooltip, so its note also says what a windowed value is. -->\n @if (hasNote()) {\n <app-report-component-note\n [data]=\"data()\"\n [windows]=\"false\"\n [windowDescriptions]=\"true\">\n </app-report-component-note>\n }\n</div>\n", styles: [":host{display:block;width:100%;height:100%;--report-kpi-good: var(--report-kpi-good-color, var(--report-kpi-good-default));--report-kpi-bad: var(--report-kpi-bad-color, var(--report-kpi-bad-default));--report-kpi-neutral: var(--report-kpi-neutral-color, var(--report-kpi-neutral-default));--report-kpi-sparkline: var(--report-kpi-sparkline-color, var(--report-kpi-sparkline-default))}.kpi{width:100%;height:100%;overflow:hidden;text-align:center}.kpi .kpi-value{font-size:var(--report-kpi-value-size);font-weight:600;color:var(--report-kpi-text-default);line-height:1}.kpi .kpi-line{max-width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.kpi-change{font-weight:600;white-space:nowrap}.kpi-change[data-tone=good]{color:var(--report-kpi-good)}.kpi-change[data-tone=bad]{color:var(--report-kpi-bad)}.kpi-change[data-tone=neutral]{color:var(--report-kpi-neutral)}.kpi-arrow{width:.7em;height:.7em;fill:currentColor}.kpi-arrow.down{transform:rotate(180deg)}.kpi-delta,.kpi-target{max-width:100%}.kpi-progress{position:relative;width:120px;max-width:100%;height:4px;border-radius:2px;overflow:hidden;color:var(--report-kpi-neutral)}.kpi-progress[data-tone=good]{color:var(--report-kpi-good)}.kpi-progress:before{content:\"\";position:absolute;inset:0;background:currentColor;opacity:.2}.kpi-progress .kpi-progress-fill{position:relative;height:100%;background:currentColor}.kpi-sparkline{max-width:240px;flex:0 0 auto}\n"] }]
62
+ }] });
63
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcG9uZW50LWtwaS5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBwL3JlcG9ydHMvY29tcG9uZW50cy9jb21wb25lbnQta3BpL2NvbXBvbmVudC1rcGkuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2FwcC9yZXBvcnRzL2NvbXBvbmVudHMvY29tcG9uZW50LWtwaS9jb21wb25lbnQta3BpLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFFNUYsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0seUNBQXlDLENBQUM7QUFDL0UsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBRW5FLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUM3QyxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUN4RCxPQUFPLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSw0Q0FBNEMsQ0FBQztBQUN0RixPQUFPLEVBQUUsd0JBQXdCLEVBQUUsMEJBQTBCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUMzRixPQUFPLEVBQUUsMEJBQTBCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUNwRSxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDaEQsT0FBTyxFQUFFLHNCQUFzQixFQUFFLE1BQU0sNENBQTRDLENBQUM7QUFFcEYsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sNENBQTRDLENBQUM7O0FBR2hGLDhFQUE4RTtBQUM5RSx5RUFBeUU7QUFDekUsMkVBQTJFO0FBQzNFLCtFQUErRTtBQUMvRSwrRUFBK0U7QUFDL0UsMEVBQTBFO0FBQzFFLCtFQUErRTtBQUMvRSxRQUFRO0FBQ1IsRUFBRTtBQUNGLDZFQUE2RTtBQUM3RSw2RUFBNkU7QUFDN0UsMkRBQTJEO0FBQzNELGdGQUFnRjtBQW9CaEYsTUFBTSxPQUFPLHFCQUFxQjtJQUVoQixTQUFTLEdBQUcsS0FBSyxDQUFDLFFBQVEsRUFBbUIsQ0FBQztJQUM5QyxJQUFJLEdBQUcsS0FBSyxDQUF1QixJQUFJLENBQUMsQ0FBQztJQUV6QyxjQUFjLEdBQUcsY0FBYyxDQUFDO0lBRWhDLE1BQU0sR0FBRztRQUN2QixTQUFTLEVBQUUsR0FBRywwQkFBMEIsSUFBSTtRQUM1QyxJQUFJLEVBQUUsa0JBQWtCO1FBQ3hCLElBQUksRUFBRSxJQUFJLHdCQUF3QixDQUFDLG9CQUFvQixDQUFDLElBQUksQ0FBQyxFQUFFO1FBQy9ELEdBQUcsRUFBRSxJQUFJLHdCQUF3QixDQUFDLG9CQUFvQixDQUFDLEdBQUcsQ0FBQyxFQUFFO1FBQzdELE9BQU8sRUFBRSxJQUFJLHdCQUF3QixDQUFDLG9CQUFvQixDQUFDLE9BQU8sQ0FBQyxFQUFFO1FBQ3JFLFNBQVMsRUFBRSxJQUFJLDBCQUEwQixFQUFFO0tBQzVDLENBQUM7SUFFRiw0RUFBNEU7SUFDNUUsMEVBQTBFO0lBQzFFLG1DQUFtQztJQUNuQixJQUFJLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLFdBQVcsQ0FDL0MsQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUMsTUFBTSxJQUFJLEVBQUUsQ0FBYyxFQUM1QyxJQUFJLENBQUMsSUFBSSxFQUFFLEVBQ1gsRUFBRSxNQUFNLEVBQUUsSUFBSSxDQUFDLFlBQVksQ0FBQyxhQUFhLEVBQUUsRUFBRSxRQUFRLEVBQUUsSUFBSSxDQUFDLFlBQVksQ0FBQyxRQUFRLEVBQUUsRUFBRSxDQUN0RixDQUFDLENBQUM7SUFFSCx3RUFBd0U7SUFDeEUsMkJBQTJCO0lBQ1gsT0FBTyxHQUFHLFFBQVEsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxrQkFBa0IsQ0FDekQsSUFBSSxDQUFDLElBQUksRUFBRSxFQUNYLEVBQUUsTUFBTSxFQUFFLElBQUksQ0FBQyxZQUFZLENBQUMsYUFBYSxFQUFFLENBQUMsTUFBTSxFQUFFLFFBQVEsRUFBRSxJQUFJLENBQUMsWUFBWSxDQUFDLFFBQVEsRUFBRSxFQUFFLEVBQzVGLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxrQkFBa0IsRUFBRSxJQUFJLEVBQUUsQ0FDN0MsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFFRyxZQUFZLEdBQUcsTUFBTSxDQUFDLHdCQUF3QixDQUFDLENBQUM7d0dBakN0RCxxQkFBcUI7NEZBQXJCLHFCQUFxQiwrcUJDaERsQyw0aUVBa0VBLG05Q0Q5Qkksc0JBQXNCLHdJQUN0QixrQkFBa0I7OzRGQVdULHFCQUFxQjtrQkFuQmpDLFNBQVM7K0JBQ0UsMEJBQTBCLG1CQUduQix1QkFBdUIsQ0FBQyxNQUFNLGNBQ25DLElBQUksV0FDUDt3QkFDUCxzQkFBc0I7d0JBQ3RCLGtCQUFrQjtxQkFDbkIsUUFDSzt3QkFDSixpQ0FBaUMsRUFBRSxrQkFBa0I7d0JBQ3JELG1DQUFtQyxFQUFFLGFBQWE7d0JBQ2xELG1DQUFtQyxFQUFFLGFBQWE7d0JBQ2xELGtDQUFrQyxFQUFFLFlBQVk7d0JBQ2hELHNDQUFzQyxFQUFFLGdCQUFnQjt3QkFDeEQsd0NBQXdDLEVBQUUsa0JBQWtCO3FCQUM3RCIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENoYW5nZURldGVjdGlvblN0cmF0ZWd5LCBDb21wb25lbnQsIGNvbXB1dGVkLCBpbmplY3QsIGlucHV0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbmltcG9ydCB7IFJlcG9ydENoYW5nZVBvbGFyaXR5IH0gZnJvbSAnLi4vLi4vZW51bXMvcmVwb3J0LWNoYW5nZS1wb2xhcml0eS5lbnVtJztcbmltcG9ydCB7IFJlcG9ydEtwaVRyZW5kIH0gZnJvbSAnLi4vLi4vZW51bXMvcmVwb3J0LWtwaS10cmVuZC5lbnVtJztcbmltcG9ydCB7IENvbXBvbmVudERhdGEsIEtwaUNvbmZpZywgUmVwb3J0Q29tcG9uZW50IH0gZnJvbSAnLi4vLi4vaW50ZXJmYWNlcy9yZXBvcnQuaW50ZXJmYWNlJztcbmltcG9ydCB7IGtwaUNhcmRWaWV3IH0gZnJvbSAnLi4vLi4va3BpLWNhcmQnO1xuaW1wb3J0IHsgY29tcG9uZW50Tm90ZUxpbmVzIH0gZnJvbSAnLi4vLi4vb3V0cHV0LW5vdGVzJztcbmltcG9ydCB7IFJlcG9ydEZpbHRlclN0YXRlU2VydmljZSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL3JlcG9ydC1maWx0ZXItc3RhdGUuc2VydmljZSc7XG5pbXBvcnQgeyBSRVBPUlRfS1BJX0NIQU5HRV9DT0xPUlMsIFJFUE9SVF9LUElfU1BBUktMSU5FX0NPTE9SIH0gZnJvbSAnLi4vLi4vdGhlbWUvcGFsZXR0ZSc7XG5pbXBvcnQgeyBSRVBPUlRfS1BJX1ZBTFVFX0ZPTlRfU0laRSB9IGZyb20gJy4uLy4uL3RoZW1lL3R5cG9ncmFwaHknO1xuaW1wb3J0IHsgREVGQVVMVF9URVhUX0NPTE9SIH0gZnJvbSAnLi4vLi4vdGV4dCc7XG5pbXBvcnQgeyBDb21wb25lbnROb3RlQ29tcG9uZW50IH0gZnJvbSAnLi4vY29tcG9uZW50LW5vdGUvY29tcG9uZW50LW5vdGUuY29tcG9uZW50JztcblxuaW1wb3J0IHsgU3BhcmtsaW5lQ29tcG9uZW50IH0gZnJvbSAnLi9jb21wb25lbnRzL3NwYXJrbGluZS9zcGFya2xpbmUuY29tcG9uZW50JztcblxuXG4vLyBBIHNpbmdsZSBiaWcgbnVtYmVyOiB0aGUgY29tcG9uZW50J3MgbWVhc3VyZSBhZ2dyZWdhdGVkIG92ZXIgYWxsIChmaWx0ZXJlZClcbi8vIHJvd3Mgc2VydmVyLXNpZGUsIGluIHRoZSBvcGVuIHJlcG9ydCdzIGZvcm1hdCBjb250ZXh0IChpdHMgbG9jYWxlLCBpdHNcbi8vIGN1cnJlbmN5LCBhbmQgd2hldGhlciBhIHBlcmNlbnQgYXJyaXZlcyBhcyBhIDAtMSByYXRpbyBvciwgZnJvbSBhbiBvbGRlclxuLy8gc2VydmVyLCBhbHJlYWR5IHgxMDApLiBXaXRoIGEgY29tcGFyaXNvbiwgYSB0YXJnZXQgb3IgYSBzcGFya2xpbmUgdGhlIHNlcnZlclxuLy8gc2VuZHMgZXZlcnkgb3RoZXIgbnVtYmVyIHRoZSBjYXJkIHNob3dzIHRvbyAoQ29tcG9uZW50RGF0YS5rcGkpOyBrcGktY2FyZC50c1xuLy8gZGVjaWRlcyB3aGF0IGVhY2ggbGluZSBzYXlzLCBhbmQgdGhpcyBvbmx5IGxheXMgdGhlbSBvdXQsIGluIG9yZGVyOiB0aGVcbi8vIHZhbHVlIGFuZCBpdHMgbGFiZWwsIHRoZSBkZWx0YSwgdGhlIGRheXMsIHRoZSB0YXJnZXQgYW5kIHRoZSB0cmVuZCwgdGhlbiB0aGVcbi8vIG5vdGUuXG4vL1xuLy8gVGhlIHBhbGV0dGUncyBjb2xvcnMgYW5kIHRoZSB2YWx1ZSdzIHNpemUgYXJyaXZlIGFzIGN1c3RvbSBwcm9wZXJ0aWVzIGZyb21cbi8vIHRoZSBzYW1lIGNvbnN0YW50cyB0aGUgZXhwb3J0cyBkcmF3IHdpdGguIEEgaG9zdCBwYWdlIG92ZXJyaWRlcyBhIGNvbG9yIGJ5XG4vLyBzZXR0aW5nIC0tcmVwb3J0LWtwaS1nb29kLWNvbG9yLCAtLXJlcG9ydC1rcGktYmFkLWNvbG9yLFxuLy8gLS1yZXBvcnQta3BpLW5ldXRyYWwtY29sb3Igb3IgLS1yZXBvcnQta3BpLXNwYXJrbGluZS1jb2xvciBhcm91bmQgdGhlIHJlcG9ydC5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ2FwcC1yZXBvcnQtY29tcG9uZW50LWtwaScsXG4gIHRlbXBsYXRlVXJsOiAnLi9jb21wb25lbnQta3BpLmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmxzOiBbJy4vY29tcG9uZW50LWtwaS5jb21wb25lbnQuc2NzcyddLFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgaW1wb3J0czogW1xuICAgIENvbXBvbmVudE5vdGVDb21wb25lbnQsXG4gICAgU3BhcmtsaW5lQ29tcG9uZW50LFxuICBdLFxuICBob3N0OiB7XG4gICAgJ1tzdHlsZS4tLXJlcG9ydC1rcGktdmFsdWUtc2l6ZV0nOiAndG9rZW5zLnZhbHVlU2l6ZScsXG4gICAgJ1tzdHlsZS4tLXJlcG9ydC1rcGktdGV4dC1kZWZhdWx0XSc6ICd0b2tlbnMudGV4dCcsXG4gICAgJ1tzdHlsZS4tLXJlcG9ydC1rcGktZ29vZC1kZWZhdWx0XSc6ICd0b2tlbnMuZ29vZCcsXG4gICAgJ1tzdHlsZS4tLXJlcG9ydC1rcGktYmFkLWRlZmF1bHRdJzogJ3Rva2Vucy5iYWQnLFxuICAgICdbc3R5bGUuLS1yZXBvcnQta3BpLW5ldXRyYWwtZGVmYXVsdF0nOiAndG9rZW5zLm5ldXRyYWwnLFxuICAgICdbc3R5bGUuLS1yZXBvcnQta3BpLXNwYXJrbGluZS1kZWZhdWx0XSc6ICd0b2tlbnMuc3BhcmtsaW5lJyxcbiAgfSxcbn0pXG5leHBvcnQgY2xhc3MgQ29tcG9uZW50S3BpQ29tcG9uZW50IHtcblxuICBwdWJsaWMgcmVhZG9ubHkgY29tcG9uZW50ID0gaW5wdXQucmVxdWlyZWQ8UmVwb3J0Q29tcG9uZW50PigpO1xuICBwdWJsaWMgcmVhZG9ubHkgZGF0YSA9IGlucHV0PENvbXBvbmVudERhdGEgfCBudWxsPihudWxsKTtcblxuICBwdWJsaWMgcmVhZG9ubHkgUmVwb3J0S3BpVHJlbmQgPSBSZXBvcnRLcGlUcmVuZDtcblxuICBwdWJsaWMgcmVhZG9ubHkgdG9rZW5zID0ge1xuICAgIHZhbHVlU2l6ZTogYCR7UkVQT1JUX0tQSV9WQUxVRV9GT05UX1NJWkV9cHhgLFxuICAgIHRleHQ6IERFRkFVTFRfVEVYVF9DT0xPUixcbiAgICBnb29kOiBgIyR7UkVQT1JUX0tQSV9DSEFOR0VfQ09MT1JTW1JlcG9ydENoYW5nZVBvbGFyaXR5Lkdvb2RdfWAsXG4gICAgYmFkOiBgIyR7UkVQT1JUX0tQSV9DSEFOR0VfQ09MT1JTW1JlcG9ydENoYW5nZVBvbGFyaXR5LkJhZF19YCxcbiAgICBuZXV0cmFsOiBgIyR7UkVQT1JUX0tQSV9DSEFOR0VfQ09MT1JTW1JlcG9ydENoYW5nZVBvbGFyaXR5Lk5ldXRyYWxdfWAsXG4gICAgc3BhcmtsaW5lOiBgIyR7UkVQT1JUX0tQSV9TUEFSS0xJTkVfQ09MT1J9YCxcbiAgfTtcblxuICAvLyBSZWFkIG9mZiB0aGUgY29tcG9uZW50IG9uIGV2ZXJ5IGRhdGEgY2hhbmdlIHJhdGhlciB0aGFuIHRocm91Z2ggYSBtZW1vIG9mXG4gIC8vIHRoZSBjb25maWc6IGEgc2V0dGluZ3Mgc2F2ZSByZXBhaW50cyB0aGUgc2FtZSBjb21wb25lbnQgb2JqZWN0IHdpdGggbmV3XG4gIC8vIEtQSSBrZXlzLCB0aGVuIHJlbG9hZHMgaXRzIGRhdGEuXG4gIHB1YmxpYyByZWFkb25seSB2aWV3ID0gY29tcHV0ZWQoKCkgPT4ga3BpQ2FyZFZpZXcoXG4gICAgKHRoaXMuY29tcG9uZW50KCkuY29uZmlnID8/IHt9KSBhcyBLcGlDb25maWcsXG4gICAgdGhpcy5kYXRhKCksXG4gICAgeyBmb3JtYXQ6IHRoaXMuX2ZpbHRlclN0YXRlLmZvcm1hdENvbnRleHQoKSwgdGltZXpvbmU6IHRoaXMuX2ZpbHRlclN0YXRlLnRpbWV6b25lKCkgfSxcbiAgKSk7XG5cbiAgLy8gVGhlIG5vdGUgc2l0cyB1bmRlciB0aGUgY2FyZCBvbmx5IHdoZW4gaXQgaGFzIHNvbWV0aGluZyB0byBzYXksIHNvIGFuXG4gIC8vIGVtcHR5IG9uZSB0YWtlcyBubyByb29tLlxuICBwdWJsaWMgcmVhZG9ubHkgaGFzTm90ZSA9IGNvbXB1dGVkKCgpID0+IGNvbXBvbmVudE5vdGVMaW5lcyhcbiAgICB0aGlzLmRhdGEoKSxcbiAgICB7IGxvY2FsZTogdGhpcy5fZmlsdGVyU3RhdGUuZm9ybWF0Q29udGV4dCgpLmxvY2FsZSwgdGltZXpvbmU6IHRoaXMuX2ZpbHRlclN0YXRlLnRpbWV6b25lKCkgfSxcbiAgICB7IHdpbmRvd3M6IGZhbHNlLCB3aW5kb3dEZXNjcmlwdGlvbnM6IHRydWUgfSxcbiAgKS5sZW5ndGggPiAwKTtcblxuICBwcml2YXRlIHJlYWRvbmx5IF9maWx0ZXJTdGF0ZSA9IGluamVjdChSZXBvcnRGaWx0ZXJTdGF0ZVNlcnZpY2UpO1xufVxuIiwiQGxldCBjYXJkID0gdmlldygpO1xuPGRpdiBjbGFzcz1cImtwaSBmcy1jb2x1bW4uZ2FwLXhzLmFsaWduLWNlbnRlci5qdXN0aWZ5LWNlbnRlclwiPlxuICA8ZGl2IGNsYXNzPVwia3BpLXZhbHVlXCI+e3sgY2FyZC52YWx1ZSB9fTwvZGl2PlxuICBAaWYgKGNhcmQubGFiZWwpIHtcbiAgICA8ZGl2IGNsYXNzPVwia3BpLWxhYmVsIHNtYWxsXCI+e3sgY2FyZC5sYWJlbCB9fTwvZGl2PlxuICB9XG4gIEBpZiAoY2FyZC5kZWx0YTsgYXMgZGVsdGEpIHtcbiAgICA8ZGl2IGNsYXNzPVwia3BpLWRlbHRhIGZzLXJvdy5nYXAteHMuYWxpZ24tYmFzZWxpbmUuanVzdGlmeS1jZW50ZXIud3JhcFwiPlxuICAgICAgPHNwYW5cbiAgICAgICAgICBjbGFzcz1cImtwaS1jaGFuZ2VcIlxuICAgICAgICAgIFthdHRyLmRhdGEtdG9uZV09XCJkZWx0YS50b25lXCI+XG4gICAgICAgIEBpZiAoZGVsdGEudHJlbmQgIT09IFJlcG9ydEtwaVRyZW5kLkZsYXQpIHtcbiAgICAgICAgICA8c3ZnXG4gICAgICAgICAgICAgIGNsYXNzPVwia3BpLWFycm93XCJcbiAgICAgICAgICAgICAgW2NsYXNzLmRvd25dPVwiZGVsdGEudHJlbmQgPT09IFJlcG9ydEtwaVRyZW5kLkRvd25cIlxuICAgICAgICAgICAgICB2aWV3Qm94PVwiMCAwIDEwIDEwXCI+XG4gICAgICAgICAgICA8cG9seWdvbiBwb2ludHM9XCI1LDEgOS41LDkgMC41LDlcIj48L3BvbHlnb24+XG4gICAgICAgICAgPC9zdmc+XG4gICAgICAgIH1cbiAgICAgICAge3sgZGVsdGEudGV4dCB9fVxuICAgICAgPC9zcGFuPlxuICAgICAgQGlmIChkZWx0YS5jb21wYXJlZCkge1xuICAgICAgICA8c3BhbiBjbGFzcz1cInNtYWxsXCI+e3sgZGVsdGEuY29tcGFyZWQgfX08L3NwYW4+XG4gICAgICB9XG4gICAgPC9kaXY+XG4gIH1cbiAgQGlmIChjYXJkLndpbmRvdykge1xuICAgIDxkaXYgY2xhc3M9XCJrcGktbGluZSBzbWFsbFwiPnt7IGNhcmQud2luZG93IH19PC9kaXY+XG4gIH1cbiAgQGlmIChjYXJkLnRhcmdldDsgYXMgdGFyZ2V0KSB7XG4gICAgPGRpdiBjbGFzcz1cImtwaS10YXJnZXQgZnMtY29sdW1uLmdhcC14cy5hbGlnbi1jZW50ZXJcIj5cbiAgICAgIDxkaXYgY2xhc3M9XCJrcGktbGluZSBzbWFsbFwiPlxuICAgICAgICB7eyB0YXJnZXQudGV4dCB9fVxuICAgICAgICBAaWYgKHRhcmdldC5wcm9ncmVzcykge1xuICAgICAgICAgIMK3IHt7IHRhcmdldC5wcm9ncmVzcyB9fVxuICAgICAgICB9XG4gICAgICA8L2Rpdj5cbiAgICAgIEBpZiAodGFyZ2V0LmZpbGwgIT09IG51bGwpIHtcbiAgICAgICAgPGRpdlxuICAgICAgICAgICAgY2xhc3M9XCJrcGktcHJvZ3Jlc3NcIlxuICAgICAgICAgICAgW2F0dHIuZGF0YS10b25lXT1cInRhcmdldC50b25lXCI+XG4gICAgICAgICAgPGRpdlxuICAgICAgICAgICAgY2xhc3M9XCJrcGktcHJvZ3Jlc3MtZmlsbFwiXG4gICAgICAgICAgICBbc3R5bGUud2lkdGguJV09XCJ0YXJnZXQuZmlsbCAqIDEwMFwiPlxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICA8L2Rpdj5cbiAgICAgIH1cbiAgICA8L2Rpdj5cbiAgfVxuICBAaWYgKGNhcmQuc3BhcmtsaW5lOyBhcyBzcGFya2xpbmUpIHtcbiAgICA8YXBwLWNvbXBvbmVudC1rcGktc3BhcmtsaW5lXG4gICAgICBjbGFzcz1cImtwaS1zcGFya2xpbmVcIlxuICAgICAgW3ZhbHVlc109XCJzcGFya2xpbmUudmFsdWVzXCJcbiAgICAgIFtwYXJ0aWFsXT1cInNwYXJrbGluZS5wYXJ0aWFsXCI+XG4gICAgPC9hcHAtY29tcG9uZW50LWtwaS1zcGFya2xpbmU+XG4gIH1cbiAgPCEtLSBUaGUgY2FyZCBwcmludHMgaXRzIG93biBkYXlzLCBzbyB0aGUgbm90ZSBsZWF2ZXMgdGhlbSBvdXQuIEEgS1BJIGhhcyBub1xuICAgICAgIHRvb2x0aXAsIHNvIGl0cyBub3RlIGFsc28gc2F5cyB3aGF0IGEgd2luZG93ZWQgdmFsdWUgaXMuIC0tPlxuICBAaWYgKGhhc05vdGUoKSkge1xuICAgIDxhcHAtcmVwb3J0LWNvbXBvbmVudC1ub3RlXG4gICAgICBbZGF0YV09XCJkYXRhKClcIlxuICAgICAgW3dpbmRvd3NdPVwiZmFsc2VcIlxuICAgICAgW3dpbmRvd0Rlc2NyaXB0aW9uc109XCJ0cnVlXCI+XG4gICAgPC9hcHAtcmVwb3J0LWNvbXBvbmVudC1ub3RlPlxuICB9XG48L2Rpdj5cbiJdfQ==
@@ -0,0 +1,19 @@
1
+ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
2
+ import { kpiSparklineGeometry } from '../../../../kpi-card';
3
+ import * as i0 from "@angular/core";
4
+ // A KPI's trend over its last periods: a line that stretches to the card's
5
+ // width, broken where a period has no value, with the last point marked
6
+ // (hollow while its period is still running). The markers are drawn over the
7
+ // line rather than in it, so they stay round however the line is stretched.
8
+ export class SparklineComponent {
9
+ values = input([]);
10
+ partial = input(false);
11
+ geometry = computed(() => kpiSparklineGeometry(this.values(), this.partial()));
12
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SparklineComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: SparklineComponent, isStandalone: true, selector: "app-component-kpi-sparkline", inputs: { values: { classPropertyName: "values", publicName: "values", isSignal: true, isRequired: false, transformFunction: null }, partial: { classPropertyName: "partial", publicName: "partial", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<svg\n class=\"sparkline-lines\"\n viewBox=\"0 0 100 100\"\n preserveAspectRatio=\"none\">\n @for (line of geometry().lines; track $index) {\n <polyline [attr.points]=\"line\"></polyline>\n }\n</svg>\n@for (dot of geometry().dots; track $index) {\n <span\n class=\"sparkline-marker\"\n [style.left.%]=\"dot.x\"\n [style.top.%]=\"dot.y\">\n </span>\n}\n@if (geometry().last; as last) {\n <span\n class=\"sparkline-marker\"\n [class.hollow]=\"last.hollow\"\n [style.left.%]=\"last.x\"\n [style.top.%]=\"last.y\">\n </span>\n}\n", styles: [":host{display:block;position:relative;width:calc(100% - 6px);height:28px;margin:3px;color:var(--report-kpi-sparkline)}.sparkline-lines{display:block;width:100%;height:100%;overflow:visible}.sparkline-lines polyline{fill:none;stroke:currentColor;stroke-width:1.5;stroke-linejoin:round;stroke-linecap:round;vector-effect:non-scaling-stroke}.sparkline-marker{position:absolute;box-sizing:border-box;width:6px;height:6px;margin:-3px 0 0 -3px;border:1.5px solid currentColor;border-radius:50%;background:currentColor}.sparkline-marker.hollow{background:Canvas}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
14
+ }
15
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SparklineComponent, decorators: [{
16
+ type: Component,
17
+ args: [{ selector: 'app-component-kpi-sparkline', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, template: "<svg\n class=\"sparkline-lines\"\n viewBox=\"0 0 100 100\"\n preserveAspectRatio=\"none\">\n @for (line of geometry().lines; track $index) {\n <polyline [attr.points]=\"line\"></polyline>\n }\n</svg>\n@for (dot of geometry().dots; track $index) {\n <span\n class=\"sparkline-marker\"\n [style.left.%]=\"dot.x\"\n [style.top.%]=\"dot.y\">\n </span>\n}\n@if (geometry().last; as last) {\n <span\n class=\"sparkline-marker\"\n [class.hollow]=\"last.hollow\"\n [style.left.%]=\"last.x\"\n [style.top.%]=\"last.y\">\n </span>\n}\n", styles: [":host{display:block;position:relative;width:calc(100% - 6px);height:28px;margin:3px;color:var(--report-kpi-sparkline)}.sparkline-lines{display:block;width:100%;height:100%;overflow:visible}.sparkline-lines polyline{fill:none;stroke:currentColor;stroke-width:1.5;stroke-linejoin:round;stroke-linecap:round;vector-effect:non-scaling-stroke}.sparkline-marker{position:absolute;box-sizing:border-box;width:6px;height:6px;margin:-3px 0 0 -3px;border:1.5px solid currentColor;border-radius:50%;background:currentColor}.sparkline-marker.hollow{background:Canvas}\n"] }]
18
+ }] });
19
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3BhcmtsaW5lLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL3NyYy9hcHAvcmVwb3J0cy9jb21wb25lbnRzL2NvbXBvbmVudC1rcGkvY29tcG9uZW50cy9zcGFya2xpbmUvc3BhcmtsaW5lLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL3NyYy9hcHAvcmVwb3J0cy9jb21wb25lbnRzL2NvbXBvbmVudC1rcGkvY29tcG9uZW50cy9zcGFya2xpbmUvc3BhcmtsaW5lLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLEtBQUssRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUVwRixPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQzs7QUFHNUQsMkVBQTJFO0FBQzNFLHdFQUF3RTtBQUN4RSw2RUFBNkU7QUFDN0UsNEVBQTRFO0FBUTVFLE1BQU0sT0FBTyxrQkFBa0I7SUFFYixNQUFNLEdBQUcsS0FBSyxDQUFvQixFQUFFLENBQUMsQ0FBQztJQUN0QyxPQUFPLEdBQUcsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBRXZCLFFBQVEsR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFLENBQUMsb0JBQW9CLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxFQUFFLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDLENBQUM7d0dBTHBGLGtCQUFrQjs0RkFBbEIsa0JBQWtCLDRWQ2hCL0Isc2pCQXVCQTs7NEZEUGEsa0JBQWtCO2tCQVA5QixTQUFTOytCQUNFLDZCQUE2QixtQkFHdEIsdUJBQXVCLENBQUMsTUFBTSxjQUNuQyxJQUFJIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksIENvbXBvbmVudCwgY29tcHV0ZWQsIGlucHV0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbmltcG9ydCB7IGtwaVNwYXJrbGluZUdlb21ldHJ5IH0gZnJvbSAnLi4vLi4vLi4vLi4va3BpLWNhcmQnO1xuXG5cbi8vIEEgS1BJJ3MgdHJlbmQgb3ZlciBpdHMgbGFzdCBwZXJpb2RzOiBhIGxpbmUgdGhhdCBzdHJldGNoZXMgdG8gdGhlIGNhcmQnc1xuLy8gd2lkdGgsIGJyb2tlbiB3aGVyZSBhIHBlcmlvZCBoYXMgbm8gdmFsdWUsIHdpdGggdGhlIGxhc3QgcG9pbnQgbWFya2VkXG4vLyAoaG9sbG93IHdoaWxlIGl0cyBwZXJpb2QgaXMgc3RpbGwgcnVubmluZykuIFRoZSBtYXJrZXJzIGFyZSBkcmF3biBvdmVyIHRoZVxuLy8gbGluZSByYXRoZXIgdGhhbiBpbiBpdCwgc28gdGhleSBzdGF5IHJvdW5kIGhvd2V2ZXIgdGhlIGxpbmUgaXMgc3RyZXRjaGVkLlxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnYXBwLWNvbXBvbmVudC1rcGktc3BhcmtsaW5lJyxcbiAgdGVtcGxhdGVVcmw6ICcuL3NwYXJrbGluZS5jb21wb25lbnQuaHRtbCcsXG4gIHN0eWxlVXJsczogWycuL3NwYXJrbGluZS5jb21wb25lbnQuc2NzcyddLFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbn0pXG5leHBvcnQgY2xhc3MgU3BhcmtsaW5lQ29tcG9uZW50IHtcblxuICBwdWJsaWMgcmVhZG9ubHkgdmFsdWVzID0gaW5wdXQ8KG51bWJlciB8IG51bGwpW10+KFtdKTtcbiAgcHVibGljIHJlYWRvbmx5IHBhcnRpYWwgPSBpbnB1dChmYWxzZSk7XG5cbiAgcHVibGljIHJlYWRvbmx5IGdlb21ldHJ5ID0gY29tcHV0ZWQoKCkgPT4ga3BpU3BhcmtsaW5lR2VvbWV0cnkodGhpcy52YWx1ZXMoKSwgdGhpcy5wYXJ0aWFsKCkpKTtcbn1cbiIsIjxzdmdcbiAgICBjbGFzcz1cInNwYXJrbGluZS1saW5lc1wiXG4gICAgdmlld0JveD1cIjAgMCAxMDAgMTAwXCJcbiAgICBwcmVzZXJ2ZUFzcGVjdFJhdGlvPVwibm9uZVwiPlxuICBAZm9yIChsaW5lIG9mIGdlb21ldHJ5KCkubGluZXM7IHRyYWNrICRpbmRleCkge1xuICAgIDxwb2x5bGluZSBbYXR0ci5wb2ludHNdPVwibGluZVwiPjwvcG9seWxpbmU+XG4gIH1cbjwvc3ZnPlxuQGZvciAoZG90IG9mIGdlb21ldHJ5KCkuZG90czsgdHJhY2sgJGluZGV4KSB7XG4gIDxzcGFuXG4gICAgY2xhc3M9XCJzcGFya2xpbmUtbWFya2VyXCJcbiAgICBbc3R5bGUubGVmdC4lXT1cImRvdC54XCJcbiAgICBbc3R5bGUudG9wLiVdPVwiZG90LnlcIj5cbiAgPC9zcGFuPlxufVxuQGlmIChnZW9tZXRyeSgpLmxhc3Q7IGFzIGxhc3QpIHtcbiAgPHNwYW5cbiAgICBjbGFzcz1cInNwYXJrbGluZS1tYXJrZXJcIlxuICAgIFtjbGFzcy5ob2xsb3ddPVwibGFzdC5ob2xsb3dcIlxuICAgIFtzdHlsZS5sZWZ0LiVdPVwibGFzdC54XCJcbiAgICBbc3R5bGUudG9wLiVdPVwibGFzdC55XCI+XG4gIDwvc3Bhbj5cbn1cbiJdfQ==