@firestitch/report 18.21.0 → 18.21.2

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.
@@ -4,7 +4,7 @@ import * as echarts from 'echarts/core';
4
4
  export { echarts as default };
5
5
  import { LabelLayout } from 'echarts/features';
6
6
  import { CanvasRenderer } from 'echarts/renderers';
7
- import { R as REPORT_CHART_COLORS_CSS, a as REPORT_CHART_FONT_FAMILY, b as REPORT_AXIS_LABEL_FONT_SIZE } from './firestitch-report-firestitch-report-DyKfI3_M.mjs';
7
+ import { R as REPORT_CHART_COLORS_CSS, a as REPORT_CHART_FONT_FAMILY, b as REPORT_AXIS_LABEL_FONT_SIZE } from './firestitch-report-firestitch-report-D2dPznfE.mjs';
8
8
 
9
9
  // Tree-shaken ECharts build for the reports renderer: only the chart types and
10
10
  // components the option-builder emits are registered, keeping the lazy chunk
@@ -63,4 +63,4 @@ echarts.registerTheme('report', {
63
63
  axisLabel: { color: '#52606d', fontSize: REPORT_AXIS_LABEL_FONT_SIZE },
64
64
  },
65
65
  });
66
- //# sourceMappingURL=firestitch-report-echarts-C8ZS6OWj.mjs.map
66
+ //# sourceMappingURL=firestitch-report-echarts-BLr6SmeK.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"firestitch-report-echarts-C8ZS6OWj.mjs","sources":["../../src/app/reports/theme/echarts.ts"],"sourcesContent":["// Tree-shaken ECharts build for the reports renderer: only the chart types and\n// components the option-builder emits are registered, keeping the lazy chunk\n// lean. Loaded on demand by ngx-echarts via provideEchartsCore — see\n// REPORT_ECHARTS_PROVIDERS in reports-echarts.provider.ts.\n//\n// The canvas renderer (not SVG) is deliberate: chart.getDataURL() needs it for\n// the high-res images the PPT/PDF exports will use.\n\nimport { BarChart, LineChart, PieChart } from 'echarts/charts';\nimport {\n DatasetComponent,\n GridComponent,\n LegendComponent,\n MarkAreaComponent,\n MarkLineComponent,\n TooltipComponent,\n} from 'echarts/components';\nimport * as echarts from 'echarts/core';\nimport { LabelLayout } from 'echarts/features';\nimport { CanvasRenderer } from 'echarts/renderers';\n\nimport { REPORT_CHART_COLORS_CSS } from './palette';\nimport { REPORT_AXIS_LABEL_FONT_SIZE, REPORT_CHART_FONT_FAMILY } from './typography';\n\necharts.use([\n BarChart,\n LineChart,\n PieChart,\n GridComponent,\n TooltipComponent,\n LegendComponent,\n DatasetComponent,\n // Reference lines and statistic lines (markLine), reference bands\n // (markArea), and the overlap hiding of 'auto' data labels (LabelLayout).\n MarkLineComponent,\n MarkAreaComponent,\n LabelLayout,\n CanvasRenderer,\n]);\n\n// The house theme — the single place report chart \"standards\" live (palette,\n// typography, tooltip styling). Polished defaults now; refined in the polish\n// phase.\necharts.registerTheme('report', {\n color: REPORT_CHART_COLORS_CSS,\n textStyle: {\n fontFamily: REPORT_CHART_FONT_FAMILY,\n },\n title: {\n textStyle: { color: '#1f2933', fontSize: 14, fontWeight: 600 },\n },\n legend: {\n textStyle: { color: '#52606d', fontSize: 11 },\n },\n tooltip: {\n backgroundColor: 'rgba(31, 41, 51, 0.92)',\n borderWidth: 0,\n textStyle: { color: '#fff', fontSize: 12 },\n },\n categoryAxis: {\n axisLine: { lineStyle: { color: '#cbd2d9' } },\n axisTick: { show: false },\n axisLabel: { color: '#52606d', fontSize: REPORT_AXIS_LABEL_FONT_SIZE },\n splitLine: { show: false },\n },\n valueAxis: {\n axisLine: { show: false },\n axisLabel: { color: '#52606d', fontSize: REPORT_AXIS_LABEL_FONT_SIZE },\n splitLine: { lineStyle: { color: '#e4e7eb' } },\n },\n timeAxis: {\n axisLine: { lineStyle: { color: '#cbd2d9' } },\n axisLabel: { color: '#52606d', fontSize: REPORT_AXIS_LABEL_FONT_SIZE },\n },\n});\n\nexport default echarts;\n"],"names":[],"mappings":";;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAkBA,OAAO,CAAC,GAAG,CAAC;IACV,QAAQ;IACR,SAAS;IACT,QAAQ;IACR,aAAa;IACb,gBAAgB;IAChB,eAAe;IACf,gBAAgB;;;IAGhB,iBAAiB;IACjB,iBAAiB;IACjB,WAAW;IACX,cAAc;AACf,CAAA,CAAC,CAAC;AAEH;AACA;AACA;AACA,OAAO,CAAC,aAAa,CAAC,QAAQ,EAAE;AAC9B,IAAA,KAAK,EAAE,uBAAuB;AAC9B,IAAA,SAAS,EAAE;AACT,QAAA,UAAU,EAAE,wBAAwB;AACrC,KAAA;AACD,IAAA,KAAK,EAAE;AACL,QAAA,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE;AAC/D,KAAA;AACD,IAAA,MAAM,EAAE;QACN,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,EAAE;AAC9C,KAAA;AACD,IAAA,OAAO,EAAE;AACP,QAAA,eAAe,EAAE,wBAAwB;AACzC,QAAA,WAAW,EAAE,CAAC;QACd,SAAS,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE;AAC3C,KAAA;AACD,IAAA,YAAY,EAAE;QACZ,QAAQ,EAAE,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE;AAC7C,QAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;QACzB,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,2BAA2B,EAAE;AACtE,QAAA,SAAS,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;AAC3B,KAAA;AACD,IAAA,SAAS,EAAE;AACT,QAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;QACzB,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,2BAA2B,EAAE;QACtE,SAAS,EAAE,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE;AAC/C,KAAA;AACD,IAAA,QAAQ,EAAE;QACR,QAAQ,EAAE,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE;QAC7C,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,2BAA2B,EAAE;AACvE,KAAA;AACF,CAAA,CAAC"}
1
+ {"version":3,"file":"firestitch-report-echarts-BLr6SmeK.mjs","sources":["../../src/app/reports/theme/echarts.ts"],"sourcesContent":["// Tree-shaken ECharts build for the reports renderer: only the chart types and\n// components the option-builder emits are registered, keeping the lazy chunk\n// lean. Loaded on demand by ngx-echarts via provideEchartsCore — see\n// REPORT_ECHARTS_PROVIDERS in reports-echarts.provider.ts.\n//\n// The canvas renderer (not SVG) is deliberate: chart.getDataURL() needs it for\n// the high-res images the PPT/PDF exports will use.\n\nimport { BarChart, LineChart, PieChart } from 'echarts/charts';\nimport {\n DatasetComponent,\n GridComponent,\n LegendComponent,\n MarkAreaComponent,\n MarkLineComponent,\n TooltipComponent,\n} from 'echarts/components';\nimport * as echarts from 'echarts/core';\nimport { LabelLayout } from 'echarts/features';\nimport { CanvasRenderer } from 'echarts/renderers';\n\nimport { REPORT_CHART_COLORS_CSS } from './palette';\nimport { REPORT_AXIS_LABEL_FONT_SIZE, REPORT_CHART_FONT_FAMILY } from './typography';\n\necharts.use([\n BarChart,\n LineChart,\n PieChart,\n GridComponent,\n TooltipComponent,\n LegendComponent,\n DatasetComponent,\n // Reference lines and statistic lines (markLine), reference bands\n // (markArea), and the overlap hiding of 'auto' data labels (LabelLayout).\n MarkLineComponent,\n MarkAreaComponent,\n LabelLayout,\n CanvasRenderer,\n]);\n\n// The house theme — the single place report chart \"standards\" live (palette,\n// typography, tooltip styling). Polished defaults now; refined in the polish\n// phase.\necharts.registerTheme('report', {\n color: REPORT_CHART_COLORS_CSS,\n textStyle: {\n fontFamily: REPORT_CHART_FONT_FAMILY,\n },\n title: {\n textStyle: { color: '#1f2933', fontSize: 14, fontWeight: 600 },\n },\n legend: {\n textStyle: { color: '#52606d', fontSize: 11 },\n },\n tooltip: {\n backgroundColor: 'rgba(31, 41, 51, 0.92)',\n borderWidth: 0,\n textStyle: { color: '#fff', fontSize: 12 },\n },\n categoryAxis: {\n axisLine: { lineStyle: { color: '#cbd2d9' } },\n axisTick: { show: false },\n axisLabel: { color: '#52606d', fontSize: REPORT_AXIS_LABEL_FONT_SIZE },\n splitLine: { show: false },\n },\n valueAxis: {\n axisLine: { show: false },\n axisLabel: { color: '#52606d', fontSize: REPORT_AXIS_LABEL_FONT_SIZE },\n splitLine: { lineStyle: { color: '#e4e7eb' } },\n },\n timeAxis: {\n axisLine: { lineStyle: { color: '#cbd2d9' } },\n axisLabel: { color: '#52606d', fontSize: REPORT_AXIS_LABEL_FONT_SIZE },\n },\n});\n\nexport default echarts;\n"],"names":[],"mappings":";;;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AAkBA,OAAO,CAAC,GAAG,CAAC;IACV,QAAQ;IACR,SAAS;IACT,QAAQ;IACR,aAAa;IACb,gBAAgB;IAChB,eAAe;IACf,gBAAgB;;;IAGhB,iBAAiB;IACjB,iBAAiB;IACjB,WAAW;IACX,cAAc;AACf,CAAA,CAAC,CAAC;AAEH;AACA;AACA;AACA,OAAO,CAAC,aAAa,CAAC,QAAQ,EAAE;AAC9B,IAAA,KAAK,EAAE,uBAAuB;AAC9B,IAAA,SAAS,EAAE;AACT,QAAA,UAAU,EAAE,wBAAwB;AACrC,KAAA;AACD,IAAA,KAAK,EAAE;AACL,QAAA,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE;AAC/D,KAAA;AACD,IAAA,MAAM,EAAE;QACN,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,EAAE;AAC9C,KAAA;AACD,IAAA,OAAO,EAAE;AACP,QAAA,eAAe,EAAE,wBAAwB;AACzC,QAAA,WAAW,EAAE,CAAC;QACd,SAAS,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE;AAC3C,KAAA;AACD,IAAA,YAAY,EAAE;QACZ,QAAQ,EAAE,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE;AAC7C,QAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;QACzB,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,2BAA2B,EAAE;AACtE,QAAA,SAAS,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;AAC3B,KAAA;AACD,IAAA,SAAS,EAAE;AACT,QAAA,QAAQ,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;QACzB,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,2BAA2B,EAAE;QACtE,SAAS,EAAE,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE;AAC/C,KAAA;AACD,IAAA,QAAQ,EAAE;QACR,QAAQ,EAAE,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE;QAC7C,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,2BAA2B,EAAE;AACvE,KAAA;AACF,CAAA,CAAC"}
@@ -7955,19 +7955,23 @@ class ReportFilterStateService {
7955
7955
  this._stateChanged$.next();
7956
7956
  }
