@firestitch/report 18.11.1 → 18.13.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 (63) hide show
  1. package/app/reports/components/component-grid/component-grid.component.d.ts +33 -0
  2. package/app/reports/components/component-grid/report-grid-cell.pipe.d.ts +11 -0
  3. package/app/reports/components/component-grid/report-grid-formatter.d.ts +9 -0
  4. package/app/reports/components/report-component/report-component.component.d.ts +0 -1
  5. package/app/reports/data/report-grid-source.d.ts +22 -0
  6. package/app/reports/data/report.data.d.ts +3 -1
  7. package/app/reports/export/report-export-collector.service.d.ts +8 -1
  8. package/app/reports/export/report-filter-lines.d.ts +2 -0
  9. package/app/reports/export/report-grid-export.d.ts +20 -0
  10. package/app/reports/export/report-pdf.service.d.ts +2 -0
  11. package/app/reports/export/report-pptx.service.d.ts +1 -0
  12. package/app/reports/group-by.d.ts +3 -0
  13. package/app/reports/interfaces/report-chat-viewer-state.interface.d.ts +2 -0
  14. package/app/reports/interfaces/report-chat-viewer-tile.interface.d.ts +5 -0
  15. package/app/reports/interfaces/report-export-snapshot.interface.d.ts +2 -0
  16. package/app/reports/interfaces/report-grid-config.interface.d.ts +5 -0
  17. package/app/reports/interfaces/report-grid-line.interface.d.ts +6 -0
  18. package/app/reports/interfaces/report-grid-params.interface.d.ts +7 -0
  19. package/app/reports/interfaces/report-grid-report.interface.d.ts +2 -0
  20. package/app/reports/interfaces/report-grid-session-layout.interface.d.ts +5 -0
  21. package/app/reports/interfaces/report.interface.d.ts +10 -2
  22. package/app/reports/report-chat-viewer-state.d.ts +4 -1
  23. package/app/reports/report-filter-items.d.ts +1 -1
  24. package/app/reports/report-grid-layout.d.ts +10 -0
  25. package/app/reports/services/report-filter-state.service.d.ts +13 -0
  26. package/app/reports/tile-data-state.d.ts +2 -2
  27. package/esm2022/app/reports/components/component-grid/component-grid.component.mjs +120 -0
  28. package/esm2022/app/reports/components/component-grid/report-grid-cell.pipe.mjs +41 -0
  29. package/esm2022/app/reports/components/component-grid/report-grid-formatter.mjs +94 -0
  30. package/esm2022/app/reports/components/component-text/component-text.component.mjs +1 -1
  31. package/esm2022/app/reports/components/report-component/report-component.component.mjs +28 -25
  32. package/esm2022/app/reports/data/report-grid-source.mjs +105 -0
  33. package/esm2022/app/reports/data/report.data.mjs +15 -4
  34. package/esm2022/app/reports/export/report-export-collector.service.mjs +72 -7
  35. package/esm2022/app/reports/export/report-filter-lines.mjs +68 -1
  36. package/esm2022/app/reports/export/report-grid-export.mjs +48 -0
  37. package/esm2022/app/reports/export/report-pdf.service.mjs +47 -17
  38. package/esm2022/app/reports/export/report-pptx.service.mjs +48 -4
  39. package/esm2022/app/reports/group-by.mjs +13 -0
  40. package/esm2022/app/reports/interfaces/report-chat-viewer-state.interface.mjs +1 -1
  41. package/esm2022/app/reports/interfaces/report-chat-viewer-tile.interface.mjs +2 -0
  42. package/esm2022/app/reports/interfaces/report-export-snapshot.interface.mjs +1 -1
  43. package/esm2022/app/reports/interfaces/report-grid-config.interface.mjs +2 -0
  44. package/esm2022/app/reports/interfaces/report-grid-line.interface.mjs +2 -0
  45. package/esm2022/app/reports/interfaces/report-grid-params.interface.mjs +2 -0
  46. package/esm2022/app/reports/interfaces/report-grid-report.interface.mjs +2 -0
  47. package/esm2022/app/reports/interfaces/report-grid-session-layout.interface.mjs +2 -0
  48. package/esm2022/app/reports/interfaces/report.interface.mjs +1 -1
  49. package/esm2022/app/reports/report-chat-viewer-state.mjs +21 -6
  50. package/esm2022/app/reports/report-filter-items.mjs +9 -7
  51. package/esm2022/app/reports/report-grid-layout.mjs +67 -0
  52. package/esm2022/app/reports/report-tile.mjs +5 -5
  53. package/esm2022/app/reports/services/report-filter-state.service.mjs +170 -18
  54. package/esm2022/app/reports/tile-data-state.mjs +12 -17
  55. package/esm2022/app/reports/views/report/fs-ai-report.component.mjs +8 -4
  56. package/esm2022/app/reports/views/report/report.component.mjs +2 -2
  57. package/fesm2022/{firestitch-report-echarts-B0RWUjR8.mjs → firestitch-report-echarts-EecIloKo.mjs} +2 -2
  58. package/fesm2022/{firestitch-report-echarts-B0RWUjR8.mjs.map → firestitch-report-echarts-EecIloKo.mjs.map} +1 -1
  59. package/fesm2022/{firestitch-report-firestitch-report-CJW0lLdW.mjs → firestitch-report-firestitch-report-DbdLxQSE.mjs} +945 -109
  60. package/fesm2022/firestitch-report-firestitch-report-DbdLxQSE.mjs.map +1 -0
  61. package/fesm2022/firestitch-report.mjs +1 -1
  62. package/package.json +4 -2
  63. package/fesm2022/firestitch-report-firestitch-report-CJW0lLdW.mjs.map +0 -1