7957
7957
  // Notifications relevant to ONE component: any change to a group one of its
7958
- // filters belongs to (or a full re-init).
7958
+ // filters belongs to (or a full re-init). The component is read on each
7959
+ // event, never captured at subscribe: a tile keeps its subscription across
7960
+ // report loads, so a filter added to it while the report is open (the MCP,
7961
+ // a chat edit, the settings dialog) reaches it on the first change of its
7962
+ // group.
7959
7963
  changesFor(component) {
7960
- const groupIds = new Set((component.filters ?? []).map((filter) => filter.filterGroupId));
7961
7964
  return this._changed$.asObservable()
7962
- .pipe(filter((groupId) => groupId === null || groupIds.has(groupId)));
7965
+ .pipe(filter((groupId) => groupId === null
7966
+ || (component().filters ?? []).some((filter) => filter.filterGroupId === groupId)));
7963
7967
  }
7964
7968
  // Notifications relevant to ANY of a set of components that comes and goes
7965
7969
  // (a page's values): a change to a group one of their filters belongs to.
7966
- // Unlike changesFor, the set is read on each event rather than captured at
7967
- // subscribe, so one subscription follows the set as the report reloads. A
7968
- // full re-init (null) is not passed on: init() runs on a report
7969
- // (re)load, and a caller of this checks on that reload itself whether the
7970
- // filter values its components are read with moved.
7970
+ // Like changesFor, the set is read on each event, so one subscription
7971
+ // follows the set as the report reloads. Unlike it, a full re-init (null)
7972
+ // is not passed on: init() runs on a report (re)load, and a caller of this
7973
+ // checks on that reload itself whether the filter values its components
7974
+ // are read with moved.
7971
7975
  changesForAny(components) {
7972
7976
  return this._changed$.asObservable()
7973
7977
  .pipe(filter((groupId) => groupId !== null
@@ -10061,6 +10065,18 @@ function decimalMark(locale) {
10061
10065
  // them. After that a change to one of the tile's filter groups settles for
10062
10066
  // TILE_REFRESH_DEBOUNCE_MS, so a sweep of changes costs one read; one sent
10063
10067
  // under an older state version is dropped.
10068
+ // - A report load (a Refresh, a chat edit, a settings save, all of which
10069
+ // re-init the filter state) reads the table again at once, even when its
10070
+ // filters did not move: the rows behind it, or its own query, may have.
10071
+ // The load is counted into the params (reportLoads), since fs-grid reads
10072
+ // only when they move. The params are derived from the tile as it is
10073
+ // drawn (params), so the read of a load carries the filters the load
10074
+ // brought, a filter added since the report opened included, and lands in
10075
+ // the same tick as the reloaded tile: a load that also moves the authored
10076
+ // layout costs one read.
10077
+ // - Which filter groups and whether the group-by reach the tile is read
10078
+ // from the tile as it stands at each change, never as it was drawn: a
10079
+ // filter added while the report is open reads on its first pick.
10064
10080
  // - The layout on screen is the state service's (gridLayout): the viewer's
10065
10081
  // own while they changed it from the layout authored now, else the
10066
10082
  // authored one. It outlives a page switch, and the chat reads it. So do
@@ -10084,7 +10100,16 @@ function decimalMark(locale) {
10084
10100
  class ComponentGridComponent {
10085
10101
  reportId = input.required();
10086
10102
  component = input.required();
10087
- params = signal(null);
10103
+ // What fs-grid reads with: the tile's params (_readParams) for the
10104
+ // component drawn now, read again at each settled change (_paramsRead).
10105
+ // A load re-inits the filter state a tick before the tile gets the
10106
+ // component it brought, so deriving them here is what puts a filter added
10107
+ // by that load into its read.
10108
+ params = computed(() => {
10109
+ const component = this.component();
10110
+ this._paramsRead();
10111
+ return untracked(() => this._readParams(component));
10112
+ });
10088
10113
  controls = computed(() => this.component().config?.controls ?? {});
10089
10114
  source = computed(() => new ReportGridSource(this._reportData, this.reportId(), this._componentId(), {
10090
10115
  locale: () => this._filterState.formatContext().locale,
@@ -10109,6 +10134,10 @@ class ComponentGridComponent {
10109
10134
  _isPruned = false;
10110
10135
  // The state version the params on screen were read under.
10111
10136
  _paramsVersion = 0;
10137
+ // The report loads seen since the tile was drawn (ReportGridParams).
10138
+ _reportLoads = 0;
10139
+ // Moves each time a settled change has the params read again.
10140
+ _paramsRead = signal(0);
10112
10141
  ngOnInit() {
10113
10142
  this.expansion.set(this._filterState.gridExpansion(this.component()));
10114
10143
  this._init();
@@ -10122,15 +10151,14 @@ class ComponentGridComponent {
10122
10151
  }
10123
10152
  _init() {
10124
10153
  this._paramsVersion = this._filterState.stateVersion();
10125
- this.params.set(this._readParams());
10126
- this._filterState.changesFor(this.component())
10127
- .pipe(switchMap$1(() => this._settled$()), tap$1(() => this._applyParams()), takeUntilDestroyed(this._destroyRef))
10154
+ // A null group is a full re-init: a report load, or a reset.
10155
+ this._filterState.changesFor(this.component)
10156
+ .pipe(switchMap$1((groupId) => this._settled$(groupId === null)), tap$1((isLoad) => this._applyParams(isLoad)), takeUntilDestroyed(this._destroyRef))
10157
+ .subscribe();
10158
+ // A load can make the table follow the group-by, or stop.
10159
+ this._filterState.frequencyChanges()
10160
+ .pipe(filter$1(() => followsGroupBy(this.component())), tap$1(() => this._applyParams()), takeUntilDestroyed(this._destroyRef))
10128
10161
  .subscribe();
10129
- if (followsGroupBy(this.component())) {
10130
- this._filterState.frequencyChanges()
10131
- .pipe(takeUntilDestroyed(this._destroyRef))
10132
- .subscribe(() => this._applyParams());
10133
- }
10134
10162
  }
10135
10163
  // Once the tile's first read is in, its notes prune the viewer's layout
10136
10164
  // (prunedGridLayout). A tile showing its authored layout has nothing to
@@ -10156,32 +10184,36 @@ class ComponentGridComponent {
10156
10184
  this._filterState.setGridLayout(component, pruned);
10157
10185
  }
10158
10186
  }
10159
- // A change, once it has settled: at once when the state version moved,
10160
- // else after the debounce; dropped when the version moves while it waits,
10161
- // since that move brings its own change.
10162
- _settled$() {
10187
+ // A change, once it has settled, passing on whether it was a report load:
10188
+ // at once for a load (one event, not a sweep) or when the state version
10189
+ // moved, else after the debounce; dropped when the version moves while it
10190
+ // waits, since that move brings its own change.
10191
+ _settled$(isLoad) {
10163
10192
  const version = this._filterState.stateVersion();
10164
10193
  const moved = version !== this._paramsVersion;
10165
- return (moved ? of(0) : timer(TILE_REFRESH_DEBOUNCE_MS))
10166
- .pipe(filter$1(() => this._filterState.stateVersion() === version), map$1(() => undefined));
10194
+ return (isLoad || moved ? of(0) : timer(TILE_REFRESH_DEBOUNCE_MS))
10195
+ .pipe(filter$1(() => this._filterState.stateVersion() === version), map$1(() => isLoad));
10167
10196
  }
10168
- _applyParams() {
10197
+ _applyParams(isLoad = false) {
10198
+ if (isLoad) {
10199
+ this._reportLoads++;
10200
+ }
10169
10201
  this._paramsVersion = this._filterState.stateVersion();
10170
- this.params.set(this._readParams());
10202
+ this._paramsRead.update((count) => count + 1);
10171
10203
  }
10172
10204
  // The tile's report filters, 'any' groups and group-by grain as they stand
10173
- // (tileState()).
10174
- _readParams() {
10175
- const component = this.component();
10205
+ // (tileState()), and the report loads it has seen.
10206
+ _readParams(component) {
10176
10207
  const { matchAnyGroupIds, frequency } = this._filterState.tileState(component);
10177
10208
  return {
10178
10209
  filters: this._filterState.resolveForComponent(component),
10179
10210
  ...(matchAnyGroupIds?.length ? { matchAnyGroupIds } : {}),
10180
10211
  ...(frequency ? { frequency } : {}),
10212
+ ...(this._reportLoads ? { reportLoads: this._reportLoads } : {}),
10181
10213
  };
10182
10214
  }
10183
10215
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ComponentGridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10184
- 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 <!-- A cell that says why (a missing or zero prior) opens it on a\n click; no cell carries a native tooltip. -->\n @if (shown.title) {\n <span\n class=\"explained\"\n fsPopover\n trigger=\"click\"\n [template]=\"explanation\"\n [indication]=\"false\"\n [maxWidth]=\"320\">\n {{ shown.text }}\n </span>\n <ng-template #explanation>{{ shown.title }}</ng-template>\n } @else {\n <span>{{ shown.text }}</span>\n }\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}.explained{cursor:pointer;text-decoration:underline dotted;text-underline-offset:2px}\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: "directive", type: FsPopoverDirective, selector: "[fsPopover]", inputs: ["text", "template", "data", "leaveDelay", "showDelay", "maxWidth", "maxHeight", "wrapperClass", "autoShow", "autoClose", "loadingDiameter", "loading", "indication", "position", "theme", "size", "trigger", "enabled", "panel"], outputs: ["closed"], exportAs: ["fsPopover"] }, { kind: "component", type: ComponentNoteComponent, selector: "app-report-component-note", inputs: ["data", "windows", "windowDescriptions", "windowNotes"] }, { kind: "pipe", type: ReportGridCellPipe, name: "fsReportGridCell" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
10216
+ 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 read from the tile drawn (params), from its first\n render on, so every read carries the tile's report filters. -->\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 <!-- A cell that says why (a missing or zero prior) opens it on a\n click; no cell carries a native tooltip. -->\n @if (shown.title) {\n <span\n class=\"explained\"\n fsPopover\n trigger=\"click\"\n [template]=\"explanation\"\n [indication]=\"false\"\n [maxWidth]=\"320\">\n {{ shown.text }}\n </span>\n <ng-template #explanation>{{ shown.title }}</ng-template>\n } @else {\n <span>{{ shown.text }}</span>\n }\n }\n </ng-template>\n </fs-grid>\n</div>\n", styles: [":host{display:block;height:100%}.grid-pane{height:100%;min-height:0}fs-grid{min-height:0}.explained{cursor:pointer;text-decoration:underline dotted;text-underline-offset:2px}\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: "directive", type: FsPopoverDirective, selector: "[fsPopover]", inputs: ["text", "template", "data", "leaveDelay", "showDelay", "maxWidth", "maxHeight", "wrapperClass", "autoShow", "autoClose", "loadingDiameter", "loading", "indication", "position", "theme", "size", "trigger", "enabled", "panel"], outputs: ["closed"], exportAs: ["fsPopover"] }, { kind: "component", type: ComponentNoteComponent, selector: "app-report-component-note", inputs: ["data", "windows", "windowDescriptions", "windowNotes"] }, { kind: "pipe", type: ReportGridCellPipe, name: "fsReportGridCell" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
10185
10217
  }
10186
10218
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: ComponentGridComponent, decorators: [{
10187
10219
  type: Component,
@@ -10191,7 +10223,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
10191
10223
  FsPopoverDirective,
10192
10224
  ComponentNoteComponent,
10193
10225
  ReportGridCellPipe,
10194
- ], 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 <!-- A cell that says why (a missing or zero prior) opens it on a\n click; no cell carries a native tooltip. -->\n @if (shown.title) {\n <span\n class=\"explained\"\n fsPopover\n trigger=\"click\"\n [template]=\"explanation\"\n [indication]=\"false\"\n [maxWidth]=\"320\">\n {{ shown.text }}\n </span>\n <ng-template #explanation>{{ shown.title }}</ng-template>\n } @else {\n <span>{{ shown.text }}</span>\n }\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}.explained{cursor:pointer;text-decoration:underline dotted;text-underline-offset:2px}\n"] }]
10226
+ ], 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 read from the tile drawn (params), from its first\n render on, so every read carries the tile's report filters. -->\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 <!-- A cell that says why (a missing or zero prior) opens it on a\n click; no cell carries a native tooltip. -->\n @if (shown.title) {\n <span\n class=\"explained\"\n fsPopover\n trigger=\"click\"\n [template]=\"explanation\"\n [indication]=\"false\"\n [maxWidth]=\"320\">\n {{ shown.text }}\n </span>\n <ng-template #explanation>{{ shown.title }}</ng-template>\n } @else {\n <span>{{ shown.text }}</span>\n }\n }\n </ng-template>\n </fs-grid>\n</div>\n", styles: [":host{display:block;height:100%}.grid-pane{height:100%;min-height:0}fs-grid{min-height:0}.explained{cursor:pointer;text-decoration:underline dotted;text-underline-offset:2px}\n"] }]
10195
10227
  }] });
10196
10228
 
10197
10229
  // A KPI's trend over its last periods: a line that stretches to the card's
@@ -10454,9 +10486,10 @@ class ComponentListComponent {
10454
10486
  this._initListConfig(config);
10455
10487
  this._observePinOffsets();
10456
10488
  // A change to any group this list's filters belong to (from the report bar
10457
- // or the component header strip) re-runs the list.
10458
- // So does a change of the text in its search box (in the component header).
10459
- merge(this._filterState.changesFor(component), this._filterState.listSearchChanges(component.id))
10489
+ // or the component header strip) re-runs the list, a filter added since
10490
+ // the list was drawn included. So does a change of the text in its search
10491
+ // box (in the component header).
10492
+ merge(this._filterState.changesFor(this.component), this._filterState.listSearchChanges(component.id))
10460
10493
  .pipe(tap$1(() => this.listComponent()?.reload()), takeUntilDestroyed(this._destroyRef))
10461
10494
  .subscribe();
10462
10495
  }
@@ -11907,19 +11940,21 @@ class ReportComponentComponent {
11907
11940
  tileLoads(this._refresh$, () => this._load(), () => this._filterState.stateVersion())
11908
11941
  .pipe(tap$1((result) => this._loaded(result)), takeUntilDestroyed(this._destroyRef))
11909
11942
  .subscribe();
11910
- this._filterState.changesFor(this.component)
11911
- .pipe(takeUntilDestroyed(this._destroyRef))
11912
- .subscribe(() => this._refresh$.next());
11943
+ // A change to one of the tile's filter groups, read from the tile as
11944
+ // it stands, so a filter a report load added reads on its first pick.
11945
+ this._filterState.changesFor(() => this.component)
11946
+ .pipe(tap$1(() => this._refresh$.next()), takeUntilDestroyed(this._destroyRef))
11947
+ .subscribe();
11913
11948
  // A group-by change re-buckets every tile that follows it; it isn't
11914
- // scoped to a filter group, so it rides its own stream.
11915
- if (followsGroupBy(this.component)) {
11916
- this._filterState.frequencyChanges()
11917
- .pipe(takeUntilDestroyed(this._destroyRef))
11918
- .subscribe(() => {
11919
- this._grainMoved = true;
11920
- this._refresh$.next();
11921
- });
11922
- }
11949
+ // scoped to a filter group, so it rides its own stream. Whether the
11950
+ // tile follows it is read at each change: a load can make it follow
11951
+ // (a chart moved onto a time axis) or stop.
11952
+ this._filterState.frequencyChanges()
11953
+ .pipe(filter$1(() => followsGroupBy(this.component)), tap$1(() => {
11954
+ this._grainMoved = true;
11955
+ this._refresh$.next();
11956
+ }), takeUntilDestroyed(this._destroyRef))
11957
+ .subscribe();
11923
11958
  // A drill moves this chart alone.
11924
11959
  this._filterState.drillChanges(this.component.id)
11925
11960
  .pipe(takeUntilDestroyed(this._destroyRef))
@@ -12336,7 +12371,7 @@ class ReportComponentComponent {
12336
12371
  // A change to one group the header shows with another value (another bar
12337
12372
  // set it) builds it again too.
12338
12373
  _rebuildHeaderOnNewState() {
12339
- this._filterState.changesFor(this.component)
12374
+ this._filterState.changesFor(() => this.component)
12340
12375
  .pipe(filter$1((groupId) => (groupId === null
12341
12376
  ? this._filterState.stateVersion() !== this._headerVersion()
12342
12377
  : this._showsOtherValue(groupId))), tap$1(() => this._buildFilterConfig()), takeUntilDestroyed(this._destroyRef))
@@ -15804,7 +15839,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
15804
15839
  // in the report's format context, as on screen.
15805
15840
  async function renderChartImage(component, data, width, height, context, pixelRatio = 3) {
15806
15841
  // The same tree-shaken echarts build + 'report' theme the live canvas uses.
15807
- const echarts = (await import('./firestitch-report-echarts-C8ZS6OWj.mjs')).default;
15842
+ const echarts = (await import('./firestitch-report-echarts-BLr6SmeK.mjs')).default;
15808
15843
  const host = document.createElement('div');
15809
15844
  host.style.width = `${width}px`;
15810
15845
  host.style.height = `${height}px`;
@@ -19497,7 +19532,7 @@ class FsAiReportComponent {
19497
19532
  ReportAiChatData,
19498
19533
  { provide: FsAiChatData, useExisting: ReportAiChatData },
19499
19534
  // Tree-shaken ECharts core + the 'report' house theme, loaded lazily.
19500
- provideEchartsCore({ echarts: () => import('./firestitch-report-echarts-C8ZS6OWj.mjs').then((module) => module.default) }),
19535
+ provideEchartsCore({ echarts: () => import('./firestitch-report-echarts-BLr6SmeK.mjs').then((module) => module.default) }),
19501
19536
  // The bar's date pickers start their weeks on Monday and list the built-in
19502
19537
  // presets; a preset only sets the days.
19503
19538
  provideReportDatePicker(),
@@ -19521,7 +19556,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
19521
19556
  ReportAiChatData,
19522
19557
  { provide: FsAiChatData, useExisting: ReportAiChatData },
19523
19558
  // Tree-shaken ECharts core + the 'report' house theme, loaded lazily.
19524
- provideEchartsCore({ echarts: () => import('./firestitch-report-echarts-C8ZS6OWj.mjs').then((module) => module.default) }),
19559
+ provideEchartsCore({ echarts: () => import('./firestitch-report-echarts-BLr6SmeK.mjs').then((module) => module.default) }),
19525
19560
  // The bar's date pickers start their weeks on Monday and list the built-in
19526
19561
  // presets; a preset only sets the days.
19527
19562
  provideReportDatePicker(),
@@ -19614,4 +19649,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
19614
19649
  */
19615
19650
 
19616
19651
  export { FsAiReportsComponent as F, GRANULARITY_OPTIONS as G, REPORT_CHART_COLORS_CSS as R, REPORT_CHART_FONT_FAMILY as a, REPORT_AXIS_LABEL_FONT_SIZE as b, FsAiReportComponent as c, ReportData as d, ReportService as e, ReportFilterStateService as f, ReportNodeType as g, ReportState as h, ReportValueFormat as i, ReportValueFormula as j, ReportListColumnFormat as k, ReportNumberUnits as l, ReportChartDataLabels as m, ReportChartColor as n, ReportChartLineStyle as o, ReportChartMarkStat as p, ReportChartDateLabel as q, ReportChartSpanThrough as r, ReportChangePolarity as s };
19617
- //# sourceMappingURL=firestitch-report-firestitch-report-DyKfI3_M.mjs.map
19652
+ //# sourceMappingURL=firestitch-report-firestitch-report-D2dPznfE.mjs.map