@@ -0,0 +1,33 @@
1
+ import { OnInit } from '@angular/core';
2
+ import { GridExpansion, GridLayout } from '@firestitch/grid';
3
+ import { ReportGridSource } from '../../data/report-grid-source';
4
+ import { ReportGridParams } from '../../interfaces/report-grid-params.interface';
5
+ import { ReportComponent } from '../../interfaces/report.interface';
6
+ import * as i0 from "@angular/core";
7
+ export declare class ComponentGridComponent implements OnInit {
8
+ readonly reportId: import("@angular/core").InputSignal<number>;
9
+ readonly component: import("@angular/core").InputSignal<ReportComponent>;
10
+ readonly params: import("@angular/core").WritableSignal<ReportGridParams>;
11
+ readonly controls: import("@angular/core").Signal<import("@firestitch/grid").GridControls>;
12
+ readonly source: import("@angular/core").Signal<ReportGridSource>;
13
+ readonly layout: import("@angular/core").Signal<GridLayout>;
14
+ readonly clearedFilter: import("@angular/core").Signal<import("@firestitch/grid").GridClearedFilter>;
15
+ readonly expansion: import("@angular/core").WritableSignal<GridExpansion>;
16
+ readonly noteData: import("@angular/core").Signal<import("../../interfaces/report-grid-report.interface").ReportGridReport>;
17
+ readonly formatContext: import("@angular/core").Signal<import("../../interfaces/report.interface").ReportFormatContext>;
18
+ readonly formatter: import("@angular/core").Signal<import("@firestitch/grid").GridFormatter>;
19
+ private readonly _componentId;
20
+ private _reportData;
21
+ private _filterState;
22
+ private _destroyRef;
23
+ private _paramsVersion;
24
+ ngOnInit(): void;
25
+ setLayout(layout: GridLayout): void;
26
+ setExpansion(expansion: GridExpansion | undefined): void;
27
+ private _init;
28
+ private _settled$;
29
+ private _applyParams;
30
+ private _readParams;
31
+ static ɵfac: i0.ɵɵFactoryDeclaration<ComponentGridComponent, never>;
32
+ static ɵcmp: i0.ɵɵComponentDeclaration<ComponentGridComponent, "app-report-component-grid", never, { "reportId": { "alias": "reportId"; "required": true; "isSignal": true; }; "component": { "alias": "component"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
33
+ }
@@ -0,0 +1,11 @@
1
+ import { PipeTransform } from '@angular/core';
2
+ import { GridField } from '@firestitch/grid';
3
+ import { FormattedOutputValue } from '../../format';
4
+ import { ReportGridReport } from '../../interfaces/report-grid-report.interface';
5
+ import { ReportFormatContext } from '../../interfaces/report.interface';
6
+ import * as i0 from "@angular/core";
7
+ export declare class ReportGridCellPipe implements PipeTransform {
8
+ transform(value: unknown, field: GridField | null, record: Record<string, unknown> | undefined, text: string, report: ReportGridReport | null, format: ReportFormatContext): FormattedOutputValue;
9
+ static ɵfac: i0.ɵɵFactoryDeclaration<ReportGridCellPipe, never>;
10
+ static ɵpipe: i0.ɵɵPipeDeclaration<ReportGridCellPipe, "fsReportGridCell", true>;
11
+ }
@@ -0,0 +1,9 @@
1
+ import { GridFormatter } from '@firestitch/grid';
2
+ import { ReportGridReport } from '../../interfaces/report-grid-report.interface';
3
+ import { ReportFormatContext } from '../../interfaces/report.interface';
4
+ export interface ReportGridFormatContext {
5
+ format: ReportFormatContext;
6
+ timezone: string;
7
+ report: ReportGridReport | null;
8
+ }
9
+ export declare function reportGridFormatter(context: ReportGridFormatContext): GridFormatter;
@@ -102,7 +102,6 @@ export declare class ReportComponentComponent implements OnInit, OnChanges {
102
102
  private _origin;
103
103
  private _track;
104
104
  private _emitGeometry;
105
- private get _isTimeSeries();
106
105
  private get _heightAuto();
107
106
  get heightAuto(): boolean;
108
107
  private _inches;
@@ -0,0 +1,22 @@
1
+ import { GridParams, GridRequest, GridResult, GridSource } from '@firestitch/grid';
2
+ import { Observable } from 'rxjs';
3
+ import { ReportGridReport } from '../interfaces/report-grid-report.interface';
4
+ import { ReportLocaleTag } from '../interfaces/report.interface';
5
+ import { ReportData } from './report.data';
6
+ export interface ReportGridSourceOptions {
7
+ isExport?: boolean;
8
+ locale?: () => ReportLocaleTag;
9
+ }
10
+ export declare class ReportGridSource implements GridSource {
11
+ private readonly _reportData;
12
+ private readonly _reportId;
13
+ private readonly _componentId;
14
+ readonly report: import("@angular/core").WritableSignal<ReportGridReport>;
15
+ private readonly _isExport;
16
+ private readonly _locale;
17
+ constructor(_reportData: ReportData, _reportId: number, _componentId: number, options?: ReportGridSourceOptions);
18
+ read(request: GridRequest, params: GridParams): Observable<GridResult>;
19
+ private _stream$;
20
+ private _part$;
21
+ private _described;
22
+ }
@@ -1,4 +1,5 @@
1
- import { RequestConfig } from '@firestitch/api';
1
+ import { RequestConfig, StreamEvent } from '@firestitch/api';
2
+ import { GridRequest } from '@firestitch/grid';
2
3
  import { Observable } from 'rxjs';
3
4
  import { ReportExportLine } from '../interfaces/report-export-line.interface';
4
5
  import { ReportFilterGroupChanges } from '../interfaces/report-filter-group-changes.interface';
@@ -54,6 +55,7 @@ export declare class ReportData<T = any> {
54
55
  h: number;
55
56
  }>)[], config?: RequestConfig): Observable<T>;
56
57
  componentData(reportId: number, componentId: number, filters?: ResolvedFilterValue[], state?: ComponentDataState, isExport?: boolean, config?: RequestConfig): Observable<T>;
58
+ componentGrid(reportId: number, componentId: number, filters: ResolvedFilterValue[], state: ComponentDataState, grid: GridRequest, isExport?: boolean, config?: RequestConfig): Observable<StreamEvent>;
57
59
  pageValues(reportId: number, pageId: number, filters?: ResolvedFilterValue[], isExport?: boolean, config?: RequestConfig, matchAnyGroupIds?: number[]): Observable<ReportPageValue[]>;
58
60
  exportComponent(reportId: number, componentId: number, filters?: ResolvedFilterValue[], state?: ComponentDataState, title?: string, config?: RequestConfig): Observable<ComponentExportResponse>;
59
61
  exportReport(reportId: number, filters?: ResolvedFilterValue[], pageId?: number | null, frequency?: ChartGranularity | null, filterLines?: ReportExportLine[], states?: Record<number, ComponentDataState>, titles?: Record<number, string>, config?: RequestConfig): Observable<T>;
@@ -1,3 +1,4 @@
1
+ import { GridTable } from '@firestitch/grid';
1
2
  import { Observable } from 'rxjs';
2
3
  import { ReportExportLine } from '../interfaces/report-export-line.interface';
3
4
  import { ReportExportSnapshot } from '../interfaces/report-export-snapshot.interface';
@@ -12,6 +13,9 @@ export interface CollectedComponent {
12
13
  search?: string;
13
14
  hiddenColumns: string[];
14
15
  crumbs?: string[];
16
+ table?: GridTable;
17
+ tableNotes?: string[];
18
+ filtered?: boolean;
15
19
  }
16
20
  export interface CollectedPage {
17
21
  page: ReportPage;
@@ -67,7 +71,7 @@ export interface KpiExportParts {
67
71
  }
68
72
  export declare const HEADER_FOOTER_NAME = "Header & footer";
69
73
  export declare function emptyLabel(item: CollectedComponent): string;
70
- export declare function exportTitle(component: ReportComponent, tokens: TextTokenContext, search?: string | null, crumbs?: readonly string[] | null): string;
74
+ export declare function exportTitle(component: ReportComponent, tokens: TextTokenContext, search?: string | null, crumbs?: readonly string[] | null, filtered?: boolean): string;
71
75
  export declare function isFailed(item: CollectedComponent): boolean;
72
76
  export declare function textOverflow(component: ReportComponent, page: string, tokens: TextTokenContext): TextOverflow;
73
77
  export declare function coverLines(collected: CollectedReport): string[];
@@ -79,6 +83,7 @@ export declare function listHeadings(component: ReportComponent, data: Component
79
83
  export declare function listTotalsRow(data: ComponentData, columns: readonly ExportListColumn[], timezone: string, context: ReportFormatContext): string[] | null;
80
84
  export declare function exportWindowsLine(item: CollectedComponent, timezone: string, locale: ReportLocaleTag): string;
81
85
  export declare function exportNoteLines(item: CollectedComponent, timezone: string, locale: ReportLocaleTag): string[];
86
+ export declare function isEmptyTile(item: CollectedComponent): boolean;
82
87
  export declare function kpiCardHasExtras(view: KpiCardView, notes: readonly string[]): boolean;
83
88
  export declare function kpiExportParts(view: KpiCardView, notes: readonly string[], available: number, heights: KpiExportHeights): KpiExportParts;
84
89
  export declare function scaledKpiHeights(heights: KpiExportHeights, scale: number): KpiExportHeights;
@@ -99,6 +104,8 @@ export declare class ReportExportCollectorService {
99
104
  private _valuesJobs;
100
105
  private _runJob;
101
106
  private _componentData;
107
+ private _tileState;
108
+ private _gridTable;
102
109
  private _collected;
103
110
  private _groupValues;
104
111
  private _pageValues;
@@ -1,3 +1,4 @@
1
+ import { GridField, GridFilter } from '@firestitch/grid';
1
2
  import { Observable } from 'rxjs';
2
3
  import { ReportDrillStep } from '../interfaces/report-drill-step.interface';
3
4
  import { ReportExportLine } from '../interfaces/report-export-line.interface';
@@ -17,3 +18,4 @@ export declare function matchLineText(match: ReportFilterMatch | null | undefine
17
18
  export declare function drillLineText(component: Pick<ReportComponent, 'title'>, path: readonly ReportDrillStep[], formatContext: Pick<ReportFormatContext, 'locale'>): string;
18
19
  export declare function exportLines(input: ReportExportLinesInput): Observable<ReportExportLine[]>;
19
20
  export declare function exportGroups(report: Report, pageId: number | null): ReportFilterGroup[];
21
+ export declare function gridFilterLineText(label: string, filter: GridFilter, field: GridField | undefined, locale: ReportFormatContext['locale']): string | null;
@@ -0,0 +1,20 @@
1
+ import { GridLayout, GridTable } from '@firestitch/grid';
2
+ import { ReportFormatContext } from '../interfaces/report.interface';
3
+ export declare const GRID_EXPORT_COLUMNS = 60;
4
+ export interface GridExportCell {
5
+ text: string;
6
+ colSpan: number;
7
+ rowSpan: number;
8
+ bold: boolean;
9
+ indent: number;
10
+ numeric: boolean;
11
+ }
12
+ export interface GridExportTable {
13
+ head: GridExportCell[][];
14
+ body: GridExportCell[][];
15
+ foot: GridExportCell[] | null;
16
+ numeric: boolean[];
17
+ moreRows: number;
18
+ }
19
+ export declare function gridExportTable(table: GridTable): GridExportTable;
20
+ export declare function gridExportNotes(table: GridTable, layout: GridLayout, locale: ReportFormatContext['locale']): string[];
@@ -33,6 +33,8 @@ export declare class ReportPdfService {
33
33
  private _componentTitle;
34
34
  private _boxNote;
35
35
  private _renderTable;
36
+ private _listCells;
37
+ private _gridCells;
36
38
  private _tableCells;
37
39
  private _tableOptions;
38
40
  private _noticeRow;
@@ -47,6 +47,7 @@ export declare class ReportPptxService {
47
47
  private _categoryTitleOptions;
48
48
  private _valueAxisRangeOptions;
49
49
  private _renderTable;
50
+ private _renderGridTable;
50
51
  private _renderKpi;
51
52
  private _kpiDelta;
52
53
  private _estimatedWidth;
@@ -0,0 +1,3 @@
1
+ import { ReportComponent } from './interfaces/report.interface';
2
+ export declare function isTimeSeriesChart(component: ReportComponent): boolean;
3
+ export declare function followsGroupBy(component: ReportComponent): boolean;
@@ -3,6 +3,7 @@ import { ReportChatViewerDetail } from './report-chat-viewer-detail.interface';
3
3
  import { ReportChatViewerDrill } from './report-chat-viewer-drill.interface';
4
4
  import { ReportChatViewerFilter } from './report-chat-viewer-filter.interface';
5
5
  import { ReportChatViewerSelection } from './report-chat-viewer-selection.interface';
6
+ import { ReportChatViewerTile } from './report-chat-viewer-tile.interface';
6
7
  import { ReportFilterMatch } from './report-filter-match.type';
7
8
  import { ChartGranularity } from './report.interface';
8
9
  export interface ReportChatViewerState {
@@ -13,5 +14,6 @@ export interface ReportChatViewerState {
13
14
  crossFilters?: ReportChatViewerCrossFilter[];
14
15
  drills?: ReportChatViewerDrill[];
15
16
  detail?: ReportChatViewerDetail | null;
17
+ tiles?: ReportChatViewerTile[];
16
18
  selection?: ReportChatViewerSelection;
17
19
  }
@@ -0,0 +1,5 @@
1
+ import { GridLayout } from '@firestitch/grid';
2
+ export interface ReportChatViewerTile {
3
+ componentId: number;
4
+ layout?: GridLayout;
5
+ }
@@ -1,3 +1,4 @@
1
+ import { GridExpansion } from '@firestitch/grid';
1
2
  import { ReportStateSnapshot } from './report-state-snapshot.interface';
2
3
  import { ReportViewerFeature } from './report-viewer-feature.type';
3
4
  import { ChartGranularity, ComponentDataState, FilterGroupValue, ReportFormatContext, ReportRelativeRange, ResolvedFilterValue } from './report.interface';
@@ -8,6 +9,7 @@ export interface ReportExportSnapshot {
8
9
  filters: Record<number, ResolvedFilterValue[]>;
9
10
  valueFilters: Record<number, ResolvedFilterValue[]>;
10
11
  states: Record<number, ComponentDataState>;
12
+ gridExpansions?: Record<number, GridExpansion>;
11
13
  frequency: ChartGranularity | null;
12
14
  features: ReportViewerFeature[];
13
15
  formatContext: ReportFormatContext;
@@ -0,0 +1,5 @@
1
+ import { GridControls, GridLayout } from '@firestitch/grid';
2
+ export interface GridConfig {
3
+ layout?: GridLayout;
4
+ controls?: GridControls;
5
+ }
@@ -0,0 +1,6 @@
1
+ import { GridResult } from '@firestitch/grid';
2
+ import { ReportGridReport } from './report-grid-report.interface';
3
+ export interface ReportGridLine {
4
+ report?: ReportGridReport;
5
+ grid?: GridResult;
6
+ }
@@ -0,0 +1,7 @@
1
+ import { GridParams } from '@firestitch/grid';
2
+ import { ChartGranularity, ResolvedFilterValue } from './report.interface';
3
+ export interface ReportGridParams extends GridParams {
4
+ filters: ResolvedFilterValue[];
5
+ matchAnyGroupIds?: number[];
6
+ frequency?: ChartGranularity;
7
+ }
@@ -0,0 +1,2 @@
1
+ import { ComponentData } from './report.interface';
2
+ export type ReportGridReport = Pick<ComponentData, 'applied' | 'timeframe' | 'comparison' | 'outputs' | 'windows'>;
@@ -0,0 +1,5 @@
1
+ import { GridLayout } from '@firestitch/grid';
2
+ export interface ReportGridSessionLayout {
3
+ layout: GridLayout;
4
+ base: string;
5
+ }
@@ -1,3 +1,4 @@
1
+ import { GridLayout } from '@firestitch/grid';
1
2
  import { ReportChangePolarity } from '../enums/report-change-polarity.enum';
2
3
  import { ReportChartColor } from '../enums/report-chart-color.enum';
3
4
  import { ReportChartDataLabels } from '../enums/report-chart-data-labels.enum';
@@ -11,6 +12,7 @@ import { ReportValueFormula } from '../enums/report-value-formula.enum';
11
12
  import { ComponentDataApplied } from './component-data-applied.interface';
12
13
  import { ComponentDataDrill } from './component-data-drill.interface';
13
14
  import { ReportDrillStep } from './report-drill-step.interface';
15
+ import { GridConfig } from './report-grid-config.interface';
14
16
  import { ReportViewerFeature } from './report-viewer-feature.type';
15
17
  export type ReportLocaleTag = string;
16
18
  export type ReportCurrencyCode = string;
@@ -84,7 +86,7 @@ export interface ReportPage {
84
86
  components: ReportComponent[];
85
87
  values?: ReportComponent[];
86
88
  }
87
- export type ReportComponentType = 'chart' | 'list' | 'kpi' | 'text' | 'value';
89
+ export type ReportComponentType = 'chart' | 'list' | 'grid' | 'kpi' | 'text' | 'value';
88
90
  export interface ReportComponent {
89
91
  id: number;
90
92
  type: ReportComponentType;
@@ -95,8 +97,13 @@ export interface ReportComponent {
95
97
  h: number;
96
98
  autoHeight: boolean;
97
99
  sql: string | null;
98
- config: ChartConfig & ListConfig & KpiConfig & TextConfig & ValueConfig & ComponentLayoutConfig & Record<string, unknown>;
100
+ config: ChartConfig & ListConfig & GridConfig & KpiConfig & TextConfig & ValueConfig & ComponentLayoutConfig & Record<string, unknown>;
99
101
  filters: ReportFilter[];
102
+ followsGroupBy?: ReportGroupByField | null;
103
+ }
104
+ export interface ReportGroupByField {
105
+ field: string;
106
+ granularity: ChartGranularity | null;
100
107
  }
101
108
  export interface ReportComponentSql {
102
109
  sql: string | null;
@@ -323,6 +330,7 @@ export interface ComponentDataState {
323
330
  hiddenColumns?: string[];
324
331
  drill?: ReportDrillStep[];
325
332
  matchAnyGroupIds?: number[];
333
+ layout?: GridLayout;
326
334
  unchangedIfUnreached?: true;
327
335
  }
328
336
  export interface ComponentData {
@@ -1,8 +1,11 @@
1
+ import { GridLayout } from '@firestitch/grid';
1
2
  import { ReportChatViewerDrill } from './interfaces/report-chat-viewer-drill.interface';
2
3
  import { ReportChatViewerState } from './interfaces/report-chat-viewer-state.interface';
4
+ import { ReportChatViewerTile } from './interfaces/report-chat-viewer-tile.interface';
3
5
  import { ReportStateSnapshot } from './interfaces/report-state-snapshot.interface';
4
- import { ReportLocaleTag } from './interfaces/report.interface';
6
+ import { Report, ReportComponent, ReportLocaleTag } from './interfaces/report.interface';
5
7
  export declare const CHAT_DRILLS_MAX = 10;
6
8
  export declare const CHAT_LABEL_MAX = 100;
7
9
  export declare function chatViewerMatch(snapshot: ReportStateSnapshot): Pick<ReportChatViewerState, 'match'>;
8
10
  export declare function chatViewerDrills(snapshot: ReportStateSnapshot, locale: ReportLocaleTag): ReportChatViewerDrill[];
11
+ export declare function chatViewerTiles(report: Report | null, pageId: number | null, layoutOf: (component: ReportComponent) => GridLayout | null): ReportChatViewerTile[];
@@ -39,7 +39,7 @@ export declare function matchFromQuery(query: Record<string, unknown>): ReportFi
39
39
  export declare const LIST_SEARCH_ITEM = "listSearch";
40
40
  export declare const LIST_SEARCH_MAX_LENGTH = 100;
41
41
  export declare function listSearchItem(value: string | null): IFilterConfigItem;
42
- export declare function hasTimeSeriesChart(report: Report | null): boolean;
42
+ export declare function hasGroupByTile(report: Report | null): boolean;
43
43
  export declare function isGranularityOffered(report: Report, granularity: ChartGranularity | null): boolean;
44
44
  export declare function groupByFilterItem(report: Report, initial?: ChartGranularity | null): IFilterConfigItem | null;
45
45
  export declare function groupBySignature(report: Report | null): string;
@@ -0,0 +1,10 @@
1
+ import { GridClearedFilter, GridGranularity, GridLayout } from '@firestitch/grid';
2
+ import { ChartGranularity, ReportComponent } from './interfaces/report.interface';
3
+ export declare const GRID_GRANULARITIES: Record<ChartGranularity, GridGranularity>;
4
+ export declare function authoredGridLayout(component: ReportComponent): GridLayout;
5
+ export declare function gridLayoutKey(layout: GridLayout): string;
6
+ export declare function rebasedGridLayout(session: GridLayout, base: string, authored: GridLayout): GridLayout;
7
+ export declare function grainedGridLayout(layout: GridLayout, field: string, granularity: ChartGranularity): {
8
+ layout: GridLayout;
9
+ cleared: GridClearedFilter | null;
10
+ };
@@ -1,3 +1,4 @@
1
+ import { GridClearedFilter, GridExpansion, GridLayout } from '@firestitch/grid';
1
2
  import { Observable } from 'rxjs';
2
3
  import { ReportChatViewerFilter } from '../interfaces/report-chat-viewer-filter.interface';
3
4
  import { ReportDrillStep } from '../interfaces/report-drill-step.interface';
@@ -49,6 +50,9 @@ export declare class ReportFilterStateService {
49
50
  private _match;
50
51
  private _drills;
51
52
  private _drillChanged$;
53
+ private _gridLayouts;
54
+ private _gridExpansions;
55
+ private _gridClearedFilters;
52
56
  private _temporary;
53
57
  init(report: Report): void;
54
58
  value(groupId: number): FilterGroupValue | undefined;
@@ -82,6 +86,12 @@ export declare class ReportFilterStateService {
82
86
  setDrill(componentId: number, path: readonly ReportDrillStep[]): void;
83
87
  drillChanges(componentId: number): Observable<void>;
84
88
  adoptDrill(componentId: number, path: readonly ReportDrillStep[]): boolean;
89
+ gridLayout(component: ReportComponent): GridLayout;
90
+ gridSessionLayout(component: ReportComponent): GridLayout | null;
91
+ setGridLayout(component: ReportComponent, layout: GridLayout): void;
92
+ gridClearedFilter(component: ReportComponent): GridClearedFilter | null;
93
+ gridExpansion(component: ReportComponent): GridExpansion | undefined;
94
+ setGridExpansion(component: ReportComponent, expansion: GridExpansion | undefined): void;
85
95
  snapshot(): ReportStateSnapshot;
86
96
  authoredConfig(): ReportStateSnapshot;
87
97
  resetToDefaults(): void;
@@ -103,10 +113,13 @@ export declare class ReportFilterStateService {
103
113
  private _snapshotTiles;
104
114
  private _seedAuthored;
105
115
  private _clearTiles;
116
+ private _putGridLayout;
117
+ private _grainGridLayouts;
106
118
  private _setDrillPath;
107
119
  private _notifyReset;
108
120
  private _tiles;
109
121
  private _isChart;
122
+ private _pruneGridLayouts;
110
123
  private _prunePageState;
111
124
  private _resolveExcludeAndRange;
112
125
  private _excludeOrRangeDefault;
@@ -1,7 +1,7 @@
1
+ import { GridLayout } from '@firestitch/grid';
1
2
  import { ReportStateSnapshot } from './interfaces/report-state-snapshot.interface';
2
3
  import { ComponentDataState, Report, ReportComponent } from './interfaces/report.interface';
3
- export declare function isTimeSeriesChart(component: ReportComponent): boolean;
4
4
  export declare function tileDataState(report: Report | null, component: ReportComponent, snapshot: ReportStateSnapshot): ComponentDataState;
5
- export declare function zipExportStates(report: Report, snapshot: ReportStateSnapshot, pageId?: number | null): Record<number, ComponentDataState>;
5
+ export declare function zipExportStates(report: Report, snapshot: ReportStateSnapshot, pageId?: number | null, gridLayout?: (component: ReportComponent) => GridLayout | null): Record<number, ComponentDataState>;
6
6
  export declare function valuesMatchAnyGroupIds(report: Report | null, layerId: number | null, snapshot: ReportStateSnapshot): number[];
7
7
  export declare function tilePageId(report: Report, component: ReportComponent, snapshot: ReportStateSnapshot): number | null;
@@ -0,0 +1,120 @@
1
+ import { ChangeDetectionStrategy, Component, DestroyRef, computed, inject, input, signal, } from '@angular/core';
2
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
3
+ import { FsGridCellDirective, FsGridComponent } from '@firestitch/grid';
4
+ import { filter, map, of, switchMap, tap, timer } from 'rxjs';
5
+ import { ReportData } from '../../data/report.data';
6
+ import { ReportGridSource } from '../../data/report-grid-source';
7
+ import { followsGroupBy } from '../../group-by';
8
+ import { ReportFilterStateService } from '../../services/report-filter-state.service';
9
+ import { TILE_REFRESH_DEBOUNCE_MS } from '../../tile-loads';
10
+ import { ComponentNoteComponent } from '../component-note/component-note.component';
11
+ import { ReportGridCellPipe } from './report-grid-cell.pipe';
12
+ import { reportGridFormatter } from './report-grid-formatter';
13
+ import * as i0 from "@angular/core";
14
+ // A grid tile: fs-grid over the tile's rows, read through ReportGridSource.
15
+ //
16
+ // - The params (the report filters the tile resolves to and the 'any'
17
+ // groups) are seeded before the first render, so no read goes out without
18
+ // them. After that a change to one of the tile's filter groups settles for
19
+ // TILE_REFRESH_DEBOUNCE_MS, so a sweep of changes costs one read; one sent
20
+ // under an older state version is dropped.
21
+ // - The layout on screen is the state service's (gridLayout): the viewer's
22
+ // own while they changed it from the layout authored now, else the
23
+ // authored one. It outlives a page switch, and the chat reads it. So do
24
+ // the groups the viewer left open (gridExpansion), which a Clear leaves.
25
+ // - A state version move (Clear filters, a saved filter) skips the wait; the
26
+ // reset has put the authored layout back in the same tick, so it costs one
27
+ // read.
28
+ // - A table that follows the report's group-by (followsGroupBy) sends its
29
+ // grain, and reads again at once when it changes. The state service has
30
+ // moved its filter on the group-by's field to the new grain by then, or
31
+ // taken it out (clearedFilter, which the status line names), so one read
32
+ // goes out and nothing paints unfiltered.
33
+ // - fs-grid compares the layout and params by value: a cloned value never
34
+ // reads again.
35
+ // - An output's detail cell is drawn by fsReportGridCell, so a change tells
36
+ // a missing prior from a zero one and says why on hover, as a list does.
37
+ export class ComponentGridComponent {
38
+ reportId = input.required();
39
+ component = input.required();
40
+ params = signal(null);
41
+ controls = computed(() => this.component().config?.controls ?? {});
42
+ source = computed(() => new ReportGridSource(this._reportData, this.reportId(), this._componentId(), {
43
+ locale: () => this._filterState.formatContext().locale,
44
+ }));
45
+ layout = computed(() => this._filterState.gridLayout(this.component()));
46
+ clearedFilter = computed(() => this._filterState.gridClearedFilter(this.component()));
47
+ // Read once: fs-grid owns the open groups from then on and writes them out.
48
+ expansion = signal(undefined);
49
+ noteData = computed(() => this.source().report());
50
+ formatContext = computed(() => this._filterState.formatContext());
51
+ formatter = computed(() => reportGridFormatter({
52
+ format: this._filterState.formatContext(),
53
+ timezone: this._filterState.timezone(),
54
+ report: this.source().report(),
55
+ }));
56
+ _componentId = computed(() => this.component().id);
57
+ _reportData = inject(ReportData);
58
+ _filterState = inject(ReportFilterStateService);
59
+ _destroyRef = inject(DestroyRef);
60
+ // The state version the params on screen were read under.
61
+ _paramsVersion = 0;
62
+ ngOnInit() {
63
+ this.expansion.set(this._filterState.gridExpansion(this.component()));
64
+ this._init();
65
+ }
66
+ setLayout(layout) {
67
+ this._filterState.setGridLayout(this.component(), layout);
68
+ }
69
+ setExpansion(expansion) {
70
+ this._filterState.setGridExpansion(this.component(), expansion);
71
+ }
72
+ _init() {
73
+ this._paramsVersion = this._filterState.stateVersion();
74
+ this.params.set(this._readParams());
75
+ this._filterState.changesFor(this.component())
76
+ .pipe(switchMap(() => this._settled$()), tap(() => this._applyParams()), takeUntilDestroyed(this._destroyRef))
77
+ .subscribe();
78
+ if (followsGroupBy(this.component())) {
79
+ this._filterState.frequencyChanges()
80
+ .pipe(takeUntilDestroyed(this._destroyRef))
81
+ .subscribe(() => this._applyParams());
82
+ }
83
+ }
84
+ // A change, once it has settled: at once when the state version moved,
85
+ // else after the debounce; dropped when the version moves while it waits,
86
+ // since that move brings its own change.
87
+ _settled$() {
88
+ const version = this._filterState.stateVersion();
89
+ const moved = version !== this._paramsVersion;
90
+ return (moved ? of(0) : timer(TILE_REFRESH_DEBOUNCE_MS))
91
+ .pipe(filter(() => this._filterState.stateVersion() === version), map(() => undefined));
92
+ }
93
+ _applyParams() {
94
+ this._paramsVersion = this._filterState.stateVersion();
95
+ this.params.set(this._readParams());
96
+ }
97
+ // The tile's report filters, 'any' groups and group-by grain as they stand
98
+ // (tileState()).
99
+ _readParams() {
100
+ const component = this.component();
101
+ const { matchAnyGroupIds, frequency } = this._filterState.tileState(component);
102
+ return {
103
+ filters: this._filterState.resolveForComponent(component),
104
+ ...(matchAnyGroupIds?.length ? { matchAnyGroupIds } : {}),
105
+ ...(frequency ? { frequency } : {}),
106
+ };
107
+ }
108
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ComponentGridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
109
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: ComponentGridComponent, isStandalone: true, selector: "app-report-component-grid", inputs: { reportId: { classPropertyName: "reportId", publicName: "reportId", isSignal: true, isRequired: true, transformFunction: null }, component: { classPropertyName: "component", publicName: "component", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<!-- The comparison and window note strip above the table, from the report\n line of the latest read; the windows' descriptions and notes go in each\n column's \"About this column\" instead. -->\n<div class=\"fs-column grid-pane\">\n <app-report-component-note\n [data]=\"noteData()\"\n [windowNotes]=\"false\">\n </app-report-component-note>\n <!-- The params are seeded before the first render, so the grid's first read\n carries the tile's report filters. -->\n @if (params(); as params) {\n <fs-grid\n class=\"fs-flex\"\n [source]=\"source()\"\n [layout]=\"layout()\"\n [expansion]=\"expansion()\"\n [clearedFilter]=\"clearedFilter()\"\n (expansionChange)=\"setExpansion($event)\"\n [params]=\"params\"\n [controls]=\"controls()\"\n [formatter]=\"formatter()\"\n (layoutChange)=\"setLayout($event)\">\n <ng-template fsGridCell let-value let-field=\"field\" let-record=\"record\" let-text=\"text\">\n @if (value | fsReportGridCell: field : record : text : noteData() : formatContext(); as shown) {\n <span [attr.title]=\"shown.title\">{{ shown.text }}</span>\n }\n </ng-template>\n </fs-grid>\n }\n</div>\n", styles: [":host{display:block;height:100%}.grid-pane{height:100%;min-height:0}fs-grid{min-height:0}\n"], dependencies: [{ kind: "component", type: FsGridComponent, selector: "fs-grid", inputs: ["source", "layout", "controls", "params", "expansion", "panel", "editorOpen", "formatter", "clearedFilter"], outputs: ["layoutChange", "expansionChange", "panelChange", "editorOpenChange"] }, { kind: "directive", type: FsGridCellDirective, selector: "[fsGridCell]" }, { kind: "component", type: ComponentNoteComponent, selector: "app-report-component-note", inputs: ["data", "windows", "windowDescriptions", "windowNotes"] }, { kind: "pipe", type: ReportGridCellPipe, name: "fsReportGridCell" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
110
+ }
111
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ComponentGridComponent, decorators: [{
112
+ type: Component,
113
+ args: [{ selector: 'app-report-component-grid', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
114
+ FsGridComponent,
115
+ FsGridCellDirective,
116
+ ComponentNoteComponent,
117
+ ReportGridCellPipe,
118
+ ], template: "<!-- The comparison and window note strip above the table, from the report\n line of the latest read; the windows' descriptions and notes go in each\n column's \"About this column\" instead. -->\n<div class=\"fs-column grid-pane\">\n <app-report-component-note\n [data]=\"noteData()\"\n [windowNotes]=\"false\">\n </app-report-component-note>\n <!-- The params are seeded before the first render, so the grid's first read\n carries the tile's report filters. -->\n @if (params(); as params) {\n <fs-grid\n class=\"fs-flex\"\n [source]=\"source()\"\n [layout]=\"layout()\"\n [expansion]=\"expansion()\"\n [clearedFilter]=\"clearedFilter()\"\n (expansionChange)=\"setExpansion($event)\"\n [params]=\"params\"\n [controls]=\"controls()\"\n [formatter]=\"formatter()\"\n (layoutChange)=\"setLayout($event)\">\n <ng-template fsGridCell let-value let-field=\"field\" let-record=\"record\" let-text=\"text\">\n @if (value | fsReportGridCell: field : record : text : noteData() : formatContext(); as shown) {\n <span [attr.title]=\"shown.title\">{{ shown.text }}</span>\n }\n </ng-template>\n </fs-grid>\n }\n</div>\n", styles: [":host{display:block;height:100%}.grid-pane{height:100%;min-height:0}fs-grid{min-height:0}\n"] }]
119
+ }] });
120
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcG9uZW50LWdyaWQuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2FwcC9yZXBvcnRzL2NvbXBvbmVudHMvY29tcG9uZW50LWdyaWQvY29tcG9uZW50LWdyaWQuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2FwcC9yZXBvcnRzL2NvbXBvbmVudHMvY29tcG9uZW50LWdyaWQvY29tcG9uZW50LWdyaWQuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUNMLHVCQUF1QixFQUFFLFNBQVMsRUFBRSxVQUFVLEVBQVUsUUFBUSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUsTUFBTSxHQUN4RixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUVoRSxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsZUFBZSxFQUE2QixNQUFNLGtCQUFrQixDQUFDO0FBRW5HLE9BQU8sRUFBYyxNQUFNLEVBQUUsR0FBRyxFQUFFLEVBQUUsRUFBRSxTQUFTLEVBQUUsR0FBRyxFQUFFLEtBQUssRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUUxRSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDcEQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDakUsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBR2hELE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxNQUFNLDRDQUE0QyxDQUFDO0FBQ3RGLE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQzVELE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLDRDQUE0QyxDQUFDO0FBRXBGLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQzdELE9BQU8s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QUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1NBQ3BDLENBQUM7SUFDSixDQUFDO3dHQTdGVSxzQkFBc0I7NEZBQXRCLHNCQUFzQixvV0MxRG5DLHlzQ0E4QkEscUpEc0JJLGVBQWUsMlBBQ2YsbUJBQW1CLHlEQUNuQixzQkFBc0IsbUlBQ3RCLGtCQUFrQjs7NEZBR1Qsc0JBQXNCO2tCQWJsQyxTQUFTOytCQUNFLDJCQUEyQixtQkFHcEIsdUJBQXVCLENBQUMsTUFBTSxjQUNuQyxJQUFJLFdBQ1A7d0JBQ1AsZUFBZTt3QkFDZixtQkFBbUI7d0JBQ25CLHNCQUFzQjt3QkFDdEIsa0JBQWtCO3FCQUNuQiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIENoYW5nZURldGVjdGlvblN0cmF0ZWd5LCBDb21wb25lbnQsIERlc3Ryb3lSZWYsIE9uSW5pdCwgY29tcHV0ZWQsIGluamVjdCwgaW5wdXQsIHNpZ25hbCxcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyB0YWtlVW50aWxEZXN0cm95ZWQgfSBmcm9tICdAYW5ndWxhci9jb3JlL3J4anMtaW50ZXJvcCc7XG5cbmltcG9ydCB7IEZzR3JpZENlbGxEaXJlY3RpdmUsIEZzR3JpZENvbXBvbmVudCwgR3JpZEV4cGFuc2lvbiwgR3JpZExheW91dCB9IGZyb20gJ0BmaXJlc3RpdGNoL2dyaWQnO1xuXG5pbXBvcnQgeyBPYnNlcnZhYmxlLCBmaWx0ZXIsIG1hcCwgb2YsIHN3aXRjaE1hcCwgdGFwLCB0aW1lciB9IGZyb20gJ3J4anMnO1xuXG5pbXBvcnQgeyBSZXBvcnREYXRhIH0gZnJvbSAnLi4vLi4vZGF0YS9yZXBvcnQuZGF0YSc7XG5pbXBvcnQgeyBSZXBvcnRHcmlkU291cmNlIH0gZnJvbSAnLi4vLi4vZGF0YS9yZXBvcnQtZ3JpZC1zb3VyY2UnO1xuaW1wb3J0IHsgZm9sbG93c0dyb3VwQnkgfSBmcm9tICcuLi8uLi9ncm91cC1ieSc7XG5pbXBvcnQgeyBSZXBvcnRHcmlkUGFyYW1zIH0gZnJvbSAnLi4vLi4vaW50ZXJmYWNlcy9yZXBvcnQtZ3JpZC1wYXJhbXMuaW50ZXJmYWNlJztcbmltcG9ydCB7IFJlcG9ydENvbXBvbmVudCB9IGZyb20gJy4uLy4uL2ludGVyZmFjZXMvcmVwb3J0LmludGVyZmFjZSc7XG5pbXBvcnQgeyBSZXBvcnRGaWx0ZXJTdGF0ZVNlcnZpY2UgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9yZXBvcnQtZmlsdGVyLXN0YXRlLnNlcnZpY2UnO1xuaW1wb3J0IHsgVElMRV9SRUZSRVNIX0RFQk9VTkNFX01TIH0gZnJvbSAnLi4vLi4vdGlsZS1sb2Fkcyc7XG5pbXBvcnQgeyBDb21wb25lbnROb3RlQ29tcG9uZW50IH0gZnJvbSAnLi4vY29tcG9uZW50LW5vdGUvY29tcG9uZW50LW5vdGUuY29tcG9uZW50JztcblxuaW1wb3J0IHsgUmVwb3J0R3JpZENlbGxQaXBlIH0gZnJvbSAnLi9yZXBvcnQtZ3JpZC1jZWxsLnBpcGUnO1xuaW1wb3J0IHsgcmVwb3J0R3JpZEZvcm1hdHRlciB9IGZyb20gJy4vcmVwb3J0LWdyaWQtZm9ybWF0dGVyJztcblxuXG4vLyBBIGdyaWQgdGlsZTogZnMtZ3JpZCBvdmVyIHRoZSB0aWxlJ3Mgcm93cywgcmVhZCB0aHJvdWdoIFJlcG9ydEdyaWRTb3VyY2UuXG4vL1xuLy8gLSBUaGUgcGFyYW1zICh0aGUgcmVwb3J0IGZpbHRlcnMgdGhlIHRpbGUgcmVzb2x2ZXMgdG8gYW5kIHRoZSAnYW55J1xuLy8gICBncm91cHMpIGFyZSBzZWVkZWQgYmVmb3JlIHRoZSBmaXJzdCByZW5kZXIsIHNvIG5vIHJlYWQgZ29lcyBvdXQgd2l0aG91dFxuLy8gICB0aGVtLiBBZnRlciB0aGF0IGEgY2hhbmdlIHRvIG9uZSBvZiB0aGUgdGlsZSdzIGZpbHRlciBncm91cHMgc2V0dGxlcyBmb3Jcbi8vICAgVElMRV9SRUZSRVNIX0RFQk9VTkNFX01TLCBzbyBhIHN3ZWVwIG9mIGNoYW5nZXMgY29zdHMgb25lIHJlYWQ7IG9uZSBzZW50XG4vLyAgIHVuZGVyIGFuIG9sZGVyIHN0YXRlIHZlcnNpb24gaXMgZHJvcHBlZC5cbi8vIC0gVGhlIGxheW91dCBvbiBzY3JlZW4gaXMgdGhlIHN0YXRlIHNlcnZpY2UncyAoZ3JpZExheW91dCk6IHRoZSB2aWV3ZXInc1xuLy8gICBvd24gd2hpbGUgdGhleSBjaGFuZ2VkIGl0IGZyb20gdGhlIGxheW91dCBhdXRob3JlZCBub3csIGVsc2UgdGhlXG4vLyAgIGF1dGhvcmVkIG9uZS4gSXQgb3V0bGl2ZXMgYSBwYWdlIHN3aXRjaCwgYW5kIHRoZSBjaGF0IHJlYWRzIGl0LiBTbyBkb1xuLy8gICB0aGUgZ3JvdXBzIHRoZSB2aWV3ZXIgbGVmdCBvcGVuIChncmlkRXhwYW5zaW9uKSwgd2hpY2ggYSBDbGVhciBsZWF2ZXMuXG4vLyAtIEEgc3RhdGUgdmVyc2lvbiBtb3ZlIChDbGVhciBmaWx0ZXJzLCBhIHNhdmVkIGZpbHRlcikgc2tpcHMgdGhlIHdhaXQ7IHRoZVxuLy8gICByZXNldCBoYXMgcHV0IHRoZSBhdXRob3JlZCBsYXlvdXQgYmFjayBpbiB0aGUgc2FtZSB0aWNrLCBzbyBpdCBjb3N0cyBvbmVcbi8vICAgcmVhZC5cbi8vIC0gQSB0YWJsZSB0aGF0IGZvbGxvd3MgdGhlIHJlcG9ydCdzIGdyb3VwLWJ5IChmb2xsb3dzR3JvdXBCeSkgc2VuZHMgaXRzXG4vLyAgIGdyYWluLCBhbmQgcmVhZHMgYWdhaW4gYXQgb25jZSB3aGVuIGl0IGNoYW5nZXMuIFRoZSBzdGF0ZSBzZXJ2aWNlIGhhc1xuLy8gICBtb3ZlZCBpdHMgZmlsdGVyIG9uIHRoZSBncm91cC1ieSdzIGZpZWxkIHRvIHRoZSBuZXcgZ3JhaW4gYnkgdGhlbiwgb3Jcbi8vICAgdGFrZW4gaXQgb3V0IChjbGVhcmVkRmlsdGVyLCB3aGljaCB0aGUgc3RhdHVzIGxpbmUgbmFtZXMpLCBzbyBvbmUgcmVhZFxuLy8gICBnb2VzIG91dCBhbmQgbm90aGluZyBwYWludHMgdW5maWx0ZXJlZC5cbi8vIC0gZnMtZ3JpZCBjb21wYXJlcyB0aGUgbGF5b3V0IGFuZCBwYXJhbXMgYnkgdmFsdWU6IGEgY2xvbmVkIHZhbHVlIG5ldmVyXG4vLyAgIHJlYWRzIGFnYWluLlxuLy8gLSBBbiBvdXRwdXQncyBkZXRhaWwgY2VsbCBpcyBkcmF3biBieSBmc1JlcG9ydEdyaWRDZWxsLCBzbyBhIGNoYW5nZSB0ZWxsc1xuLy8gICBhIG1pc3NpbmcgcHJpb3IgZnJvbSBhIHplcm8gb25lIGFuZCBzYXlzIHdoeSBvbiBob3ZlciwgYXMgYSBsaXN0IGRvZXMuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdhcHAtcmVwb3J0LWNvbXBvbmVudC1ncmlkJyxcbiAgdGVtcGxhdGVVcmw6ICcuL2NvbXBvbmVudC1ncmlkLmNvbXBvbmVudC5odG1sJyxcbiAgc3R5bGVVcmxzOiBbJy4vY29tcG9uZW50LWdyaWQuY29tcG9uZW50LnNjc3MnXSxcbiAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2gsXG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIGltcG9ydHM6IFtcbiAgICBGc0dyaWRDb21wb25lbnQsXG4gICAgRnNHcmlkQ2VsbERpcmVjdGl2ZSxcbiAgICBDb21wb25lbnROb3RlQ29tcG9uZW50LFxuICAgIFJlcG9ydEdyaWRDZWxsUGlwZSxcbiAgXSxcbn0pXG5leHBvcnQgY2xhc3MgQ29tcG9uZW50R3JpZENvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCB7XG5cbiAgcHVibGljIHJlYWRvbmx5IHJlcG9ydElkID0gaW5wdXQucmVxdWlyZWQ8bnVtYmVyPigpO1xuICBwdWJsaWMgcmVhZG9ubHkgY29tcG9uZW50ID0gaW5wdXQucmVxdWlyZWQ8UmVwb3J0Q29tcG9uZW50PigpO1xuXG4gIHB1YmxpYyByZWFkb25seSBwYXJhbXMgPSBzaWduYWw8UmVwb3J0R3JpZFBhcmFtcyB8IG51bGw+KG51bGwpO1xuICBwdWJsaWMgcmVhZG9ubHkgY29udHJvbHMgPSBjb21wdXRlZCgoKSA9PiB0aGlzLmNvbXBvbmVudCgpLmNvbmZpZz8uY29udHJvbHMgPz8ge30pO1xuICBwdWJsaWMgcmVhZG9ubHkgc291cmNlID0gY29tcHV0ZWQoKCkgPT4gbmV3IFJlcG9ydEdyaWRTb3VyY2UodGhpcy5fcmVwb3J0RGF0YSwgdGhpcy5yZXBvcnRJZCgpLCB0aGlzLl9jb21wb25lbnRJZCgpLCB7XG4gICAgbG9jYWxlOiAoKSA9PiB0aGlzLl9maWx0ZXJTdGF0ZS5mb3JtYXRDb250ZXh0KCkubG9jYWxlLFxuICB9KSk7XG5cbiAgcHVibGljIHJlYWRvbmx5IGxheW91dCA9IGNvbXB1dGVkKCgpID0+IHRoaXMuX2ZpbHRlclN0YXRlLmdyaWRMYXlvdXQodGhpcy5jb21wb25lbnQoKSkpO1xuICBwdWJsaWMgcmVhZG9ubHkgY2xlYXJlZEZpbHRlciA9IGNvbXB1dGVkKCgpID0+IHRoaXMuX2ZpbHRlclN0YXRlLmdyaWRDbGVhcmVkRmlsdGVyKHRoaXMuY29tcG9uZW50KCkpKTtcbiAgLy8gUmVhZCBvbmNlOiBmcy1ncmlkIG93bnMgdGhlIG9wZW4gZ3JvdXBzIGZyb20gdGhlbiBvbiBhbmQgd3JpdGVzIHRoZW0gb3V0LlxuICBwdWJsaWMgcmVhZG9ubHkgZXhwYW5zaW9uID0gc2lnbmFsPEdyaWRFeHBhbnNpb24gfCB1bmRlZmluZWQ+KHVuZGVmaW5lZCk7XG5cbiAgcHVibGljIHJlYWRvbmx5IG5vdGVEYXRhID0gY29tcHV0ZWQoKCkgPT4gdGhpcy5zb3VyY2UoKS5yZXBvcnQoKSk7XG4gIHB1YmxpYyByZWFkb25seSBmb3JtYXRDb250ZXh0ID0gY29tcHV0ZWQoKCkgPT4gdGhpcy5fZmlsdGVyU3RhdGUuZm9ybWF0Q29udGV4dCgpKTtcbiAgcHVibGljIHJlYWRvbmx5IGZvcm1hdHRlciA9IGNvbXB1dGVkKCgpID0+IHJlcG9ydEdyaWRGb3JtYXR0ZXIoe1xuICAgIGZvcm1hdDogdGhpcy5fZmlsdGVyU3RhdGUuZm9ybWF0Q29udGV4dCgpLFxuICAgIHRpbWV6b25lOiB0aGlzLl9maWx0ZXJTdGF0ZS50aW1lem9uZSgpLFxuICAgIHJlcG9ydDogdGhpcy5zb3VyY2UoKS5yZXBvcnQoKSxcbiAgfSkpO1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgX2NvbXBvbmVudElkID0gY29tcHV0ZWQoKCkgPT4gdGhpcy5jb21wb25lbnQoKS5pZCk7XG4gIHByaXZhdGUgX3JlcG9ydERhdGEgPSBpbmplY3QoUmVwb3J0RGF0YSk7XG4gIHByaXZhdGUgX2ZpbHRlclN0YXRlID0gaW5qZWN0KFJlcG9ydEZpbHRlclN0YXRlU2VydmljZSk7XG4gIHByaXZhdGUgX2Rlc3Ryb3lSZWYgPSBpbmplY3QoRGVzdHJveVJlZik7XG4gIC8vIFRoZSBzdGF0ZSB2ZXJzaW9uIHRoZSBwYXJhbXMgb24gc2NyZWVuIHdlcmUgcmVhZCB1bmRlci5cbiAgcHJpdmF0ZSBfcGFyYW1zVmVyc2lvbiA9IDA7XG5cbiAgcHVibGljIG5nT25Jbml0KCk6IHZvaWQge1xuICAgIHRoaXMuZXhwYW5zaW9uLnNldCh0aGlzLl9maWx0ZXJTdGF0ZS5ncmlkRXhwYW5zaW9uKHRoaXMuY29tcG9uZW50KCkpKTtcbiAgICB0aGlzLl9pbml0KCk7XG4gIH1cblxuICBwdWJsaWMgc2V0TGF5b3V0KGxheW91dDogR3JpZExheW91dCk6IHZvaWQge1xuICAgIHRoaXMuX2ZpbHRlclN0YXRlLnNldEdyaWRMYXlvdXQodGhpcy5jb21wb25lbnQoKSwgbGF5b3V0KTtcbiAgfVxuXG4gIHB1YmxpYyBzZXRFeHBhbnNpb24oZXhwYW5zaW9uOiBHcmlkRXhwYW5zaW9uIHwgdW5kZWZpbmVkKTogdm9pZCB7XG4gICAgdGhpcy5fZmlsdGVyU3RhdGUuc2V0R3JpZEV4cGFuc2lvbih0aGlzLmNvbXBvbmVudCgpLCBleHBhbnNpb24pO1xuICB9XG5cbiAgcHJpdmF0ZSBfaW5pdCgpOiB2b2lkIHtcbiAgICB0aGlzLl9wYXJhbXNWZXJzaW9uID0gdGhpcy5fZmlsdGVyU3RhdGUuc3RhdGVWZXJzaW9uKCk7XG4gICAgdGhpcy5wYXJhbXMuc2V0KHRoaXMuX3JlYWRQYXJhbXMoKSk7XG5cbiAgICB0aGlzLl9maWx0ZXJTdGF0ZS5jaGFuZ2VzRm9yKHRoaXMuY29tcG9uZW50KCkpXG4gICAgICAucGlwZShcbiAgICAgICAgc3dpdGNoTWFwKCgpID0+IHRoaXMuX3NldHRsZWQkKCkpLFxuICAgICAgICB0YXAoKCkgPT4gdGhpcy5fYXBwbHlQYXJhbXMoKSksXG4gICAgICAgIHRha2VVbnRpbERlc3Ryb3llZCh0aGlzLl9kZXN0cm95UmVmKSxcbiAgICAgIClcbiAgICAgIC5zdWJzY3JpYmUoKTtcblxuICAgIGlmIChmb2xsb3dzR3JvdXBCeSh0aGlzLmNvbXBvbmVudCgpKSkge1xuICAgICAgdGhpcy5fZmlsdGVyU3RhdGUuZnJlcXVlbmN5Q2hhbmdlcygpXG4gICAgICAgIC5waXBlKHRha2VVbnRpbERlc3Ryb3llZCh0aGlzLl9kZXN0cm95UmVmKSlcbiAgICAgICAgLnN1YnNjcmliZSgoKSA9PiB0aGlzLl9hcHBseVBhcmFtcygpKTtcbiAgICB9XG4gIH1cblxuICAvLyBBIGNoYW5nZSwgb25jZSBpdCBoYXMgc2V0dGxlZDogYXQgb25jZSB3aGVuIHRoZSBzdGF0ZSB2ZXJzaW9uIG1vdmVkLFxuICAvLyBlbHNlIGFmdGVyIHRoZSBkZWJvdW5jZTsgZHJvcHBlZCB3aGVuIHRoZSB2ZXJzaW9uIG1vdmVzIHdoaWxlIGl0IHdhaXRzLFxuICAvLyBzaW5jZSB0aGF0IG1vdmUgYnJpbmdzIGl0cyBvd24gY2hhbmdlLlxuICBwcml2YXRlIF9zZXR0bGVkJCgpOiBPYnNlcnZhYmxlPHZvaWQ+IHtcbiAgICBjb25zdCB2ZXJzaW9uID0gdGhpcy5fZmlsdGVyU3RhdGUuc3RhdGVWZXJzaW9uKCk7XG4gICAgY29uc3QgbW92ZWQgPSB2ZXJzaW9uICE9PSB0aGlzLl9wYXJhbXNWZXJzaW9uO1xuXG4gICAgcmV0dXJuIChtb3ZlZCA/IG9mKDApIDogdGltZXIoVElMRV9SRUZSRVNIX0RFQk9VTkNFX01TKSlcbiAgICAgIC5waXBlKFxuICAgICAgICBmaWx0ZXIoKCkgPT4gdGhpcy5fZmlsdGVyU3RhdGUuc3RhdGVWZXJzaW9uKCkgPT09IHZlcnNpb24pLFxuICAgICAgICBtYXAoKCkgPT4gdW5kZWZpbmVkKSxcbiAgICAgICk7XG4gIH1cblxuICBwcml2YXRlIF9hcHBseVBhcmFtcygpOiB2b2lkIHtcbiAgICB0aGlzLl9wYXJhbXNWZXJzaW9uID0gdGhpcy5fZmlsdGVyU3RhdGUuc3RhdGVWZXJzaW9uKCk7XG4gICAgdGhpcy5wYXJhbXMuc2V0KHRoaXMuX3JlYWRQYXJhbXMoKSk7XG4gIH1cblxuICAvLyBUaGUgdGlsZSdzIHJlcG9ydCBmaWx0ZXJzLCAnYW55JyBncm91cHMgYW5kIGdyb3VwLWJ5IGdyYWluIGFzIHRoZXkgc3RhbmRcbiAgLy8gKHRpbGVTdGF0ZSgpKS5cbiAgcHJpdmF0ZSBfcmVhZFBhcmFtcygpOiBSZXBvcnRHcmlkUGFyYW1zIHtcbiAgICBjb25zdCBjb21wb25lbnQgPSB0aGlzLmNvbXBvbmVudCgpO1xuICAgIGNvbnN0IHsgbWF0Y2hBbnlHcm91cElkcywgZnJlcXVlbmN5IH0gPSB0aGlzLl9maWx0ZXJTdGF0ZS50aWxlU3RhdGUoY29tcG9uZW50KTtcblxuICAgIHJldHVybiB7XG4gICAgICBmaWx0ZXJzOiB0aGlzLl9maWx0ZXJTdGF0ZS5yZXNvbHZlRm9yQ29tcG9uZW50KGNvbXBvbmVudCksXG4gICAgICAuLi4obWF0Y2hBbnlHcm91cElkcz8ubGVuZ3RoID8geyBtYXRjaEFueUdyb3VwSWRzIH0gOiB7fSksXG4gICAgICAuLi4oZnJlcXVlbmN5ID8geyBmcmVxdWVuY3kgfSA6IHt9KSxcbiAgICB9O1xuICB9XG5cbn1cbiIsIjwhLS0gVGhlIGNvbXBhcmlzb24gYW5kIHdpbmRvdyBub3RlIHN0cmlwIGFib3ZlIHRoZSB0YWJsZSwgZnJvbSB0aGUgcmVwb3J0XG4gICAgIGxpbmUgb2YgdGhlIGxhdGVzdCByZWFkOyB0aGUgd2luZG93cycgZGVzY3JpcHRpb25zIGFuZCBub3RlcyBnbyBpbiBlYWNoXG4gICAgIGNvbHVtbidzIFwiQWJvdXQgdGhpcyBjb2x1bW5cIiBpbnN0ZWFkLiAtLT5cbjxkaXYgY2xhc3M9XCJmcy1jb2x1bW4gZ3JpZC1wYW5lXCI+XG4gIDxhcHAtcmVwb3J0LWNvbXBvbmVudC1ub3RlXG4gICAgW2RhdGFdPVwibm90ZURhdGEoKVwiXG4gICAgW3dpbmRvd05vdGVzXT1cImZhbHNlXCI+XG4gIDwvYXBwLXJlcG9ydC1jb21wb25lbnQtbm90ZT5cbiAgPCEtLSBUaGUgcGFyYW1zIGFyZSBzZWVkZWQgYmVmb3JlIHRoZSBmaXJzdCByZW5kZXIsIHNvIHRoZSBncmlkJ3MgZmlyc3QgcmVhZFxuICAgICAgIGNhcnJpZXMgdGhlIHRpbGUncyByZXBvcnQgZmlsdGVycy4gLS0+XG4gIEBpZiAocGFyYW1zKCk7IGFzIHBhcmFtcykge1xuICAgIDxmcy1ncmlkXG4gICAgICBjbGFzcz1cImZzLWZsZXhcIlxuICAgICAgW3NvdXJjZV09XCJzb3VyY2UoKVwiXG4gICAgICBbbGF5b3V0XT1cImxheW91dCgpXCJcbiAgICAgIFtleHBhbnNpb25dPVwiZXhwYW5zaW9uKClcIlxuICAgICAgW2NsZWFyZWRGaWx0ZXJdPVwiY2xlYXJlZEZpbHRlcigpXCJcbiAgICAgIChleHBhbnNpb25DaGFuZ2UpPVwic2V0RXhwYW5zaW9uKCRldmVudClcIlxuICAgICAgW3BhcmFtc109XCJwYXJhbXNcIlxuICAgICAgW2NvbnRyb2xzXT1cImNvbnRyb2xzKClcIlxuICAgICAgW2Zvcm1hdHRlcl09XCJmb3JtYXR0ZXIoKVwiXG4gICAgICAobGF5b3V0Q2hhbmdlKT1cInNldExheW91dCgkZXZlbnQpXCI+XG4gICAgICA8bmctdGVtcGxhdGUgZnNHcmlkQ2VsbCBsZXQtdmFsdWUgbGV0LWZpZWxkPVwiZmllbGRcIiBsZXQtcmVjb3JkPVwicmVjb3JkXCIgbGV0LXRleHQ9XCJ0ZXh0XCI+XG4gICAgICAgIEBpZiAodmFsdWUgfCBmc1JlcG9ydEdyaWRDZWxsOiBmaWVsZCA6IHJlY29yZCA6IHRleHQgOiBub3RlRGF0YSgpIDogZm9ybWF0Q29udGV4dCgpOyBhcyBzaG93bikge1xuICAgICAgICAgIDxzcGFuIFthdHRyLnRpdGxlXT1cInNob3duLnRpdGxlXCI+e3sgc2hvd24udGV4dCB9fTwvc3Bhbj5cbiAgICAgICAgfVxuICAgICAgPC9uZy10ZW1wbGF0ZT5cbiAgICA8L2ZzLWdyaWQ+XG4gIH1cbjwvZGl2PlxuIl19