@meshmakers/octo-meshboard 3.4.190 → 3.4.210

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.
@@ -1,7 +1,7 @@
1
1
  import { FieldFilterOperatorsDto, CkTypeSelectorService, RuntimeEntitySelectDataSource as RuntimeEntitySelectDataSource$2, RuntimeEntityDialogDataSource as RuntimeEntityDialogDataSource$2, DeleteStrategiesDto, AssociationModOptionsDto, CkModelService, QueryModeDto, GraphDirectionDto, AttributeSelectorService, GraphQL, GetCkTypeAvailableQueryColumnsDtoGQL, SortOrdersDto, HealthService, TENANT_ID_PROVIDER, AssetRepoService, JobManagementService } from '@meshmakers/octo-services';
2
2
  export { RuntimeEntityDialogDataSource, RuntimeEntitySelectDataSource, TENANT_ID_PROVIDER } from '@meshmakers/octo-services';
3
3
  import * as i0 from '@angular/core';
4
- import { Injectable, inject, EventEmitter, forwardRef, Output, Input, ViewChild, ChangeDetectionStrategy, Component, Injector, EnvironmentInjector, ApplicationRef, signal, computed, Directive, makeEnvironmentProviders, provideAppInitializer, InjectionToken, effect } from '@angular/core';
4
+ import { Injectable, inject, EventEmitter, forwardRef, Output, Input, ViewChild, ChangeDetectionStrategy, Component, Injector, EnvironmentInjector, ApplicationRef, signal, computed, Directive, ElementRef, NgZone, makeEnvironmentProviders, provideAppInitializer, InjectionToken, effect } from '@angular/core';
5
5
  import * as i1$1 from '@angular/forms';
6
6
  import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
7
7
  import { firstValueFrom, from, map, Observable, of, catchError, interval, filter } from 'rxjs';
@@ -1761,7 +1761,8 @@ class MeshBoardPersistenceService {
1761
1761
  ckTypeId: es.ckTypeId,
1762
1762
  attributeMappings: es.attributeMappings,
1763
1763
  showInToolbar: es.showInToolbar,
1764
- defaultRtId: es.defaultRtId
1764
+ defaultRtId: es.defaultRtId,
1765
+ childScope: es.childScope
1765
1766
  }));
1766
1767
  }
1767
1768
  const dataJson = JSON.stringify(data);
@@ -2001,6 +2002,14 @@ class MeshBoardStateService {
2001
2002
  ...(ngDevMode ? [{ debugName: "_isModelAvailable" }] : /* istanbul ignore next */ []));
2002
2003
  _variableResolutionErrors = signal([], /* @ts-ignore */
2003
2004
  ...(ngDevMode ? [{ debugName: "_variableResolutionErrors" }] : /* istanbul ignore next */ []));
2005
+ /**
2006
+ * Transient cache of the source rtIds resolved from each entity selector's
2007
+ * current selection (keyed by selector id). Populated by the view component
2008
+ * when a selection resolves (one-hop childScope traversal, or the picked
2009
+ * entity's own rtId). Not persisted — derived fresh from each selection.
2010
+ */
2011
+ _entitySelectorRtIds = signal({}, /* @ts-ignore */
2012
+ ...(ngDevMode ? [{ debugName: "_entitySelectorRtIds" }] : /* istanbul ignore next */ []));
2004
2013
  // Public computed signals
2005
2014
  meshBoardConfig = computed(() => this._meshBoardConfig(), /* @ts-ignore */
2006
2015
  ...(ngDevMode ? [{ debugName: "meshBoardConfig" }] : /* istanbul ignore next */ []));
@@ -2504,6 +2513,42 @@ class MeshBoardStateService {
2504
2513
  }
2505
2514
  return { from: range.from, to: range.to };
2506
2515
  }
2516
+ /**
2517
+ * Stores the source rtIds resolved from an entity selector's current
2518
+ * selection. Called by the view component after a selection resolves (the
2519
+ * one-hop childScope traversal, or the picked entity's own rtId when the
2520
+ * selector has no childScope). Transient — not persisted.
2521
+ */
2522
+ setEntitySelectorRtIds(selectorId, rtIds) {
2523
+ this._entitySelectorRtIds.update(map => ({ ...map, [selectorId]: rtIds }));
2524
+ }
2525
+ /**
2526
+ * Clears the cached source rtIds for an entity selector (e.g. when its
2527
+ * selection is cleared).
2528
+ */
2529
+ clearEntitySelectorRtIds(selectorId) {
2530
+ this._entitySelectorRtIds.update(map => {
2531
+ if (!(selectorId in map)) {
2532
+ return map;
2533
+ }
2534
+ const next = { ...map };
2535
+ delete next[selectorId];
2536
+ return next;
2537
+ });
2538
+ }
2539
+ /**
2540
+ * Resolves the stream-data `rtIds` scope for a persistent-query widget bound
2541
+ * to an entity selector. Returns the source rtIds cached from the selector's
2542
+ * current selection, or `undefined` when the selector id is absent, unknown,
2543
+ * or has no active selection (so the query keeps its persisted scope).
2544
+ */
2545
+ resolveStreamDataRtIds(entitySelectorId) {
2546
+ if (!entitySelectorId) {
2547
+ return undefined;
2548
+ }
2549
+ const rtIds = this._entitySelectorRtIds()[entitySelectorId];
2550
+ return rtIds && rtIds.length > 0 ? rtIds : undefined;
2551
+ }
2507
2552
  /**
2508
2553
  * Updates the time filter configuration.
2509
2554
  */
@@ -2945,7 +2990,8 @@ class QueryExecutorService {
2945
2990
  // Skip the entire `arg` field when the caller has nothing to override —
2946
2991
  // the persisted query then runs with its intrinsic from/to/limit and the
2947
2992
  // GraphQL request stays minimal.
2948
- const hasOverride = args.from != null || args.to != null || args.interval != null || args.limit != null || args.queryMode != null;
2993
+ const hasRtIds = args.rtIds != null && args.rtIds.length > 0;
2994
+ const hasOverride = args.from != null || args.to != null || args.interval != null || args.limit != null || args.queryMode != null || hasRtIds;
2949
2995
  if (!hasOverride) {
2950
2996
  return undefined;
2951
2997
  }
@@ -2957,6 +3003,7 @@ class QueryExecutorService {
2957
3003
  to: args.to ?? undefined,
2958
3004
  interval: args.interval ?? undefined,
2959
3005
  limit: args.limit ?? undefined,
3006
+ rtIds: hasRtIds ? args.rtIds : undefined,
2960
3007
  queryMode: args.queryMode ?? QueryModeDto.DefaultDto
2961
3008
  };
2962
3009
  }
@@ -3633,7 +3680,10 @@ class MeshBoardDataService {
3633
3680
  roleId,
3634
3681
  direction: graphDirection,
3635
3682
  first: first ?? 100,
3636
- attributeNames: attributeNames ?? null
3683
+ // The server's `attributes` resolver rejects a null attributeNames argument
3684
+ // (ARGUMENT_NULL); send an empty list when the caller doesn't need attributes
3685
+ // (e.g. childScope rtId resolution, which only reads rtId).
3686
+ attributeNames: attributeNames ?? []
3637
3687
  }
3638
3688
  }).pipe(map(result => {
3639
3689
  const entity = result.data?.runtime?.runtimeEntities?.items?.[0];
@@ -5082,7 +5132,12 @@ class KpiWidgetComponent {
5082
5132
  }
5083
5133
  buildStreamDataArgs() {
5084
5134
  const ds = this.config.dataSource;
5085
- return this.stateService.resolveStreamDataTimeArgs(ds.ignoreTimeFilter);
5135
+ const timeArgs = this.stateService.resolveStreamDataTimeArgs(ds.ignoreTimeFilter);
5136
+ const rtIds = this.stateService.resolveStreamDataRtIds(ds.entitySelectorId);
5137
+ if (!timeArgs && !rtIds) {
5138
+ return undefined;
5139
+ }
5140
+ return { ...timeArgs, rtIds };
5086
5141
  }
5087
5142
  extractAggregationValue(queryResult) {
5088
5143
  const firstRow = queryResult.rows.find(row => KpiWidgetComponent.SUPPORTED_ROW_TYPES.has(row.__typename ?? ''));
@@ -5273,6 +5328,111 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
5273
5328
  type: Output
5274
5329
  }] } });
5275
5330
 
5331
+ /**
5332
+ * Per-widget asset-scope binding for stream-data persistent-query widgets.
5333
+ *
5334
+ * Stream-data archives are keyed by their source rtIds, so a widget is scoped
5335
+ * to a picked asset by passing those source rtIds as `streamDataArgs.rtIds`
5336
+ * (see `MeshBoardStateService.resolveStreamDataRtIds` and the backend
5337
+ * `StreamDataArguments.rtIds` override). This control lets the designer bind a
5338
+ * single widget to one of the MeshBoard's entity selectors: when the selector's
5339
+ * entity is picked, its resolved source rtIds (the selector's `childScope`
5340
+ * one-hop, or the picked entity itself) scope this widget's query.
5341
+ *
5342
+ * The control renders only when the selected query is a stream-data query
5343
+ * (`family === 'streamData'`); for runtime queries the binding has no effect and
5344
+ * the picker stays hidden (mirrors `SdTimeFilterToggleComponent`).
5345
+ *
5346
+ * Two-way bindable:
5347
+ * ```html
5348
+ * <mm-entity-selector-scope-picker
5349
+ * [family]="selectedQueryFamily"
5350
+ * [selectors]="availableEntitySelectors"
5351
+ * [(entitySelectorId)]="entitySelectorId">
5352
+ * </mm-entity-selector-scope-picker>
5353
+ * ```
5354
+ */
5355
+ class EntitySelectorScopePickerComponent {
5356
+ /** Family of the currently selected query. Picker is shown only for 'streamData'. */
5357
+ family;
5358
+ /** Currently bound entity selector id (`undefined` ⇒ no binding). */
5359
+ entitySelectorId;
5360
+ /** Available entity selectors to choose from. */
5361
+ set selectors(value) {
5362
+ this._selectors = value ?? [];
5363
+ }
5364
+ get selectors() {
5365
+ return this._selectors;
5366
+ }
5367
+ _selectors = [];
5368
+ /** Emits on change (enables `[(entitySelectorId)]` two-way binding). */
5369
+ entitySelectorIdChange = new EventEmitter();
5370
+ get visible() {
5371
+ return this.family === 'streamData';
5372
+ }
5373
+ /** "None" row first, then one row per available selector. */
5374
+ get options() {
5375
+ return [
5376
+ { label: '— None —', value: undefined },
5377
+ ...this._selectors.map(s => ({ label: s.label || s.id, value: s.id }))
5378
+ ];
5379
+ }
5380
+ onSelect(value) {
5381
+ this.entitySelectorId = value ?? undefined;
5382
+ this.entitySelectorIdChange.emit(this.entitySelectorId);
5383
+ }
5384
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: EntitySelectorScopePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5385
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: EntitySelectorScopePickerComponent, isStandalone: true, selector: "mm-entity-selector-scope-picker", inputs: { family: "family", entitySelectorId: "entitySelectorId", selectors: "selectors" }, outputs: { entitySelectorIdChange: "entitySelectorIdChange" }, ngImport: i0, template: `
5386
+ @if (visible) {
5387
+ <div class="form-field entity-selector-scope-picker">
5388
+ <label>Scope to entity selector</label>
5389
+ <kendo-dropdownlist
5390
+ [data]="options"
5391
+ textField="label"
5392
+ valueField="value"
5393
+ [valuePrimitive]="true"
5394
+ [ngModel]="entitySelectorId ?? null"
5395
+ (valueChange)="onSelect($event)">
5396
+ </kendo-dropdownlist>
5397
+ <p class="field-hint">
5398
+ Scopes this widget's stream-data query to the rtIds resolved from the
5399
+ selected asset. Leave unset to use the saved query's own scope.
5400
+ </p>
5401
+ </div>
5402
+ }
5403
+ `, isInline: true, styles: [".entity-selector-scope-picker .field-hint{font-size:.85em;margin-top:4px;opacity:.7}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: DropDownsModule }, { kind: "component", type: i4.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
5404
+ }
5405
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: EntitySelectorScopePickerComponent, decorators: [{
5406
+ type: Component,
5407
+ args: [{ selector: 'mm-entity-selector-scope-picker', standalone: true, imports: [FormsModule, DropDownsModule], changeDetection: ChangeDetectionStrategy.Eager, template: `
5408
+ @if (visible) {
5409
+ <div class="form-field entity-selector-scope-picker">
5410
+ <label>Scope to entity selector</label>
5411
+ <kendo-dropdownlist
5412
+ [data]="options"
5413
+ textField="label"
5414
+ valueField="value"
5415
+ [valuePrimitive]="true"
5416
+ [ngModel]="entitySelectorId ?? null"
5417
+ (valueChange)="onSelect($event)">
5418
+ </kendo-dropdownlist>
5419
+ <p class="field-hint">
5420
+ Scopes this widget's stream-data query to the rtIds resolved from the
5421
+ selected asset. Leave unset to use the saved query's own scope.
5422
+ </p>
5423
+ </div>
5424
+ }
5425
+ `, styles: [".entity-selector-scope-picker .field-hint{font-size:.85em;margin-top:4px;opacity:.7}\n"] }]
5426
+ }], propDecorators: { family: [{
5427
+ type: Input
5428
+ }], entitySelectorId: [{
5429
+ type: Input
5430
+ }], selectors: [{
5431
+ type: Input
5432
+ }], entitySelectorIdChange: [{
5433
+ type: Output
5434
+ }] } });
5435
+
5276
5436
  /**
5277
5437
  * Configuration dialog for KPI widgets.
5278
5438
  * Allows selecting data source, value attribute, and display options.
@@ -5313,6 +5473,7 @@ class KpiConfigDialogComponent {
5313
5473
  initialQueryName;
5314
5474
  initialQueryFamily;
5315
5475
  initialIgnoreTimeFilter;
5476
+ initialEntitySelectorId;
5316
5477
  initialQueryMode;
5317
5478
  initialQueryValueField;
5318
5479
  initialQueryCategoryField;
@@ -5334,6 +5495,7 @@ class KpiConfigDialogComponent {
5334
5495
  selectedPersistentQuery = null;
5335
5496
  /** Stream-data opt-out: when true the MeshBoard time filter is not bound to the query. */
5336
5497
  ignoreTimeFilter = false;
5498
+ entitySelectorId;
5337
5499
  queryColumns = [];
5338
5500
  categoryValues = [];
5339
5501
  queryMode = 'simpleCount';
@@ -5341,6 +5503,10 @@ class KpiConfigDialogComponent {
5341
5503
  get selectedQueryFamily() {
5342
5504
  return queryFamily(this.selectedPersistentQuery?.ckTypeId) ?? this.initialQueryFamily ?? null;
5343
5505
  }
5506
+ /** Entity selectors available on the current MeshBoard (for the scope picker). */
5507
+ get availableEntitySelectors() {
5508
+ return this.meshBoardStateService.getEntitySelectors();
5509
+ }
5344
5510
  isLoadingQueryColumns = false;
5345
5511
  isLoadingCategoryValues = false;
5346
5512
  // Attribute selection
@@ -5437,6 +5603,7 @@ class KpiConfigDialogComponent {
5437
5603
  this.dataSourceType = this.initialDataSourceType || 'runtimeEntity';
5438
5604
  this.queryMode = this.initialQueryMode || 'simpleCount';
5439
5605
  this.ignoreTimeFilter = this.initialIgnoreTimeFilter ?? false;
5606
+ this.entitySelectorId = this.initialEntitySelectorId;
5440
5607
  // Check if this is count mode (for runtime entity)
5441
5608
  this.isCountMode = this.initialValueAttribute === '_count';
5442
5609
  if (this.dataSourceType === 'persistentQuery' && this.initialQueryRtId) {
@@ -5607,6 +5774,7 @@ class KpiConfigDialogComponent {
5607
5774
  queryName: this.selectedPersistentQuery.name,
5608
5775
  queryFamily: family,
5609
5776
  ignoreTimeFilter: this.ignoreTimeFilter,
5777
+ entitySelectorId: this.entitySelectorId,
5610
5778
  queryMode: this.queryMode,
5611
5779
  queryValueField: this.form.queryValueField || undefined,
5612
5780
  queryCategoryField: this.form.queryCategoryField || undefined,
@@ -5771,7 +5939,7 @@ class KpiConfigDialogComponent {
5771
5939
  this.filters = updatedFilters;
5772
5940
  }
5773
5941
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: KpiConfigDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5774
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: KpiConfigDialogComponent, isStandalone: true, selector: "mm-kpi-config-dialog", inputs: { initialCkTypeId: "initialCkTypeId", initialRtId: "initialRtId", initialValueAttribute: "initialValueAttribute", initialLabelAttribute: "initialLabelAttribute", initialPrefix: "initialPrefix", initialSuffix: "initialSuffix", initialTrend: "initialTrend", initialComparisonText: "initialComparisonText", initialDataSourceType: "initialDataSourceType", initialQueryRtId: "initialQueryRtId", initialQueryName: "initialQueryName", initialQueryFamily: "initialQueryFamily", initialIgnoreTimeFilter: "initialIgnoreTimeFilter", initialQueryMode: "initialQueryMode", initialQueryValueField: "initialQueryValueField", initialQueryCategoryField: "initialQueryCategoryField", initialQueryCategoryValue: "initialQueryCategoryValue", initialStaticValue: "initialStaticValue", initialFilters: "initialFilters" }, viewQueries: [{ propertyName: "ckTypeSelectorInput", first: true, predicate: ["ckTypeSelector"], descendants: true }, { propertyName: "entitySelectorInput", first: true, predicate: ["entitySelector"], descendants: true }, { propertyName: "querySelector", first: true, predicate: ["querySelector"], descendants: true }], ngImport: i0, template: `
5942
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: KpiConfigDialogComponent, isStandalone: true, selector: "mm-kpi-config-dialog", inputs: { initialCkTypeId: "initialCkTypeId", initialRtId: "initialRtId", initialValueAttribute: "initialValueAttribute", initialLabelAttribute: "initialLabelAttribute", initialPrefix: "initialPrefix", initialSuffix: "initialSuffix", initialTrend: "initialTrend", initialComparisonText: "initialComparisonText", initialDataSourceType: "initialDataSourceType", initialQueryRtId: "initialQueryRtId", initialQueryName: "initialQueryName", initialQueryFamily: "initialQueryFamily", initialIgnoreTimeFilter: "initialIgnoreTimeFilter", initialEntitySelectorId: "initialEntitySelectorId", initialQueryMode: "initialQueryMode", initialQueryValueField: "initialQueryValueField", initialQueryCategoryField: "initialQueryCategoryField", initialQueryCategoryValue: "initialQueryCategoryValue", initialStaticValue: "initialStaticValue", initialFilters: "initialFilters" }, viewQueries: [{ propertyName: "ckTypeSelectorInput", first: true, predicate: ["ckTypeSelector"], descendants: true }, { propertyName: "entitySelectorInput", first: true, predicate: ["entitySelector"], descendants: true }, { propertyName: "querySelector", first: true, predicate: ["querySelector"], descendants: true }], ngImport: i0, template: `
5775
5943
  <div class="config-container">
5776
5944
 
5777
5945
  <div class="config-form" [class.loading]="isLoadingInitial">
@@ -5952,6 +6120,12 @@ class KpiConfigDialogComponent {
5952
6120
  [(ignoreTimeFilter)]="ignoreTimeFilter">
5953
6121
  </mm-sd-time-filter-toggle>
5954
6122
 
6123
+ <mm-entity-selector-scope-picker
6124
+ [family]="selectedQueryFamily"
6125
+ [selectors]="availableEntitySelectors"
6126
+ [(entitySelectorId)]="entitySelectorId">
6127
+ </mm-entity-selector-scope-picker>
6128
+
5955
6129
  <!-- Query Mode Selection -->
5956
6130
  <div class="form-field">
5957
6131
  <label>Query Mode</label>
@@ -6147,7 +6321,7 @@ class KpiConfigDialogComponent {
6147
6321
  </button>
6148
6322
  </div>
6149
6323
  </div>
6150
- `, isInline: true, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;gap:20px;flex:1;overflow-y:auto;padding:16px;position:relative}.config-form.loading{pointer-events:none}.form-field{display:flex;flex-direction:column;gap:6px}.form-field.disabled{opacity:.6}.form-field.flex-1{flex:1}.form-field label{font-weight:600;font-size:.9rem;color:var(--kendo-color-on-app-surface, #212529)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.form-section{padding:16px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.form-section h4{margin:0 0 16px;font-size:.95rem;color:var(--kendo-color-primary, #0d6efd)}.section-hint{margin:0 0 12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d)}.form-row{display:flex;gap:16px}.mode-toggle{display:flex;gap:8px}.mode-toggle button{flex:1}.attribute-item{display:flex;justify-content:space-between;align-items:center;gap:8px;width:100%}.attribute-path{flex:1}.attribute-type{font-size:.75rem;color:var(--kendo-color-subtle, #6c757d);background:var(--kendo-color-surface-alt, #f8f9fa);padding:2px 6px;border-radius:3px}.required{color:var(--kendo-color-error, #dc3545)}.query-item{display:flex;flex-direction:column;gap:2px}.query-name{font-weight:500}.query-description{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.column-item{display:flex;justify-content:space-between;gap:16px}.column-path{font-weight:500}.column-type{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i3.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "ngmodule", type: DropDownsModule }, { kind: "directive", type: i4.ItemTemplateDirective, selector: "[kendoDropDownListItemTemplate],[kendoComboBoxItemTemplate],[kendoAutoCompleteItemTemplate],[kendoMultiSelectItemTemplate]" }, { kind: "component", type: i4.ComboBoxComponent, selector: "kendo-combobox", inputs: ["icon", "svgIcon", "inputAttributes", "showStickyHeader", "focusableId", "allowCustom", "data", "value", "textField", "valueField", "valuePrimitive", "valueNormalizer", "placeholder", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "loading", "suggest", "clearButton", "disabled", "itemDisabled", "readonly", "tabindex", "tabIndex", "filterable", "virtual", "size", "rounded", "fillMode"], outputs: ["valueChange", "selectionChange", "filterChange", "open", "opened", "close", "closed", "focus", "blur", "inputFocus", "inputBlur", "escape"], exportAs: ["kendoComboBox"] }, { kind: "component", type: i4.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }, { kind: "component", type: CkTypeSelectorInputComponent, selector: "mm-ck-type-selector-input", inputs: ["placeholder", "minSearchLength", "maxResults", "debounceMs", "ckModelIds", "allowAbstract", "dialogTitle", "advancedSearchLabel", "derivedFromRtCkTypeId", "messages", "dialogMessages", "disabled", "required"], outputs: ["ckTypeSelected", "ckTypeCleared"] }, { kind: "component", type: EntitySelectInputComponent, selector: "mm-entity-select-input", inputs: ["dataSource", "placeholder", "minSearchLength", "maxResults", "debounceMs", "prefix", "initialDisplayValue", "dialogDataSource", "dialogTitle", "multiSelect", "advancedSearchLabel", "dialogMessages", "messages", "disabled", "required"], outputs: ["entitySelected", "entityCleared", "entitiesSelected"] }, { kind: "component", type: FieldFilterEditorComponent, selector: "mm-field-filter-editor", inputs: ["availableAttributes", "ckTypeId", "hideNavigationProperties", "attributePaths", "enableVariables", "availableVariables", "filters"], outputs: ["filtersChange"] }, { kind: "component", type: QuerySelectorComponent, selector: "mm-query-selector", inputs: ["placeholder", "hint", "disabled", "acceptFamilies"], outputs: ["querySelected", "queriesLoaded"] }, { kind: "component", type: SdTimeFilterToggleComponent, selector: "mm-sd-time-filter-toggle", inputs: ["family", "ignoreTimeFilter"], outputs: ["ignoreTimeFilterChange"] }, { kind: "component", type: LoadingOverlayComponent, selector: "mm-loading-overlay", inputs: ["loading"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
6324
+ `, isInline: true, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;gap:20px;flex:1;overflow-y:auto;padding:16px;position:relative}.config-form.loading{pointer-events:none}.form-field{display:flex;flex-direction:column;gap:6px}.form-field.disabled{opacity:.6}.form-field.flex-1{flex:1}.form-field label{font-weight:600;font-size:.9rem;color:var(--kendo-color-on-app-surface, #212529)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.form-section{padding:16px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.form-section h4{margin:0 0 16px;font-size:.95rem;color:var(--kendo-color-primary, #0d6efd)}.section-hint{margin:0 0 12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d)}.form-row{display:flex;gap:16px}.mode-toggle{display:flex;gap:8px}.mode-toggle button{flex:1}.attribute-item{display:flex;justify-content:space-between;align-items:center;gap:8px;width:100%}.attribute-path{flex:1}.attribute-type{font-size:.75rem;color:var(--kendo-color-subtle, #6c757d);background:var(--kendo-color-surface-alt, #f8f9fa);padding:2px 6px;border-radius:3px}.required{color:var(--kendo-color-error, #dc3545)}.query-item{display:flex;flex-direction:column;gap:2px}.query-name{font-weight:500}.query-description{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.column-item{display:flex;justify-content:space-between;gap:16px}.column-path{font-weight:500}.column-type{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i3.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "ngmodule", type: DropDownsModule }, { kind: "directive", type: i4.ItemTemplateDirective, selector: "[kendoDropDownListItemTemplate],[kendoComboBoxItemTemplate],[kendoAutoCompleteItemTemplate],[kendoMultiSelectItemTemplate]" }, { kind: "component", type: i4.ComboBoxComponent, selector: "kendo-combobox", inputs: ["icon", "svgIcon", "inputAttributes", "showStickyHeader", "focusableId", "allowCustom", "data", "value", "textField", "valueField", "valuePrimitive", "valueNormalizer", "placeholder", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "loading", "suggest", "clearButton", "disabled", "itemDisabled", "readonly", "tabindex", "tabIndex", "filterable", "virtual", "size", "rounded", "fillMode"], outputs: ["valueChange", "selectionChange", "filterChange", "open", "opened", "close", "closed", "focus", "blur", "inputFocus", "inputBlur", "escape"], exportAs: ["kendoComboBox"] }, { kind: "component", type: i4.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }, { kind: "component", type: CkTypeSelectorInputComponent, selector: "mm-ck-type-selector-input", inputs: ["placeholder", "minSearchLength", "maxResults", "debounceMs", "ckModelIds", "allowAbstract", "dialogTitle", "advancedSearchLabel", "derivedFromRtCkTypeId", "messages", "dialogMessages", "disabled", "required"], outputs: ["ckTypeSelected", "ckTypeCleared"] }, { kind: "component", type: EntitySelectInputComponent, selector: "mm-entity-select-input", inputs: ["dataSource", "placeholder", "minSearchLength", "maxResults", "debounceMs", "prefix", "initialDisplayValue", "dialogDataSource", "dialogTitle", "multiSelect", "advancedSearchLabel", "dialogMessages", "messages", "disabled", "required"], outputs: ["entitySelected", "entityCleared", "entitiesSelected"] }, { kind: "component", type: FieldFilterEditorComponent, selector: "mm-field-filter-editor", inputs: ["availableAttributes", "ckTypeId", "hideNavigationProperties", "attributePaths", "enableVariables", "availableVariables", "filters"], outputs: ["filtersChange"] }, { kind: "component", type: QuerySelectorComponent, selector: "mm-query-selector", inputs: ["placeholder", "hint", "disabled", "acceptFamilies"], outputs: ["querySelected", "queriesLoaded"] }, { kind: "component", type: SdTimeFilterToggleComponent, selector: "mm-sd-time-filter-toggle", inputs: ["family", "ignoreTimeFilter"], outputs: ["ignoreTimeFilterChange"] }, { kind: "component", type: EntitySelectorScopePickerComponent, selector: "mm-entity-selector-scope-picker", inputs: ["family", "entitySelectorId", "selectors"], outputs: ["entitySelectorIdChange"] }, { kind: "component", type: LoadingOverlayComponent, selector: "mm-loading-overlay", inputs: ["loading"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
6151
6325
  }
6152
6326
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: KpiConfigDialogComponent, decorators: [{
6153
6327
  type: Component,
@@ -6162,6 +6336,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
6162
6336
  FieldFilterEditorComponent,
6163
6337
  QuerySelectorComponent,
6164
6338
  SdTimeFilterToggleComponent,
6339
+ EntitySelectorScopePickerComponent,
6165
6340
  LoadingOverlayComponent
6166
6341
  ], template: `
6167
6342
  <div class="config-container">
@@ -6344,6 +6519,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
6344
6519
  [(ignoreTimeFilter)]="ignoreTimeFilter">
6345
6520
  </mm-sd-time-filter-toggle>
6346
6521
 
6522
+ <mm-entity-selector-scope-picker
6523
+ [family]="selectedQueryFamily"
6524
+ [selectors]="availableEntitySelectors"
6525
+ [(entitySelectorId)]="entitySelectorId">
6526
+ </mm-entity-selector-scope-picker>
6527
+
6347
6528
  <!-- Query Mode Selection -->
6348
6529
  <div class="form-field">
6349
6530
  <label>Query Mode</label>
@@ -6575,6 +6756,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
6575
6756
  type: Input
6576
6757
  }], initialIgnoreTimeFilter: [{
6577
6758
  type: Input
6759
+ }], initialEntitySelectorId: [{
6760
+ type: Input
6578
6761
  }], initialQueryMode: [{
6579
6762
  type: Input
6580
6763
  }], initialQueryValueField: [{
@@ -8279,8 +8462,13 @@ class TableWidgetDataSourceDirective extends OctoGraphQlDataSource {
8279
8462
  */
8280
8463
  buildStreamDataArgs() {
8281
8464
  const ds = this._config?.dataSource;
8282
- const ignoreTimeFilter = ds?.type === 'persistentQuery' ? ds.ignoreTimeFilter : undefined;
8283
- return this.stateService.resolveStreamDataTimeArgs(ignoreTimeFilter);
8465
+ const isPersistentQuery = ds?.type === 'persistentQuery';
8466
+ const timeArgs = this.stateService.resolveStreamDataTimeArgs(isPersistentQuery ? ds.ignoreTimeFilter : undefined);
8467
+ const rtIds = this.stateService.resolveStreamDataRtIds(isPersistentQuery ? ds.entitySelectorId : undefined);
8468
+ if (!timeArgs && !rtIds) {
8469
+ return undefined;
8470
+ }
8471
+ return { ...timeArgs, rtIds };
8284
8472
  }
8285
8473
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TableWidgetDataSourceDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
8286
8474
  static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: TableWidgetDataSourceDirective, isStandalone: true, selector: "[mmTableWidgetDataSource]", inputs: { config: "config" }, outputs: { queryColumnsLoaded: "queryColumnsLoaded" }, providers: [
@@ -8525,6 +8713,7 @@ class TableConfigDialogComponent {
8525
8713
  initialQueryName;
8526
8714
  initialQueryFamily;
8527
8715
  initialIgnoreTimeFilter;
8716
+ initialEntitySelectorId;
8528
8717
  columnsIcon = columnsIcon;
8529
8718
  sortIcon = sortAscIcon;
8530
8719
  filterIcon = filterIcon;
@@ -8545,8 +8734,13 @@ class TableConfigDialogComponent {
8545
8734
  // Query configuration
8546
8735
  selectedPersistentQuery = null;
8547
8736
  ignoreTimeFilter = false;
8737
+ entitySelectorId;
8548
8738
  // Variables for filter editor
8549
8739
  filterVariables = [];
8740
+ /** Entity selectors available on the current MeshBoard (for the scope picker). */
8741
+ get availableEntitySelectors() {
8742
+ return this.stateService.getEntitySelectors();
8743
+ }
8550
8744
  form = {
8551
8745
  pageSize: 10,
8552
8746
  sortable: true
@@ -8576,6 +8770,7 @@ class TableConfigDialogComponent {
8576
8770
  this.form.pageSize = this.initialPageSize ?? 10;
8577
8771
  this.form.sortable = this.initialSortable ?? true;
8578
8772
  this.ignoreTimeFilter = this.initialIgnoreTimeFilter ?? false;
8773
+ this.entitySelectorId = this.initialEntitySelectorId;
8579
8774
  // Convert initial sorting from string-based format to SortDto
8580
8775
  if (this.initialSorting) {
8581
8776
  this.sorting = this.initialSorting.map(s => ({
@@ -8764,6 +8959,7 @@ class TableConfigDialogComponent {
8764
8959
  queryName: this.selectedPersistentQuery.name,
8765
8960
  queryFamily: family,
8766
8961
  ignoreTimeFilter: this.ignoreTimeFilter,
8962
+ entitySelectorId: this.entitySelectorId,
8767
8963
  pageSize: this.form.pageSize,
8768
8964
  sortable: this.form.sortable
8769
8965
  });
@@ -8780,7 +8976,7 @@ class TableConfigDialogComponent {
8780
8976
  this.windowRef.close();
8781
8977
  }
8782
8978
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TableConfigDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
8783
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: TableConfigDialogComponent, isStandalone: true, selector: "mm-table-config-dialog", inputs: { initialDataSourceType: "initialDataSourceType", initialCkTypeId: "initialCkTypeId", initialColumns: "initialColumns", initialSorting: "initialSorting", initialFilters: "initialFilters", initialPageSize: "initialPageSize", initialSortable: "initialSortable", initialQueryRtId: "initialQueryRtId", initialQueryName: "initialQueryName", initialQueryFamily: "initialQueryFamily", initialIgnoreTimeFilter: "initialIgnoreTimeFilter" }, providers: [
8979
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: TableConfigDialogComponent, isStandalone: true, selector: "mm-table-config-dialog", inputs: { initialDataSourceType: "initialDataSourceType", initialCkTypeId: "initialCkTypeId", initialColumns: "initialColumns", initialSorting: "initialSorting", initialFilters: "initialFilters", initialPageSize: "initialPageSize", initialSortable: "initialSortable", initialQueryRtId: "initialQueryRtId", initialQueryName: "initialQueryName", initialQueryFamily: "initialQueryFamily", initialIgnoreTimeFilter: "initialIgnoreTimeFilter", initialEntitySelectorId: "initialEntitySelectorId" }, providers: [
8784
8980
  AttributeSelectorDialogService,
8785
8981
  AttributeSortSelectorDialogService
8786
8982
  ], viewQueries: [{ propertyName: "ckTypeSelectorInput", first: true, predicate: ["ckTypeSelector"], descendants: true }, { propertyName: "filterEditor", first: true, predicate: ["filterEditor"], descendants: true }, { propertyName: "querySelector", first: true, predicate: ["querySelector"], descendants: true }], ngImport: i0, template: `
@@ -8905,6 +9101,12 @@ class TableConfigDialogComponent {
8905
9101
  [(ignoreTimeFilter)]="ignoreTimeFilter">
8906
9102
  </mm-sd-time-filter-toggle>
8907
9103
 
9104
+ <mm-entity-selector-scope-picker
9105
+ [family]="selectedQueryFamily"
9106
+ [selectors]="availableEntitySelectors"
9107
+ [(entitySelectorId)]="entitySelectorId">
9108
+ </mm-entity-selector-scope-picker>
9109
+
8908
9110
  @if (selectedPersistentQuery) {
8909
9111
  <div class="config-card">
8910
9112
  <div class="card-header">
@@ -8988,7 +9190,7 @@ class TableConfigDialogComponent {
8988
9190
  </button>
8989
9191
  </div>
8990
9192
  </div>
8991
- `, isInline: true, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;gap:16px;flex:1;overflow-y:auto;padding:16px;position:relative}.config-form.loading{pointer-events:none}.form-field{display:flex;flex-direction:column;gap:6px}.form-field label{font-weight:600;font-size:.9rem;color:var(--kendo-color-on-app-surface, #212529)}.required{color:var(--kendo-color-error, #dc3545)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.config-card{padding:12px 16px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.card-header{display:flex;align-items:center;gap:8px;margin-bottom:8px}.card-header kendo-svgicon{color:var(--kendo-color-primary, #0d6efd)}.card-title{font-weight:600;font-size:.95rem;color:var(--kendo-color-primary, #0d6efd)}.card-count{color:var(--kendo-color-subtle, #6c757d);font-size:.85rem}.card-content{display:flex;align-items:center;justify-content:space-between;gap:16px}.config-summary{margin:0;font-size:.85rem;color:var(--kendo-color-on-app-surface, #212529);flex:1}.filters-card .card-content{flex-direction:column;align-items:stretch}.filter-content{width:100%}.options-card .options-content{display:flex;gap:24px;align-items:flex-end}.checkbox-field{flex-direction:row;align-items:center}.checkbox-field label{display:flex;align-items:center;gap:8px;cursor:pointer;color:var(--kendo-color-on-app-surface, #212529)}.data-source-type .radio-group{display:flex;gap:24px}.radio-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400;color:var(--kendo-color-on-app-surface, #212529)}.radio-label span{color:var(--kendo-color-on-app-surface, #212529)}.query-info{flex-direction:column;align-items:stretch;gap:8px}.info-row{display:flex;gap:8px}.info-label{font-weight:600;min-width:100px;color:var(--kendo-color-subtle, #6c757d)}.info-value{flex:1;color:var(--kendo-color-on-app-surface, #212529)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox]:not([ngNoCva])[formControlName],input[type=checkbox]:not([ngNoCva])[formControl],input[type=checkbox]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1$1.RadioControlValueAccessor, selector: "input[type=radio]:not([ngNoCva])[formControlName],input[type=radio]:not([ngNoCva])[formControl],input[type=radio]:not([ngNoCva])[ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i3.NumericTextBoxComponent, selector: "kendo-numerictextbox", inputs: ["focusableId", "disabled", "readonly", "title", "autoCorrect", "format", "max", "min", "decimals", "placeholder", "step", "spinners", "rangeValidation", "tabindex", "tabIndex", "changeValueOnScroll", "selectOnFocus", "value", "maxlength", "size", "rounded", "fillMode", "inputAttributes"], outputs: ["valueChange", "focus", "blur", "inputFocus", "inputBlur"], exportAs: ["kendoNumericTextBox"] }, { kind: "directive", type: i3.CheckBoxDirective, selector: "input[kendoCheckBox]", inputs: ["size", "rounded"] }, { kind: "directive", type: i3.RadioButtonDirective, selector: "input[kendoRadioButton]", inputs: ["size"] }, { kind: "ngmodule", type: NumericTextBoxModule }, { kind: "ngmodule", type: DropDownsModule }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i1$4.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon", "variant"], exportAs: ["kendoSVGIcon"] }, { kind: "component", type: CkTypeSelectorInputComponent, selector: "mm-ck-type-selector-input", inputs: ["placeholder", "minSearchLength", "maxResults", "debounceMs", "ckModelIds", "allowAbstract", "dialogTitle", "advancedSearchLabel", "derivedFromRtCkTypeId", "messages", "dialogMessages", "disabled", "required"], outputs: ["ckTypeSelected", "ckTypeCleared"] }, { kind: "component", type: FieldFilterEditorComponent, selector: "mm-field-filter-editor", inputs: ["availableAttributes", "ckTypeId", "hideNavigationProperties", "attributePaths", "enableVariables", "availableVariables", "filters"], outputs: ["filtersChange"] }, { kind: "component", type: QuerySelectorComponent, selector: "mm-query-selector", inputs: ["placeholder", "hint", "disabled", "acceptFamilies"], outputs: ["querySelected", "queriesLoaded"] }, { kind: "component", type: SdTimeFilterToggleComponent, selector: "mm-sd-time-filter-toggle", inputs: ["family", "ignoreTimeFilter"], outputs: ["ignoreTimeFilterChange"] }, { kind: "component", type: LoadingOverlayComponent, selector: "mm-loading-overlay", inputs: ["loading"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
9193
+ `, isInline: true, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;gap:16px;flex:1;overflow-y:auto;padding:16px;position:relative}.config-form.loading{pointer-events:none}.form-field{display:flex;flex-direction:column;gap:6px}.form-field label{font-weight:600;font-size:.9rem;color:var(--kendo-color-on-app-surface, #212529)}.required{color:var(--kendo-color-error, #dc3545)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.config-card{padding:12px 16px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.card-header{display:flex;align-items:center;gap:8px;margin-bottom:8px}.card-header kendo-svgicon{color:var(--kendo-color-primary, #0d6efd)}.card-title{font-weight:600;font-size:.95rem;color:var(--kendo-color-primary, #0d6efd)}.card-count{color:var(--kendo-color-subtle, #6c757d);font-size:.85rem}.card-content{display:flex;align-items:center;justify-content:space-between;gap:16px}.config-summary{margin:0;font-size:.85rem;color:var(--kendo-color-on-app-surface, #212529);flex:1}.filters-card .card-content{flex-direction:column;align-items:stretch}.filter-content{width:100%}.options-card .options-content{display:flex;gap:24px;align-items:flex-end}.checkbox-field{flex-direction:row;align-items:center}.checkbox-field label{display:flex;align-items:center;gap:8px;cursor:pointer;color:var(--kendo-color-on-app-surface, #212529)}.data-source-type .radio-group{display:flex;gap:24px}.radio-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400;color:var(--kendo-color-on-app-surface, #212529)}.radio-label span{color:var(--kendo-color-on-app-surface, #212529)}.query-info{flex-direction:column;align-items:stretch;gap:8px}.info-row{display:flex;gap:8px}.info-label{font-weight:600;min-width:100px;color:var(--kendo-color-subtle, #6c757d)}.info-value{flex:1;color:var(--kendo-color-on-app-surface, #212529)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox]:not([ngNoCva])[formControlName],input[type=checkbox]:not([ngNoCva])[formControl],input[type=checkbox]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1$1.RadioControlValueAccessor, selector: "input[type=radio]:not([ngNoCva])[formControlName],input[type=radio]:not([ngNoCva])[formControl],input[type=radio]:not([ngNoCva])[ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i3.NumericTextBoxComponent, selector: "kendo-numerictextbox", inputs: ["focusableId", "disabled", "readonly", "title", "autoCorrect", "format", "max", "min", "decimals", "placeholder", "step", "spinners", "rangeValidation", "tabindex", "tabIndex", "changeValueOnScroll", "selectOnFocus", "value", "maxlength", "size", "rounded", "fillMode", "inputAttributes"], outputs: ["valueChange", "focus", "blur", "inputFocus", "inputBlur"], exportAs: ["kendoNumericTextBox"] }, { kind: "directive", type: i3.CheckBoxDirective, selector: "input[kendoCheckBox]", inputs: ["size", "rounded"] }, { kind: "directive", type: i3.RadioButtonDirective, selector: "input[kendoRadioButton]", inputs: ["size"] }, { kind: "ngmodule", type: NumericTextBoxModule }, { kind: "ngmodule", type: DropDownsModule }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: i1$4.SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon", "variant"], exportAs: ["kendoSVGIcon"] }, { kind: "component", type: CkTypeSelectorInputComponent, selector: "mm-ck-type-selector-input", inputs: ["placeholder", "minSearchLength", "maxResults", "debounceMs", "ckModelIds", "allowAbstract", "dialogTitle", "advancedSearchLabel", "derivedFromRtCkTypeId", "messages", "dialogMessages", "disabled", "required"], outputs: ["ckTypeSelected", "ckTypeCleared"] }, { kind: "component", type: FieldFilterEditorComponent, selector: "mm-field-filter-editor", inputs: ["availableAttributes", "ckTypeId", "hideNavigationProperties", "attributePaths", "enableVariables", "availableVariables", "filters"], outputs: ["filtersChange"] }, { kind: "component", type: QuerySelectorComponent, selector: "mm-query-selector", inputs: ["placeholder", "hint", "disabled", "acceptFamilies"], outputs: ["querySelected", "queriesLoaded"] }, { kind: "component", type: SdTimeFilterToggleComponent, selector: "mm-sd-time-filter-toggle", inputs: ["family", "ignoreTimeFilter"], outputs: ["ignoreTimeFilterChange"] }, { kind: "component", type: EntitySelectorScopePickerComponent, selector: "mm-entity-selector-scope-picker", inputs: ["family", "entitySelectorId", "selectors"], outputs: ["entitySelectorIdChange"] }, { kind: "component", type: LoadingOverlayComponent, selector: "mm-loading-overlay", inputs: ["loading"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
8992
9194
  }
8993
9195
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: TableConfigDialogComponent, decorators: [{
8994
9196
  type: Component,
@@ -9004,6 +9206,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
9004
9206
  FieldFilterEditorComponent,
9005
9207
  QuerySelectorComponent,
9006
9208
  SdTimeFilterToggleComponent,
9209
+ EntitySelectorScopePickerComponent,
9007
9210
  LoadingOverlayComponent
9008
9211
  ], providers: [
9009
9212
  AttributeSelectorDialogService,
@@ -9130,6 +9333,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
9130
9333
  [(ignoreTimeFilter)]="ignoreTimeFilter">
9131
9334
  </mm-sd-time-filter-toggle>
9132
9335
 
9336
+ <mm-entity-selector-scope-picker
9337
+ [family]="selectedQueryFamily"
9338
+ [selectors]="availableEntitySelectors"
9339
+ [(entitySelectorId)]="entitySelectorId">
9340
+ </mm-entity-selector-scope-picker>
9341
+
9133
9342
  @if (selectedPersistentQuery) {
9134
9343
  <div class="config-card">
9135
9344
  <div class="card-header">
@@ -9245,6 +9454,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
9245
9454
  type: Input
9246
9455
  }], initialIgnoreTimeFilter: [{
9247
9456
  type: Input
9457
+ }], initialEntitySelectorId: [{
9458
+ type: Input
9248
9459
  }] } });
9249
9460
 
9250
9461
  class GaugeWidgetComponent {
@@ -9427,7 +9638,12 @@ class GaugeWidgetComponent {
9427
9638
  }
9428
9639
  buildStreamDataArgs() {
9429
9640
  const ds = this.config.dataSource;
9430
- return this.stateService.resolveStreamDataTimeArgs(ds.ignoreTimeFilter);
9641
+ const timeArgs = this.stateService.resolveStreamDataTimeArgs(ds.ignoreTimeFilter);
9642
+ const rtIds = this.stateService.resolveStreamDataRtIds(ds.entitySelectorId);
9643
+ if (!timeArgs && !rtIds) {
9644
+ return undefined;
9645
+ }
9646
+ return { ...timeArgs, rtIds };
9431
9647
  }
9432
9648
  extractAggregationValue(queryResult) {
9433
9649
  const firstRow = queryResult.rows.find(row => GaugeWidgetComponent.SUPPORTED_ROW_TYPES.has(row.__typename ?? ''));
@@ -9877,6 +10093,7 @@ class GaugeConfigDialogComponent {
9877
10093
  initialQueryName;
9878
10094
  initialQueryFamily;
9879
10095
  initialIgnoreTimeFilter;
10096
+ initialEntitySelectorId;
9880
10097
  initialQueryMode;
9881
10098
  initialQueryValueField;
9882
10099
  initialQueryCategoryField;
@@ -9895,6 +10112,7 @@ class GaugeConfigDialogComponent {
9895
10112
  selectedPersistentQuery = null;
9896
10113
  /** Stream-data opt-out: when true the MeshBoard time filter is not bound to the query. */
9897
10114
  ignoreTimeFilter = false;
10115
+ entitySelectorId;
9898
10116
  queryColumns = [];
9899
10117
  categoryValues = [];
9900
10118
  queryMode = 'simpleCount';
@@ -9902,6 +10120,10 @@ class GaugeConfigDialogComponent {
9902
10120
  get selectedQueryFamily() {
9903
10121
  return queryFamily(this.selectedPersistentQuery?.ckTypeId) ?? this.initialQueryFamily ?? null;
9904
10122
  }
10123
+ /** Entity selectors available on the current MeshBoard (for the scope picker). */
10124
+ get availableEntitySelectors() {
10125
+ return this.meshBoardStateService.getEntitySelectors();
10126
+ }
9905
10127
  isLoadingQueryColumns = false;
9906
10128
  isLoadingCategoryValues = false;
9907
10129
  // Attribute selection
@@ -9996,6 +10218,7 @@ class GaugeConfigDialogComponent {
9996
10218
  this.dataSourceType = this.initialDataSourceType || 'runtimeEntity';
9997
10219
  this.queryMode = this.initialQueryMode || 'simpleCount';
9998
10220
  this.ignoreTimeFilter = this.initialIgnoreTimeFilter ?? false;
10221
+ this.entitySelectorId = this.initialEntitySelectorId;
9999
10222
  if (this.dataSourceType === 'persistentQuery' && this.initialQueryRtId) {
10000
10223
  this.isLoadingInitial = true;
10001
10224
  setTimeout(async () => {
@@ -10145,6 +10368,7 @@ class GaugeConfigDialogComponent {
10145
10368
  queryName: this.selectedPersistentQuery.name,
10146
10369
  queryFamily: family,
10147
10370
  ignoreTimeFilter: this.ignoreTimeFilter,
10371
+ entitySelectorId: this.entitySelectorId,
10148
10372
  queryMode: this.queryMode,
10149
10373
  queryValueField: this.form.queryValueField || undefined,
10150
10374
  queryCategoryField: this.form.queryCategoryField || undefined,
@@ -10308,7 +10532,7 @@ class GaugeConfigDialogComponent {
10308
10532
  this.filters = updatedFilters;
10309
10533
  }
10310
10534
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: GaugeConfigDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10311
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: GaugeConfigDialogComponent, isStandalone: true, selector: "mm-gauge-config-dialog", inputs: { initialCkTypeId: "initialCkTypeId", initialRtId: "initialRtId", initialGaugeType: "initialGaugeType", initialValueAttribute: "initialValueAttribute", initialLabelAttribute: "initialLabelAttribute", initialMin: "initialMin", initialMax: "initialMax", initialRanges: "initialRanges", initialShowLabel: "initialShowLabel", initialPrefix: "initialPrefix", initialSuffix: "initialSuffix", initialReverse: "initialReverse", initialDataSourceType: "initialDataSourceType", initialQueryRtId: "initialQueryRtId", initialQueryName: "initialQueryName", initialQueryFamily: "initialQueryFamily", initialIgnoreTimeFilter: "initialIgnoreTimeFilter", initialQueryMode: "initialQueryMode", initialQueryValueField: "initialQueryValueField", initialQueryCategoryField: "initialQueryCategoryField", initialQueryCategoryValue: "initialQueryCategoryValue", initialFilters: "initialFilters" }, viewQueries: [{ propertyName: "ckTypeSelectorInput", first: true, predicate: ["ckTypeSelector"], descendants: true }, { propertyName: "entitySelectorInput", first: true, predicate: ["entitySelector"], descendants: true }, { propertyName: "querySelector", first: true, predicate: ["querySelector"], descendants: true }], ngImport: i0, template: `
10535
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: GaugeConfigDialogComponent, isStandalone: true, selector: "mm-gauge-config-dialog", inputs: { initialCkTypeId: "initialCkTypeId", initialRtId: "initialRtId", initialGaugeType: "initialGaugeType", initialValueAttribute: "initialValueAttribute", initialLabelAttribute: "initialLabelAttribute", initialMin: "initialMin", initialMax: "initialMax", initialRanges: "initialRanges", initialShowLabel: "initialShowLabel", initialPrefix: "initialPrefix", initialSuffix: "initialSuffix", initialReverse: "initialReverse", initialDataSourceType: "initialDataSourceType", initialQueryRtId: "initialQueryRtId", initialQueryName: "initialQueryName", initialQueryFamily: "initialQueryFamily", initialIgnoreTimeFilter: "initialIgnoreTimeFilter", initialEntitySelectorId: "initialEntitySelectorId", initialQueryMode: "initialQueryMode", initialQueryValueField: "initialQueryValueField", initialQueryCategoryField: "initialQueryCategoryField", initialQueryCategoryValue: "initialQueryCategoryValue", initialFilters: "initialFilters" }, viewQueries: [{ propertyName: "ckTypeSelectorInput", first: true, predicate: ["ckTypeSelector"], descendants: true }, { propertyName: "entitySelectorInput", first: true, predicate: ["entitySelector"], descendants: true }, { propertyName: "querySelector", first: true, predicate: ["querySelector"], descendants: true }], ngImport: i0, template: `
10312
10536
  <div class="config-container">
10313
10537
 
10314
10538
  <div class="config-form" [class.loading]="isLoadingInitial">
@@ -10428,6 +10652,12 @@ class GaugeConfigDialogComponent {
10428
10652
  [(ignoreTimeFilter)]="ignoreTimeFilter">
10429
10653
  </mm-sd-time-filter-toggle>
10430
10654
 
10655
+ <mm-entity-selector-scope-picker
10656
+ [family]="selectedQueryFamily"
10657
+ [selectors]="availableEntitySelectors"
10658
+ [(entitySelectorId)]="entitySelectorId">
10659
+ </mm-entity-selector-scope-picker>
10660
+
10431
10661
  <!-- Query Mode Selection -->
10432
10662
  <div class="form-field">
10433
10663
  <label>Query Mode</label>
@@ -10712,7 +10942,7 @@ class GaugeConfigDialogComponent {
10712
10942
  </button>
10713
10943
  </div>
10714
10944
  </div>
10715
- `, isInline: true, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;gap:20px;padding:16px;position:relative;flex:1;overflow-y:auto}.config-form.loading{pointer-events:none}.form-field{display:flex;flex-direction:column;gap:6px}.form-field.disabled{opacity:.6}.form-field.flex-1{flex:1}.form-field label{font-weight:600;font-size:.9rem;color:var(--kendo-color-on-app-surface, #212529)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.form-section{padding:16px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.form-section h4{margin:0 0 16px;font-size:.95rem;color:var(--kendo-color-primary, #0d6efd)}.section-hint{margin:0 0 12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d)}.form-row{display:flex;gap:16px}.checkbox-label{margin-left:8px;font-weight:400}.gauge-type-item{display:flex;flex-direction:column;gap:2px}.gauge-type-label{font-weight:500}.gauge-type-desc{font-size:.75rem;color:var(--kendo-color-subtle, #6c757d)}.attribute-item{display:flex;justify-content:space-between;align-items:center;gap:8px;width:100%}.attribute-path{flex:1}.attribute-type{font-size:.75rem;color:var(--kendo-color-subtle, #6c757d);background:var(--kendo-color-surface-alt, #f8f9fa);padding:2px 6px;border-radius:3px}.range-row{display:flex;align-items:center;gap:8px;margin-bottom:8px}.range-input{width:80px}.range-separator{color:var(--kendo-color-subtle, #6c757d)}.color-picker{width:40px;height:32px;padding:2px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;cursor:pointer}.mode-toggle{display:flex;gap:8px}.mode-toggle button{flex:1}.required{color:var(--kendo-color-error, #dc3545)}.query-item{display:flex;flex-direction:column;gap:2px}.query-name{font-weight:500}.query-description{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.column-item{display:flex;justify-content:space-between;gap:16px}.column-path{font-weight:500}.column-type{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox]:not([ngNoCva])[formControlName],input[type=checkbox]:not([ngNoCva])[formControl],input[type=checkbox]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i3.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "component", type: i3.NumericTextBoxComponent, selector: "kendo-numerictextbox", inputs: ["focusableId", "disabled", "readonly", "title", "autoCorrect", "format", "max", "min", "decimals", "placeholder", "step", "spinners", "rangeValidation", "tabindex", "tabIndex", "changeValueOnScroll", "selectOnFocus", "value", "maxlength", "size", "rounded", "fillMode", "inputAttributes"], outputs: ["valueChange", "focus", "blur", "inputFocus", "inputBlur"], exportAs: ["kendoNumericTextBox"] }, { kind: "directive", type: i3.CheckBoxDirective, selector: "input[kendoCheckBox]", inputs: ["size", "rounded"] }, { kind: "ngmodule", type: DropDownsModule }, { kind: "directive", type: i4.ItemTemplateDirective, selector: "[kendoDropDownListItemTemplate],[kendoComboBoxItemTemplate],[kendoAutoCompleteItemTemplate],[kendoMultiSelectItemTemplate]" }, { kind: "component", type: i4.ComboBoxComponent, selector: "kendo-combobox", inputs: ["icon", "svgIcon", "inputAttributes", "showStickyHeader", "focusableId", "allowCustom", "data", "value", "textField", "valueField", "valuePrimitive", "valueNormalizer", "placeholder", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "loading", "suggest", "clearButton", "disabled", "itemDisabled", "readonly", "tabindex", "tabIndex", "filterable", "virtual", "size", "rounded", "fillMode"], outputs: ["valueChange", "selectionChange", "filterChange", "open", "opened", "close", "closed", "focus", "blur", "inputFocus", "inputBlur", "escape"], exportAs: ["kendoComboBox"] }, { kind: "component", type: i4.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }, { kind: "component", type: CkTypeSelectorInputComponent, selector: "mm-ck-type-selector-input", inputs: ["placeholder", "minSearchLength", "maxResults", "debounceMs", "ckModelIds", "allowAbstract", "dialogTitle", "advancedSearchLabel", "derivedFromRtCkTypeId", "messages", "dialogMessages", "disabled", "required"], outputs: ["ckTypeSelected", "ckTypeCleared"] }, { kind: "component", type: EntitySelectInputComponent, selector: "mm-entity-select-input", inputs: ["dataSource", "placeholder", "minSearchLength", "maxResults", "debounceMs", "prefix", "initialDisplayValue", "dialogDataSource", "dialogTitle", "multiSelect", "advancedSearchLabel", "dialogMessages", "messages", "disabled", "required"], outputs: ["entitySelected", "entityCleared", "entitiesSelected"] }, { kind: "component", type: FieldFilterEditorComponent, selector: "mm-field-filter-editor", inputs: ["availableAttributes", "ckTypeId", "hideNavigationProperties", "attributePaths", "enableVariables", "availableVariables", "filters"], outputs: ["filtersChange"] }, { kind: "component", type: QuerySelectorComponent, selector: "mm-query-selector", inputs: ["placeholder", "hint", "disabled", "acceptFamilies"], outputs: ["querySelected", "queriesLoaded"] }, { kind: "component", type: SdTimeFilterToggleComponent, selector: "mm-sd-time-filter-toggle", inputs: ["family", "ignoreTimeFilter"], outputs: ["ignoreTimeFilterChange"] }, { kind: "component", type: LoadingOverlayComponent, selector: "mm-loading-overlay", inputs: ["loading"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
10945
+ `, isInline: true, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;gap:20px;padding:16px;position:relative;flex:1;overflow-y:auto}.config-form.loading{pointer-events:none}.form-field{display:flex;flex-direction:column;gap:6px}.form-field.disabled{opacity:.6}.form-field.flex-1{flex:1}.form-field label{font-weight:600;font-size:.9rem;color:var(--kendo-color-on-app-surface, #212529)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.form-section{padding:16px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.form-section h4{margin:0 0 16px;font-size:.95rem;color:var(--kendo-color-primary, #0d6efd)}.section-hint{margin:0 0 12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d)}.form-row{display:flex;gap:16px}.checkbox-label{margin-left:8px;font-weight:400}.gauge-type-item{display:flex;flex-direction:column;gap:2px}.gauge-type-label{font-weight:500}.gauge-type-desc{font-size:.75rem;color:var(--kendo-color-subtle, #6c757d)}.attribute-item{display:flex;justify-content:space-between;align-items:center;gap:8px;width:100%}.attribute-path{flex:1}.attribute-type{font-size:.75rem;color:var(--kendo-color-subtle, #6c757d);background:var(--kendo-color-surface-alt, #f8f9fa);padding:2px 6px;border-radius:3px}.range-row{display:flex;align-items:center;gap:8px;margin-bottom:8px}.range-input{width:80px}.range-separator{color:var(--kendo-color-subtle, #6c757d)}.color-picker{width:40px;height:32px;padding:2px;border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px;cursor:pointer}.mode-toggle{display:flex;gap:8px}.mode-toggle button{flex:1}.required{color:var(--kendo-color-error, #dc3545)}.query-item{display:flex;flex-direction:column;gap:2px}.query-name{font-weight:500}.query-description{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.column-item{display:flex;justify-content:space-between;gap:16px}.column-path{font-weight:500}.column-type{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox]:not([ngNoCva])[formControlName],input[type=checkbox]:not([ngNoCva])[formControl],input[type=checkbox]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i3.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "component", type: i3.NumericTextBoxComponent, selector: "kendo-numerictextbox", inputs: ["focusableId", "disabled", "readonly", "title", "autoCorrect", "format", "max", "min", "decimals", "placeholder", "step", "spinners", "rangeValidation", "tabindex", "tabIndex", "changeValueOnScroll", "selectOnFocus", "value", "maxlength", "size", "rounded", "fillMode", "inputAttributes"], outputs: ["valueChange", "focus", "blur", "inputFocus", "inputBlur"], exportAs: ["kendoNumericTextBox"] }, { kind: "directive", type: i3.CheckBoxDirective, selector: "input[kendoCheckBox]", inputs: ["size", "rounded"] }, { kind: "ngmodule", type: DropDownsModule }, { kind: "directive", type: i4.ItemTemplateDirective, selector: "[kendoDropDownListItemTemplate],[kendoComboBoxItemTemplate],[kendoAutoCompleteItemTemplate],[kendoMultiSelectItemTemplate]" }, { kind: "component", type: i4.ComboBoxComponent, selector: "kendo-combobox", inputs: ["icon", "svgIcon", "inputAttributes", "showStickyHeader", "focusableId", "allowCustom", "data", "value", "textField", "valueField", "valuePrimitive", "valueNormalizer", "placeholder", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "loading", "suggest", "clearButton", "disabled", "itemDisabled", "readonly", "tabindex", "tabIndex", "filterable", "virtual", "size", "rounded", "fillMode"], outputs: ["valueChange", "selectionChange", "filterChange", "open", "opened", "close", "closed", "focus", "blur", "inputFocus", "inputBlur", "escape"], exportAs: ["kendoComboBox"] }, { kind: "component", type: i4.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }, { kind: "component", type: CkTypeSelectorInputComponent, selector: "mm-ck-type-selector-input", inputs: ["placeholder", "minSearchLength", "maxResults", "debounceMs", "ckModelIds", "allowAbstract", "dialogTitle", "advancedSearchLabel", "derivedFromRtCkTypeId", "messages", "dialogMessages", "disabled", "required"], outputs: ["ckTypeSelected", "ckTypeCleared"] }, { kind: "component", type: EntitySelectInputComponent, selector: "mm-entity-select-input", inputs: ["dataSource", "placeholder", "minSearchLength", "maxResults", "debounceMs", "prefix", "initialDisplayValue", "dialogDataSource", "dialogTitle", "multiSelect", "advancedSearchLabel", "dialogMessages", "messages", "disabled", "required"], outputs: ["entitySelected", "entityCleared", "entitiesSelected"] }, { kind: "component", type: FieldFilterEditorComponent, selector: "mm-field-filter-editor", inputs: ["availableAttributes", "ckTypeId", "hideNavigationProperties", "attributePaths", "enableVariables", "availableVariables", "filters"], outputs: ["filtersChange"] }, { kind: "component", type: QuerySelectorComponent, selector: "mm-query-selector", inputs: ["placeholder", "hint", "disabled", "acceptFamilies"], outputs: ["querySelected", "queriesLoaded"] }, { kind: "component", type: SdTimeFilterToggleComponent, selector: "mm-sd-time-filter-toggle", inputs: ["family", "ignoreTimeFilter"], outputs: ["ignoreTimeFilterChange"] }, { kind: "component", type: EntitySelectorScopePickerComponent, selector: "mm-entity-selector-scope-picker", inputs: ["family", "entitySelectorId", "selectors"], outputs: ["entitySelectorIdChange"] }, { kind: "component", type: LoadingOverlayComponent, selector: "mm-loading-overlay", inputs: ["loading"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
10716
10946
  }
10717
10947
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: GaugeConfigDialogComponent, decorators: [{
10718
10948
  type: Component,
@@ -10727,6 +10957,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
10727
10957
  FieldFilterEditorComponent,
10728
10958
  QuerySelectorComponent,
10729
10959
  SdTimeFilterToggleComponent,
10960
+ EntitySelectorScopePickerComponent,
10730
10961
  LoadingOverlayComponent
10731
10962
  ], template: `
10732
10963
  <div class="config-container">
@@ -10848,6 +11079,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
10848
11079
  [(ignoreTimeFilter)]="ignoreTimeFilter">
10849
11080
  </mm-sd-time-filter-toggle>
10850
11081
 
11082
+ <mm-entity-selector-scope-picker
11083
+ [family]="selectedQueryFamily"
11084
+ [selectors]="availableEntitySelectors"
11085
+ [(entitySelectorId)]="entitySelectorId">
11086
+ </mm-entity-selector-scope-picker>
11087
+
10851
11088
  <!-- Query Mode Selection -->
10852
11089
  <div class="form-field">
10853
11090
  <label>Query Mode</label>
@@ -11176,6 +11413,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
11176
11413
  type: Input
11177
11414
  }], initialIgnoreTimeFilter: [{
11178
11415
  type: Input
11416
+ }], initialEntitySelectorId: [{
11417
+ type: Input
11179
11418
  }], initialQueryMode: [{
11180
11419
  type: Input
11181
11420
  }], initialQueryValueField: [{
@@ -11370,7 +11609,12 @@ class PieChartWidgetComponent {
11370
11609
  }
11371
11610
  buildStreamDataArgs() {
11372
11611
  const ds = this.config.dataSource;
11373
- return this.stateService.resolveStreamDataTimeArgs(ds.ignoreTimeFilter);
11612
+ const timeArgs = this.stateService.resolveStreamDataTimeArgs(ds.ignoreTimeFilter);
11613
+ const rtIds = this.stateService.resolveStreamDataRtIds(ds.entitySelectorId);
11614
+ if (!timeArgs && !rtIds) {
11615
+ return undefined;
11616
+ }
11617
+ return { ...timeArgs, rtIds };
11374
11618
  }
11375
11619
  /**
11376
11620
  * Converts widget filter configuration to GraphQL FieldFilterDto format.
@@ -11485,6 +11729,7 @@ class PieChartConfigDialogComponent {
11485
11729
  initialQueryName;
11486
11730
  initialQueryFamily;
11487
11731
  initialIgnoreTimeFilter;
11732
+ initialEntitySelectorId;
11488
11733
  initialChartType;
11489
11734
  initialCategoryField;
11490
11735
  initialValueField;
@@ -11519,6 +11764,7 @@ class PieChartConfigDialogComponent {
11519
11764
  selectedPersistentQuery = null;
11520
11765
  /** Stream-data opt-out: when true the MeshBoard time filter is not bound to the query. */
11521
11766
  ignoreTimeFilter = false;
11767
+ entitySelectorId;
11522
11768
  queryColumns = [];
11523
11769
  // Filter state
11524
11770
  filters = [];
@@ -11557,6 +11803,10 @@ class PieChartConfigDialogComponent {
11557
11803
  get selectedQueryFamily() {
11558
11804
  return queryFamily(this.selectedPersistentQuery?.ckTypeId) ?? this.initialQueryFamily ?? null;
11559
11805
  }
11806
+ /** Entity selectors available on the current MeshBoard (for the scope picker). */
11807
+ get availableEntitySelectors() {
11808
+ return this.stateService.getEntitySelectors();
11809
+ }
11560
11810
  async ngOnInit() {
11561
11811
  // Initialize filter variables from MeshBoard state
11562
11812
  this.filterVariables = this.stateService.getVariables().map(v => ({
@@ -11574,6 +11824,7 @@ class PieChartConfigDialogComponent {
11574
11824
  this.form.ckQueryTarget = this.initialCkQueryTarget ?? 'models';
11575
11825
  this.form.ckGroupBy = this.initialCkGroupBy ?? 'modelState';
11576
11826
  this.ignoreTimeFilter = this.initialIgnoreTimeFilter ?? false;
11827
+ this.entitySelectorId = this.initialEntitySelectorId;
11577
11828
  // Initialize filters
11578
11829
  if (this.initialFilters && this.initialFilters.length > 0) {
11579
11830
  this.filters = this.initialFilters.map((f, index) => ({
@@ -11736,6 +11987,7 @@ class PieChartConfigDialogComponent {
11736
11987
  result.queryName = this.selectedPersistentQuery.name;
11737
11988
  result.queryFamily = queryFamily(this.selectedPersistentQuery.ckTypeId) ?? this.initialQueryFamily ?? undefined;
11738
11989
  result.ignoreTimeFilter = this.ignoreTimeFilter;
11990
+ result.entitySelectorId = this.entitySelectorId;
11739
11991
  }
11740
11992
  else if (this.form.dataSourceType === 'constructionKitQuery') {
11741
11993
  result.ckQueryTarget = this.form.ckQueryTarget;
@@ -11747,7 +11999,7 @@ class PieChartConfigDialogComponent {
11747
11999
  this.windowRef.close();
11748
12000
  }
11749
12001
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: PieChartConfigDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
11750
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: PieChartConfigDialogComponent, isStandalone: true, selector: "mm-pie-chart-config-dialog", inputs: { initialDataSourceType: "initialDataSourceType", initialQueryRtId: "initialQueryRtId", initialQueryName: "initialQueryName", initialQueryFamily: "initialQueryFamily", initialIgnoreTimeFilter: "initialIgnoreTimeFilter", initialChartType: "initialChartType", initialCategoryField: "initialCategoryField", initialValueField: "initialValueField", initialShowLabels: "initialShowLabels", initialShowLegend: "initialShowLegend", initialLegendPosition: "initialLegendPosition", initialCkQueryTarget: "initialCkQueryTarget", initialCkGroupBy: "initialCkGroupBy", initialFilters: "initialFilters" }, viewQueries: [{ propertyName: "querySelector", first: true, predicate: ["querySelector"], descendants: true }], ngImport: i0, template: `
12002
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: PieChartConfigDialogComponent, isStandalone: true, selector: "mm-pie-chart-config-dialog", inputs: { initialDataSourceType: "initialDataSourceType", initialQueryRtId: "initialQueryRtId", initialQueryName: "initialQueryName", initialQueryFamily: "initialQueryFamily", initialIgnoreTimeFilter: "initialIgnoreTimeFilter", initialEntitySelectorId: "initialEntitySelectorId", initialChartType: "initialChartType", initialCategoryField: "initialCategoryField", initialValueField: "initialValueField", initialShowLabels: "initialShowLabels", initialShowLegend: "initialShowLegend", initialLegendPosition: "initialLegendPosition", initialCkQueryTarget: "initialCkQueryTarget", initialCkGroupBy: "initialCkGroupBy", initialFilters: "initialFilters" }, viewQueries: [{ propertyName: "querySelector", first: true, predicate: ["querySelector"], descendants: true }], ngImport: i0, template: `
11751
12003
  <div class="config-container">
11752
12004
 
11753
12005
  <div class="config-form" [class.loading]="isLoadingInitial">
@@ -11787,6 +12039,12 @@ class PieChartConfigDialogComponent {
11787
12039
  [family]="selectedQueryFamily"
11788
12040
  [(ignoreTimeFilter)]="ignoreTimeFilter">
11789
12041
  </mm-sd-time-filter-toggle>
12042
+
12043
+ <mm-entity-selector-scope-picker
12044
+ [family]="selectedQueryFamily"
12045
+ [selectors]="availableEntitySelectors"
12046
+ [(entitySelectorId)]="entitySelectorId">
12047
+ </mm-entity-selector-scope-picker>
11790
12048
  }
11791
12049
 
11792
12050
  <!-- Construction Kit Query Options -->
@@ -11951,7 +12209,7 @@ class PieChartConfigDialogComponent {
11951
12209
  </button>
11952
12210
  </div>
11953
12211
  </div>
11954
- `, isInline: true, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;flex:1;overflow-y:auto;gap:20px;padding:16px;position:relative}.config-form.loading{pointer-events:none}.config-section{padding:16px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.section-title{margin:0 0 16px;font-size:1rem;font-weight:600;color:var(--kendo-color-primary, #0d6efd)}.section-hint{margin:0 0 12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d)}.form-field{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}.form-field:last-child{margin-bottom:0}.form-field label{font-weight:600;font-size:.9rem;color:var(--kendo-color-on-app-surface, #212529)}.required{color:var(--kendo-color-error, #dc3545)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.radio-group{display:flex;gap:24px}.radio-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.query-item{display:flex;flex-direction:column;gap:2px}.query-name{font-weight:500}.query-description{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.column-item{display:flex;justify-content:space-between;gap:16px}.column-path{font-weight:500}.column-type{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.RadioControlValueAccessor, selector: "input[type=radio]:not([ngNoCva])[formControlName],input[type=radio]:not([ngNoCva])[formControl],input[type=radio]:not([ngNoCva])[ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: InputsModule }, { kind: "directive", type: i3.RadioButtonDirective, selector: "input[kendoRadioButton]", inputs: ["size"] }, { kind: "ngmodule", type: DropDownsModule }, { kind: "directive", type: i4.ItemTemplateDirective, selector: "[kendoDropDownListItemTemplate],[kendoComboBoxItemTemplate],[kendoAutoCompleteItemTemplate],[kendoMultiSelectItemTemplate]" }, { kind: "component", type: i4.ComboBoxComponent, selector: "kendo-combobox", inputs: ["icon", "svgIcon", "inputAttributes", "showStickyHeader", "focusableId", "allowCustom", "data", "value", "textField", "valueField", "valuePrimitive", "valueNormalizer", "placeholder", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "loading", "suggest", "clearButton", "disabled", "itemDisabled", "readonly", "tabindex", "tabIndex", "filterable", "virtual", "size", "rounded", "fillMode"], outputs: ["valueChange", "selectionChange", "filterChange", "open", "opened", "close", "closed", "focus", "blur", "inputFocus", "inputBlur", "escape"], exportAs: ["kendoComboBox"] }, { kind: "component", type: i4.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: FieldFilterEditorComponent, selector: "mm-field-filter-editor", inputs: ["availableAttributes", "ckTypeId", "hideNavigationProperties", "attributePaths", "enableVariables", "availableVariables", "filters"], outputs: ["filtersChange"] }, { kind: "component", type: QuerySelectorComponent, selector: "mm-query-selector", inputs: ["placeholder", "hint", "disabled", "acceptFamilies"], outputs: ["querySelected", "queriesLoaded"] }, { kind: "component", type: SdTimeFilterToggleComponent, selector: "mm-sd-time-filter-toggle", inputs: ["family", "ignoreTimeFilter"], outputs: ["ignoreTimeFilterChange"] }, { kind: "component", type: LoadingOverlayComponent, selector: "mm-loading-overlay", inputs: ["loading"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
12212
+ `, isInline: true, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;flex:1;overflow-y:auto;gap:20px;padding:16px;position:relative}.config-form.loading{pointer-events:none}.config-section{padding:16px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.section-title{margin:0 0 16px;font-size:1rem;font-weight:600;color:var(--kendo-color-primary, #0d6efd)}.section-hint{margin:0 0 12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d)}.form-field{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}.form-field:last-child{margin-bottom:0}.form-field label{font-weight:600;font-size:.9rem;color:var(--kendo-color-on-app-surface, #212529)}.required{color:var(--kendo-color-error, #dc3545)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.radio-group{display:flex;gap:24px}.radio-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.query-item{display:flex;flex-direction:column;gap:2px}.query-name{font-weight:500}.query-description{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.column-item{display:flex;justify-content:space-between;gap:16px}.column-path{font-weight:500}.column-type{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.RadioControlValueAccessor, selector: "input[type=radio]:not([ngNoCva])[formControlName],input[type=radio]:not([ngNoCva])[formControl],input[type=radio]:not([ngNoCva])[ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: InputsModule }, { kind: "directive", type: i3.RadioButtonDirective, selector: "input[kendoRadioButton]", inputs: ["size"] }, { kind: "ngmodule", type: DropDownsModule }, { kind: "directive", type: i4.ItemTemplateDirective, selector: "[kendoDropDownListItemTemplate],[kendoComboBoxItemTemplate],[kendoAutoCompleteItemTemplate],[kendoMultiSelectItemTemplate]" }, { kind: "component", type: i4.ComboBoxComponent, selector: "kendo-combobox", inputs: ["icon", "svgIcon", "inputAttributes", "showStickyHeader", "focusableId", "allowCustom", "data", "value", "textField", "valueField", "valuePrimitive", "valueNormalizer", "placeholder", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "loading", "suggest", "clearButton", "disabled", "itemDisabled", "readonly", "tabindex", "tabIndex", "filterable", "virtual", "size", "rounded", "fillMode"], outputs: ["valueChange", "selectionChange", "filterChange", "open", "opened", "close", "closed", "focus", "blur", "inputFocus", "inputBlur", "escape"], exportAs: ["kendoComboBox"] }, { kind: "component", type: i4.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: FieldFilterEditorComponent, selector: "mm-field-filter-editor", inputs: ["availableAttributes", "ckTypeId", "hideNavigationProperties", "attributePaths", "enableVariables", "availableVariables", "filters"], outputs: ["filtersChange"] }, { kind: "component", type: QuerySelectorComponent, selector: "mm-query-selector", inputs: ["placeholder", "hint", "disabled", "acceptFamilies"], outputs: ["querySelected", "queriesLoaded"] }, { kind: "component", type: SdTimeFilterToggleComponent, selector: "mm-sd-time-filter-toggle", inputs: ["family", "ignoreTimeFilter"], outputs: ["ignoreTimeFilterChange"] }, { kind: "component", type: EntitySelectorScopePickerComponent, selector: "mm-entity-selector-scope-picker", inputs: ["family", "entitySelectorId", "selectors"], outputs: ["entitySelectorIdChange"] }, { kind: "component", type: LoadingOverlayComponent, selector: "mm-loading-overlay", inputs: ["loading"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
11955
12213
  }
11956
12214
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: PieChartConfigDialogComponent, decorators: [{
11957
12215
  type: Component,
@@ -11965,6 +12223,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
11965
12223
  FieldFilterEditorComponent,
11966
12224
  QuerySelectorComponent,
11967
12225
  SdTimeFilterToggleComponent,
12226
+ EntitySelectorScopePickerComponent,
11968
12227
  LoadingOverlayComponent
11969
12228
  ], template: `
11970
12229
  <div class="config-container">
@@ -12006,6 +12265,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
12006
12265
  [family]="selectedQueryFamily"
12007
12266
  [(ignoreTimeFilter)]="ignoreTimeFilter">
12008
12267
  </mm-sd-time-filter-toggle>
12268
+
12269
+ <mm-entity-selector-scope-picker
12270
+ [family]="selectedQueryFamily"
12271
+ [selectors]="availableEntitySelectors"
12272
+ [(entitySelectorId)]="entitySelectorId">
12273
+ </mm-entity-selector-scope-picker>
12009
12274
  }
12010
12275
 
12011
12276
  <!-- Construction Kit Query Options -->
@@ -12184,6 +12449,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
12184
12449
  type: Input
12185
12450
  }], initialIgnoreTimeFilter: [{
12186
12451
  type: Input
12452
+ }], initialEntitySelectorId: [{
12453
+ type: Input
12187
12454
  }], initialChartType: [{
12188
12455
  type: Input
12189
12456
  }], initialCategoryField: [{
@@ -12366,7 +12633,12 @@ class BarChartWidgetComponent {
12366
12633
  }
12367
12634
  buildStreamDataArgs() {
12368
12635
  const ds = this.config.dataSource;
12369
- return this.stateService.resolveStreamDataTimeArgs(ds.ignoreTimeFilter);
12636
+ const timeArgs = this.stateService.resolveStreamDataTimeArgs(ds.ignoreTimeFilter);
12637
+ const rtIds = this.stateService.resolveStreamDataRtIds(ds.entitySelectorId);
12638
+ if (!timeArgs && !rtIds) {
12639
+ return undefined;
12640
+ }
12641
+ return { ...timeArgs, rtIds };
12370
12642
  }
12371
12643
  /**
12372
12644
  * Processes data in Static Series Mode.
@@ -12703,6 +12975,7 @@ class BarChartConfigDialogComponent {
12703
12975
  initialQueryName;
12704
12976
  initialQueryFamily;
12705
12977
  initialIgnoreTimeFilter;
12978
+ initialEntitySelectorId;
12706
12979
  initialChartType;
12707
12980
  initialCategoryField;
12708
12981
  initialSeries;
@@ -12723,6 +12996,12 @@ class BarChartConfigDialogComponent {
12723
12996
  numericColumns = [];
12724
12997
  /** Per-widget opt-out of the MeshBoard time-filter → stream-data binding. */
12725
12998
  ignoreTimeFilter = false;
12999
+ /** Asset-scope binding: id of the entity selector whose selection scopes the stream-data query. */
13000
+ entitySelectorId;
13001
+ /** Entity selectors available on the current MeshBoard (for the scope picker). */
13002
+ get availableEntitySelectors() {
13003
+ return this.stateService.getEntitySelectors();
13004
+ }
12726
13005
  /** Family of the currently selected query — gates the time-filter opt-out toggle. */
12727
13006
  get selectedQueryFamily() {
12728
13007
  return queryFamily(this.selectedPersistentQuery?.ckTypeId) ?? this.initialQueryFamily ?? null;
@@ -12779,6 +13058,7 @@ class BarChartConfigDialogComponent {
12779
13058
  }));
12780
13059
  // Initialize form with initial values
12781
13060
  this.ignoreTimeFilter = this.initialIgnoreTimeFilter ?? false;
13061
+ this.entitySelectorId = this.initialEntitySelectorId;
12782
13062
  this.form.chartType = this.initialChartType ?? 'column';
12783
13063
  this.form.categoryField = this.initialCategoryField ?? '';
12784
13064
  this.form.showLegend = this.initialShowLegend ?? true;
@@ -12937,6 +13217,7 @@ class BarChartConfigDialogComponent {
12937
13217
  queryName: this.selectedPersistentQuery.name,
12938
13218
  queryFamily: family,
12939
13219
  ignoreTimeFilter: this.ignoreTimeFilter,
13220
+ entitySelectorId: this.entitySelectorId,
12940
13221
  chartType: this.form.chartType,
12941
13222
  categoryField: this.form.categoryField,
12942
13223
  series,
@@ -12964,7 +13245,7 @@ class BarChartConfigDialogComponent {
12964
13245
  this.windowRef.close();
12965
13246
  }
12966
13247
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BarChartConfigDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
12967
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: BarChartConfigDialogComponent, isStandalone: true, selector: "mm-bar-chart-config-dialog", inputs: { initialQueryRtId: "initialQueryRtId", initialQueryName: "initialQueryName", initialQueryFamily: "initialQueryFamily", initialIgnoreTimeFilter: "initialIgnoreTimeFilter", initialChartType: "initialChartType", initialCategoryField: "initialCategoryField", initialSeries: "initialSeries", initialSeriesGroupField: "initialSeriesGroupField", initialValueField: "initialValueField", initialShowLegend: "initialShowLegend", initialLegendPosition: "initialLegendPosition", initialShowDataLabels: "initialShowDataLabels", initialColorThresholds: "initialColorThresholds", initialDefaultBarColor: "initialDefaultBarColor", initialFilters: "initialFilters" }, viewQueries: [{ propertyName: "querySelector", first: true, predicate: ["querySelector"], descendants: true }], ngImport: i0, template: `
13248
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: BarChartConfigDialogComponent, isStandalone: true, selector: "mm-bar-chart-config-dialog", inputs: { initialQueryRtId: "initialQueryRtId", initialQueryName: "initialQueryName", initialQueryFamily: "initialQueryFamily", initialIgnoreTimeFilter: "initialIgnoreTimeFilter", initialEntitySelectorId: "initialEntitySelectorId", initialChartType: "initialChartType", initialCategoryField: "initialCategoryField", initialSeries: "initialSeries", initialSeriesGroupField: "initialSeriesGroupField", initialValueField: "initialValueField", initialShowLegend: "initialShowLegend", initialLegendPosition: "initialLegendPosition", initialShowDataLabels: "initialShowDataLabels", initialColorThresholds: "initialColorThresholds", initialDefaultBarColor: "initialDefaultBarColor", initialFilters: "initialFilters" }, viewQueries: [{ propertyName: "querySelector", first: true, predicate: ["querySelector"], descendants: true }], ngImport: i0, template: `
12968
13249
  <div class="config-container">
12969
13250
 
12970
13251
  <div class="config-form" [class.loading]="isLoadingInitial">
@@ -12989,6 +13270,12 @@ class BarChartConfigDialogComponent {
12989
13270
  [family]="selectedQueryFamily"
12990
13271
  [(ignoreTimeFilter)]="ignoreTimeFilter">
12991
13272
  </mm-sd-time-filter-toggle>
13273
+
13274
+ <mm-entity-selector-scope-picker
13275
+ [family]="selectedQueryFamily"
13276
+ [selectors]="availableEntitySelectors"
13277
+ [(entitySelectorId)]="entitySelectorId">
13278
+ </mm-entity-selector-scope-picker>
12992
13279
  </div>
12993
13280
 
12994
13281
  <!-- Field Mapping Section -->
@@ -13230,7 +13517,7 @@ class BarChartConfigDialogComponent {
13230
13517
  </button>
13231
13518
  </div>
13232
13519
  </div>
13233
- `, isInline: true, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;gap:20px;flex:1;overflow-y:auto;padding:16px;position:relative}.config-form.loading{pointer-events:none}.config-section{padding:16px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.section-title{margin:0 0 16px;font-size:1rem;font-weight:600;color:var(--kendo-color-primary, #0d6efd)}.section-hint{margin:0 0 12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d)}.form-field{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}.form-field:last-child{margin-bottom:0}.form-field label{font-weight:600;font-size:.9rem;color:var(--kendo-color-on-app-surface, #212529)}.required{color:var(--kendo-color-error, #dc3545)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.chart-type-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}.radio-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.form-row{display:flex;gap:24px}.checkbox-field{flex-direction:row;align-items:center}.checkbox-field label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.query-item{display:flex;flex-direction:column;gap:2px}.query-name{font-weight:500}.query-description{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.column-item{display:flex;justify-content:space-between;gap:16px}.column-path{font-weight:500}.column-type{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.form-section{margin-top:8px}.form-section h4{margin:0 0 4px;font-size:.95rem;font-weight:600}.threshold-row{display:flex;gap:8px;align-items:center;margin-bottom:8px}.threshold-row label{font-size:.85rem;min-width:70px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox]:not([ngNoCva])[formControlName],input[type=checkbox]:not([ngNoCva])[formControl],input[type=checkbox]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1$1.RadioControlValueAccessor, selector: "input[type=radio]:not([ngNoCva])[formControlName],input[type=radio]:not([ngNoCva])[formControl],input[type=radio]:not([ngNoCva])[ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i3.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "component", type: i3.NumericTextBoxComponent, selector: "kendo-numerictextbox", inputs: ["focusableId", "disabled", "readonly", "title", "autoCorrect", "format", "max", "min", "decimals", "placeholder", "step", "spinners", "rangeValidation", "tabindex", "tabIndex", "changeValueOnScroll", "selectOnFocus", "value", "maxlength", "size", "rounded", "fillMode", "inputAttributes"], outputs: ["valueChange", "focus", "blur", "inputFocus", "inputBlur"], exportAs: ["kendoNumericTextBox"] }, { kind: "directive", type: i3.CheckBoxDirective, selector: "input[kendoCheckBox]", inputs: ["size", "rounded"] }, { kind: "directive", type: i3.RadioButtonDirective, selector: "input[kendoRadioButton]", inputs: ["size"] }, { kind: "ngmodule", type: DropDownsModule }, { kind: "directive", type: i4.ItemTemplateDirective, selector: "[kendoDropDownListItemTemplate],[kendoComboBoxItemTemplate],[kendoAutoCompleteItemTemplate],[kendoMultiSelectItemTemplate]" }, { kind: "component", type: i4.ComboBoxComponent, selector: "kendo-combobox", inputs: ["icon", "svgIcon", "inputAttributes", "showStickyHeader", "focusableId", "allowCustom", "data", "value", "textField", "valueField", "valuePrimitive", "valueNormalizer", "placeholder", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "loading", "suggest", "clearButton", "disabled", "itemDisabled", "readonly", "tabindex", "tabIndex", "filterable", "virtual", "size", "rounded", "fillMode"], outputs: ["valueChange", "selectionChange", "filterChange", "open", "opened", "close", "closed", "focus", "blur", "inputFocus", "inputBlur", "escape"], exportAs: ["kendoComboBox"] }, { kind: "component", type: i4.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }, { kind: "component", type: i4.MultiSelectComponent, selector: "kendo-multiselect", inputs: ["showStickyHeader", "focusableId", "autoClose", "loading", "data", "value", "valueField", "textField", "tabindex", "tabIndex", "size", "rounded", "fillMode", "placeholder", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "disabled", "itemDisabled", "checkboxes", "readonly", "filterable", "virtual", "popupSettings", "listHeight", "valuePrimitive", "clearButton", "tagMapper", "allowCustom", "valueNormalizer", "inputAttributes"], outputs: ["filterChange", "valueChange", "open", "opened", "close", "closed", "focus", "blur", "inputFocus", "inputBlur", "removeTag"], exportAs: ["kendoMultiSelect"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: FieldFilterEditorComponent, selector: "mm-field-filter-editor", inputs: ["availableAttributes", "ckTypeId", "hideNavigationProperties", "attributePaths", "enableVariables", "availableVariables", "filters"], outputs: ["filtersChange"] }, { kind: "component", type: QuerySelectorComponent, selector: "mm-query-selector", inputs: ["placeholder", "hint", "disabled", "acceptFamilies"], outputs: ["querySelected", "queriesLoaded"] }, { kind: "component", type: SdTimeFilterToggleComponent, selector: "mm-sd-time-filter-toggle", inputs: ["family", "ignoreTimeFilter"], outputs: ["ignoreTimeFilterChange"] }, { kind: "component", type: LoadingOverlayComponent, selector: "mm-loading-overlay", inputs: ["loading"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
13520
+ `, isInline: true, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;gap:20px;flex:1;overflow-y:auto;padding:16px;position:relative}.config-form.loading{pointer-events:none}.config-section{padding:16px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.section-title{margin:0 0 16px;font-size:1rem;font-weight:600;color:var(--kendo-color-primary, #0d6efd)}.section-hint{margin:0 0 12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d)}.form-field{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}.form-field:last-child{margin-bottom:0}.form-field label{font-weight:600;font-size:.9rem;color:var(--kendo-color-on-app-surface, #212529)}.required{color:var(--kendo-color-error, #dc3545)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.chart-type-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}.radio-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.form-row{display:flex;gap:24px}.checkbox-field{flex-direction:row;align-items:center}.checkbox-field label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.query-item{display:flex;flex-direction:column;gap:2px}.query-name{font-weight:500}.query-description{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.column-item{display:flex;justify-content:space-between;gap:16px}.column-path{font-weight:500}.column-type{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.form-section{margin-top:8px}.form-section h4{margin:0 0 4px;font-size:.95rem;font-weight:600}.threshold-row{display:flex;gap:8px;align-items:center;margin-bottom:8px}.threshold-row label{font-size:.85rem;min-width:70px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox]:not([ngNoCva])[formControlName],input[type=checkbox]:not([ngNoCva])[formControl],input[type=checkbox]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1$1.RadioControlValueAccessor, selector: "input[type=radio]:not([ngNoCva])[formControlName],input[type=radio]:not([ngNoCva])[formControl],input[type=radio]:not([ngNoCva])[ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i3.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "component", type: i3.NumericTextBoxComponent, selector: "kendo-numerictextbox", inputs: ["focusableId", "disabled", "readonly", "title", "autoCorrect", "format", "max", "min", "decimals", "placeholder", "step", "spinners", "rangeValidation", "tabindex", "tabIndex", "changeValueOnScroll", "selectOnFocus", "value", "maxlength", "size", "rounded", "fillMode", "inputAttributes"], outputs: ["valueChange", "focus", "blur", "inputFocus", "inputBlur"], exportAs: ["kendoNumericTextBox"] }, { kind: "directive", type: i3.CheckBoxDirective, selector: "input[kendoCheckBox]", inputs: ["size", "rounded"] }, { kind: "directive", type: i3.RadioButtonDirective, selector: "input[kendoRadioButton]", inputs: ["size"] }, { kind: "ngmodule", type: DropDownsModule }, { kind: "directive", type: i4.ItemTemplateDirective, selector: "[kendoDropDownListItemTemplate],[kendoComboBoxItemTemplate],[kendoAutoCompleteItemTemplate],[kendoMultiSelectItemTemplate]" }, { kind: "component", type: i4.ComboBoxComponent, selector: "kendo-combobox", inputs: ["icon", "svgIcon", "inputAttributes", "showStickyHeader", "focusableId", "allowCustom", "data", "value", "textField", "valueField", "valuePrimitive", "valueNormalizer", "placeholder", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "loading", "suggest", "clearButton", "disabled", "itemDisabled", "readonly", "tabindex", "tabIndex", "filterable", "virtual", "size", "rounded", "fillMode"], outputs: ["valueChange", "selectionChange", "filterChange", "open", "opened", "close", "closed", "focus", "blur", "inputFocus", "inputBlur", "escape"], exportAs: ["kendoComboBox"] }, { kind: "component", type: i4.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }, { kind: "component", type: i4.MultiSelectComponent, selector: "kendo-multiselect", inputs: ["showStickyHeader", "focusableId", "autoClose", "loading", "data", "value", "valueField", "textField", "tabindex", "tabIndex", "size", "rounded", "fillMode", "placeholder", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "disabled", "itemDisabled", "checkboxes", "readonly", "filterable", "virtual", "popupSettings", "listHeight", "valuePrimitive", "clearButton", "tagMapper", "allowCustom", "valueNormalizer", "inputAttributes"], outputs: ["filterChange", "valueChange", "open", "opened", "close", "closed", "focus", "blur", "inputFocus", "inputBlur", "removeTag"], exportAs: ["kendoMultiSelect"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: FieldFilterEditorComponent, selector: "mm-field-filter-editor", inputs: ["availableAttributes", "ckTypeId", "hideNavigationProperties", "attributePaths", "enableVariables", "availableVariables", "filters"], outputs: ["filtersChange"] }, { kind: "component", type: QuerySelectorComponent, selector: "mm-query-selector", inputs: ["placeholder", "hint", "disabled", "acceptFamilies"], outputs: ["querySelected", "queriesLoaded"] }, { kind: "component", type: SdTimeFilterToggleComponent, selector: "mm-sd-time-filter-toggle", inputs: ["family", "ignoreTimeFilter"], outputs: ["ignoreTimeFilterChange"] }, { kind: "component", type: EntitySelectorScopePickerComponent, selector: "mm-entity-selector-scope-picker", inputs: ["family", "entitySelectorId", "selectors"], outputs: ["entitySelectorIdChange"] }, { kind: "component", type: LoadingOverlayComponent, selector: "mm-loading-overlay", inputs: ["loading"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
13234
13521
  }
13235
13522
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: BarChartConfigDialogComponent, decorators: [{
13236
13523
  type: Component,
@@ -13244,6 +13531,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
13244
13531
  FieldFilterEditorComponent,
13245
13532
  QuerySelectorComponent,
13246
13533
  SdTimeFilterToggleComponent,
13534
+ EntitySelectorScopePickerComponent,
13247
13535
  LoadingOverlayComponent
13248
13536
  ], template: `
13249
13537
  <div class="config-container">
@@ -13270,6 +13558,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
13270
13558
  [family]="selectedQueryFamily"
13271
13559
  [(ignoreTimeFilter)]="ignoreTimeFilter">
13272
13560
  </mm-sd-time-filter-toggle>
13561
+
13562
+ <mm-entity-selector-scope-picker
13563
+ [family]="selectedQueryFamily"
13564
+ [selectors]="availableEntitySelectors"
13565
+ [(entitySelectorId)]="entitySelectorId">
13566
+ </mm-entity-selector-scope-picker>
13273
13567
  </div>
13274
13568
 
13275
13569
  <!-- Field Mapping Section -->
@@ -13523,6 +13817,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
13523
13817
  type: Input
13524
13818
  }], initialIgnoreTimeFilter: [{
13525
13819
  type: Input
13820
+ }], initialEntitySelectorId: [{
13821
+ type: Input
13526
13822
  }], initialChartType: [{
13527
13823
  type: Input
13528
13824
  }], initialCategoryField: [{
@@ -13547,8 +13843,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
13547
13843
  type: Input
13548
13844
  }] } });
13549
13845
 
13846
+ /** Series colours so a series' min/max band and its avg line share one hue. */
13847
+ const SERIES_PALETTE = ['#3b82f6', '#ef4444', '#10b981', '#f59e0b', '#8b5cf6', '#ec4899', '#14b8a6', '#f97316'];
13550
13848
  class LineChartWidgetComponent {
13551
13849
  queryExecutor = inject(QueryExecutorService);
13850
+ elementRef = inject(ElementRef);
13851
+ ngZone = inject(NgZone);
13852
+ // FE-2 resize handling: re-query at a new resolution when the chart is resized materially.
13853
+ resizeObserver;
13854
+ resizeTimer;
13855
+ /** The downsampling `limit` used by the last load; 0 when the last load wasn't downsampled. */
13856
+ lastLimit = 0;
13552
13857
  static SUPPORTED_ROW_TYPES = new Set([
13553
13858
  'RtSimpleQueryRow',
13554
13859
  'RtAggregationQueryRow',
@@ -13571,11 +13876,17 @@ class LineChartWidgetComponent {
13571
13876
  ...(ngDevMode ? [{ debugName: "_error" }] : /* istanbul ignore next */ []));
13572
13877
  _seriesUnitMap = signal(new Map(), /* @ts-ignore */
13573
13878
  ...(ngDevMode ? [{ debugName: "_seriesUnitMap" }] : /* istanbul ignore next */ []));
13879
+ // Debug aid: how many rows came back vs. how many distinct category points
13880
+ // actually plotted. A large `rows` with tiny `points` flags a data collapse
13881
+ // (e.g. all rows sharing one category/timestamp).
13882
+ _dataInfo = signal(null, /* @ts-ignore */
13883
+ ...(ngDevMode ? [{ debugName: "_dataInfo" }] : /* istanbul ignore next */ []));
13574
13884
  isLoading = this._isLoading.asReadonly();
13575
13885
  categories = this._categories.asReadonly();
13576
13886
  seriesData = this._seriesData.asReadonly();
13577
13887
  valueAxes = this._valueAxes.asReadonly();
13578
13888
  error = this._error.asReadonly();
13889
+ dataInfo = this._dataInfo.asReadonly();
13579
13890
  data = computed(() => this._seriesData(), /* @ts-ignore */
13580
13891
  ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
13581
13892
  plotBands = computed(() => {
@@ -13639,8 +13950,37 @@ class LineChartWidgetComponent {
13639
13950
  const maxLen = 18;
13640
13951
  return e.value.length > maxLen ? e.value.substring(0, maxLen) + '...' : e.value;
13641
13952
  };
13642
- ngOnInit() {
13953
+ ngAfterViewInit() {
13954
+ // Defer the initial load to after view init so the host has a measured width — the
13955
+ // downsampling bucket count (FE-1) is derived from the rendered pixel width.
13643
13956
  this.loadData();
13957
+ this.setupResizeObserver();
13958
+ }
13959
+ ngOnDestroy() {
13960
+ this.resizeObserver?.disconnect();
13961
+ if (this.resizeTimer)
13962
+ clearTimeout(this.resizeTimer);
13963
+ }
13964
+ /**
13965
+ * FE-2: when the chart is resized so the pixel-derived bucket count would change materially
13966
+ * (>15%), re-query at the new resolution after a 300 ms debounce. Only relevant while
13967
+ * downsampling is active (lastLimit > 0); raw charts don't depend on width.
13968
+ */
13969
+ setupResizeObserver() {
13970
+ if (typeof ResizeObserver === 'undefined')
13971
+ return;
13972
+ this.resizeObserver = new ResizeObserver(() => this.onResize());
13973
+ this.resizeObserver.observe(this.elementRef.nativeElement);
13974
+ }
13975
+ onResize() {
13976
+ if (this.lastLimit <= 0)
13977
+ return; // last load wasn't downsampled (or hasn't completed yet)
13978
+ const newLimit = this.computeDownsampleLimit();
13979
+ if (Math.abs(newLimit - this.lastLimit) / this.lastLimit < 0.15)
13980
+ return;
13981
+ if (this.resizeTimer)
13982
+ clearTimeout(this.resizeTimer);
13983
+ this.resizeTimer = setTimeout(() => this.ngZone.run(() => this.loadData()), 300);
13644
13984
  }
13645
13985
  ngOnChanges(changes) {
13646
13986
  if (changes['config'] && !changes['config'].firstChange) {
@@ -13681,6 +14021,8 @@ class LineChartWidgetComponent {
13681
14021
  // falls back to a one-time lookup by rtId. streamDataArgs is sent
13682
14022
  // unconditionally because the runtime path ignores it.
13683
14023
  const streamDataArgs = this.buildStreamDataArgs();
14024
+ // Remember the resolution used so a later resize can decide whether to re-query (FE-2).
14025
+ this.lastLimit = streamDataArgs?.limit ?? 0;
13684
14026
  const result = await firstValueFrom(this.queryExecutor.execute(queryDataSource.queryFamily, queryDataSource.queryRtId, {
13685
14027
  fieldFilter: fieldFilter ?? undefined,
13686
14028
  streamDataArgs
@@ -13689,52 +14031,94 @@ class LineChartWidgetComponent {
13689
14031
  throw err;
13690
14032
  })));
13691
14033
  const filteredRows = result.rows.filter(row => LineChartWidgetComponent.SUPPORTED_ROW_TYPES.has(row.__typename ?? ''));
13692
- this.processData(filteredRows);
14034
+ const downsampled = streamDataArgs?.queryMode === QueryModeDto.DownsamplingDto;
14035
+ this.processData(filteredRows, downsampled);
14036
+ this._dataInfo.set({ rows: filteredRows.length, points: this._categories().length, total: result.totalCount });
13693
14037
  this._isLoading.set(false);
13694
14038
  }
13695
14039
  catch (err) {
13696
14040
  console.error('Error loading Line Chart data:', err);
13697
14041
  this._error.set('Failed to load data');
14042
+ this._dataInfo.set(null);
13698
14043
  this._isLoading.set(false);
13699
14044
  }
13700
14045
  }
13701
14046
  buildStreamDataArgs() {
13702
14047
  const ds = this.config.dataSource;
13703
- return this.stateService.resolveStreamDataTimeArgs(ds.ignoreTimeFilter);
14048
+ const timeArgs = this.stateService.resolveStreamDataTimeArgs(ds.ignoreTimeFilter);
14049
+ const rtIds = this.stateService.resolveStreamDataRtIds(ds.entitySelectorId);
14050
+ if (!timeArgs && !rtIds) {
14051
+ return undefined;
14052
+ }
14053
+ // Auto-downsampling (FE-1): for stream-data queries with a resolved time range, ask the
14054
+ // backend to reduce to ~one bucket per 2 px of chart width instead of streaming every row.
14055
+ // Without a time range the backend can't downsample (needs from/to/limit) → raw fallback.
14056
+ if (ds.queryFamily === 'streamData' && timeArgs?.from && timeArgs?.to) {
14057
+ return { ...timeArgs, rtIds, limit: this.computeDownsampleLimit(), queryMode: QueryModeDto.DownsamplingDto };
14058
+ }
14059
+ return { ...timeArgs, rtIds };
14060
+ }
14061
+ /**
14062
+ * Bucket count for backend downsampling, sized to the rendered width (~1 bucket / 2 px),
14063
+ * clamped to [50, 4000]. Falls back to 1500 before the host has a measured width.
14064
+ */
14065
+ computeDownsampleLimit() {
14066
+ const width = this.elementRef.nativeElement?.offsetWidth ?? 0;
14067
+ const effective = width > 50 ? width : 1500;
14068
+ return Math.min(4000, Math.max(50, Math.round(effective / 2)));
13704
14069
  }
13705
14070
  /**
13706
14071
  * Processes query rows into line chart data.
13707
14072
  * Groups by seriesGroupField, orders by categoryField (date), supports multi-axis by unitField.
13708
14073
  */
13709
- processData(filteredRows) {
14074
+ processData(filteredRows, downsample = false) {
13710
14075
  const categoryField = this.config.categoryField;
13711
14076
  const seriesGroupField = this.config.seriesGroupField;
13712
14077
  const valueField = this.config.valueField;
13713
14078
  const unitField = this.config.unitField;
13714
- // Collect data: category -> seriesGroup -> value
14079
+ // Collect data: category -> seriesGroup -> value (and, for downsampling, the min/max band)
13715
14080
  const dataMap = new Map();
14081
+ const bandMap = new Map();
13716
14082
  const allCategories = new Map(); // label -> parsed date for sorting
13717
14083
  const allSeriesGroups = new Set();
13718
14084
  const seriesUnitMap = new Map(); // seriesGroup -> unit
13719
14085
  for (const row of filteredRows) {
13720
- let categoryValue = '';
14086
+ // In downsampling mode the x-axis is the bucket timestamp (the persisted category column
14087
+ // comes back reduced, e.g. `window_start_max`); use the row's bin timestamp instead.
14088
+ let categoryValue = downsample && row.timestamp ? String(row.timestamp) : '';
13721
14089
  let seriesGroupValue = '';
13722
- let numericValue = 0;
14090
+ let lineValue = downsample ? null : 0;
14091
+ let minValue = null;
14092
+ let maxValue = null;
13723
14093
  let unitValue = '';
13724
14094
  for (const cell of row.cells) {
13725
- if (matchesAttributePath(cell.attributePath, categoryField)) {
14095
+ const path = cell.attributePath;
14096
+ if (!downsample && matchesAttributePath(path, categoryField)) {
13726
14097
  categoryValue = String(cell.value ?? '');
13727
14098
  }
13728
- else if (matchesAttributePath(cell.attributePath, seriesGroupField)) {
14099
+ else if (matchesAttributePath(path, seriesGroupField)) {
13729
14100
  seriesGroupValue = String(cell.value ?? '');
13730
14101
  }
13731
- else if (matchesAttributePath(cell.attributePath, valueField)) {
13732
- const val = cell.value;
13733
- numericValue = typeof val === 'number' ? val : parseFloat(String(val));
13734
- if (isNaN(numericValue))
13735
- numericValue = 0;
14102
+ else if (matchesAttributePath(path, valueField)) {
14103
+ if (downsample) {
14104
+ // The value column is reduced to <field>_avg / _min / _max — split them out so the
14105
+ // avg drives the line and min/max form the envelope band.
14106
+ const lower = path.toLowerCase();
14107
+ if (lower.endsWith('_min'))
14108
+ minValue = this.toNumber(cell.value);
14109
+ else if (lower.endsWith('_max'))
14110
+ maxValue = this.toNumber(cell.value);
14111
+ else
14112
+ lineValue = this.toNumber(cell.value); // _avg (or an unsuffixed value)
14113
+ }
14114
+ else {
14115
+ const val = cell.value;
14116
+ lineValue = typeof val === 'number' ? val : parseFloat(String(val));
14117
+ if (isNaN(lineValue))
14118
+ lineValue = 0;
14119
+ }
13736
14120
  }
13737
- else if (unitField && matchesAttributePath(cell.attributePath, unitField)) {
14121
+ else if (unitField && matchesAttributePath(path, unitField)) {
13738
14122
  unitValue = String(cell.value ?? '');
13739
14123
  }
13740
14124
  }
@@ -13747,7 +14131,13 @@ class LineChartWidgetComponent {
13747
14131
  if (!dataMap.has(categoryValue)) {
13748
14132
  dataMap.set(categoryValue, new Map());
13749
14133
  }
13750
- dataMap.get(categoryValue).set(seriesGroupValue, numericValue);
14134
+ dataMap.get(categoryValue).set(seriesGroupValue, lineValue);
14135
+ if (downsample && minValue !== null && maxValue !== null) {
14136
+ if (!bandMap.has(categoryValue)) {
14137
+ bandMap.set(categoryValue, new Map());
14138
+ }
14139
+ bandMap.get(categoryValue).set(seriesGroupValue, { from: minValue, to: maxValue });
14140
+ }
13751
14141
  // Track unit per series
13752
14142
  if (unitField && unitValue) {
13753
14143
  seriesUnitMap.set(seriesGroupValue, unitValue);
@@ -13776,17 +14166,23 @@ class LineChartWidgetComponent {
13776
14166
  });
13777
14167
  }
13778
14168
  // Build series data
13779
- const seriesData = seriesGroups.map(seriesGroup => {
14169
+ const seriesData = seriesGroups.map((seriesGroup, index) => {
13780
14170
  const data = categoryKeys.map(categoryKey => {
13781
14171
  return dataMap.get(categoryKey)?.get(seriesGroup) ?? null;
13782
14172
  });
14173
+ // Build the min/max envelope band only when downsampling produced one for this series.
14174
+ const band = downsample && bandMap.size > 0
14175
+ ? categoryKeys.map(categoryKey => bandMap.get(categoryKey)?.get(seriesGroup) ?? null)
14176
+ : undefined;
13783
14177
  const unit = seriesUnitMap.get(seriesGroup);
13784
14178
  const axisName = unit ? `unit_${this.sanitizeAxisName(unit)}` : undefined;
13785
14179
  return {
13786
14180
  name: seriesGroup,
13787
14181
  data,
14182
+ band,
13788
14183
  unit,
13789
- axisName
14184
+ axisName,
14185
+ color: SERIES_PALETTE[index % SERIES_PALETTE.length]
13790
14186
  };
13791
14187
  });
13792
14188
  this._categories.set(categories);
@@ -13823,6 +14219,13 @@ class LineChartWidgetComponent {
13823
14219
  sanitizeAxisName(name) {
13824
14220
  return name.replace(/[^a-zA-Z0-9]/g, '_');
13825
14221
  }
14222
+ /** Parses a cell value to a number, or null when missing / non-numeric (renders as a gap). */
14223
+ toNumber(value) {
14224
+ if (value === null || value === undefined)
14225
+ return null;
14226
+ const n = typeof value === 'number' ? value : parseFloat(String(value));
14227
+ return isNaN(n) ? null : n;
14228
+ }
13826
14229
  convertFiltersToDto(filters) {
13827
14230
  const variables = this.stateService.getVariables();
13828
14231
  return this.variableService.convertToFieldFilterDto(filters, variables);
@@ -13841,6 +14244,11 @@ class LineChartWidgetComponent {
13841
14244
  <span>{{ error() }}</span>
13842
14245
  </div>
13843
14246
  } @else {
14247
+ @if (dataInfo(); as info) {
14248
+ <span class="data-count" [title]="'Loaded rows · distinct category points (totalCount ' + info.total + ')'">
14249
+ {{ info.rows }} rows · {{ info.points }} pts
14250
+ </span>
14251
+ }
13844
14252
  <kendo-chart class="chart-container" [plotArea]="{ background: 'transparent', margin: { top: 0, right: 0, bottom: 0, left: 0 } }">
13845
14253
  <kendo-chart-area [background]="'transparent'"></kendo-chart-area>
13846
14254
 
@@ -13885,12 +14293,26 @@ class LineChartWidgetComponent {
13885
14293
 
13886
14294
  <kendo-chart-series>
13887
14295
  @for (series of seriesData(); track series.name) {
14296
+ @if (series.band) {
14297
+ <kendo-chart-series-item
14298
+ [type]="'rangeArea'"
14299
+ [data]="series.band"
14300
+ [fromField]="'from'"
14301
+ [toField]="'to'"
14302
+ [name]="series.name + ' (min/max)'"
14303
+ [axis]="series.axisName ?? ''"
14304
+ [color]="series.color"
14305
+ [opacity]="0.18"
14306
+ [markers]="{ visible: false }">
14307
+ </kendo-chart-series-item>
14308
+ }
13888
14309
  <kendo-chart-series-item
13889
14310
  [type]="chartType()"
13890
14311
  [style]="'smooth'"
13891
14312
  [data]="series.data"
13892
14313
  [name]="series.name"
13893
14314
  [axis]="series.axisName ?? ''"
14315
+ [color]="series.color"
13894
14316
  [opacity]="0.7"
13895
14317
  [markers]="{ visible: config.showMarkers ?? false }">
13896
14318
  </kendo-chart-series-item>
@@ -13913,7 +14335,7 @@ class LineChartWidgetComponent {
13913
14335
  </kendo-chart>
13914
14336
  }
13915
14337
  </div>
13916
- `, isInline: true, styles: [":host{display:block;width:100%;height:100%}.line-chart-widget{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:8px;box-sizing:border-box;overflow:hidden}.line-chart-widget.loading,.line-chart-widget.error{opacity:.7}.loading-indicator,.error-message{display:flex;align-items:center;justify-content:center;height:100%;width:100%}.loading-indicator span{font-size:1.5rem;color:var(--kendo-color-subtle, #6c757d)}.error-message span{color:var(--kendo-color-error, #dc3545);font-size:.875rem}.chart-container{width:100%;height:100%}kendo-chart{width:100%;height:100%}.chart-tooltip{padding:4px 8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ChartsModule }, { kind: "component", type: i1$7.ChartComponent, selector: "kendo-chart", inputs: ["pannable", "renderAs", "seriesColors", "subtitle", "title", "noData", "observeStyles", "transitions", "zoomable", "axisDefaults", "categoryAxis", "chartArea", "legend", "panes", "paneDefaults", "plotArea", "series", "seriesDefaults", "tooltip", "valueAxis", "xAxis", "yAxis", "resizeRateLimit", "popupSettings", "drilldownLevel"], outputs: ["axisLabelClick", "drag", "dragEnd", "dragStart", "legendItemHover", "legendItemLeave", "noteClick", "noteHover", "noteLeave", "paneRender", "plotAreaClick", "plotAreaHover", "plotAreaLeave", "render", "select", "selectEnd", "selectStart", "seriesClick", "drilldown", "seriesHover", "seriesOver", "seriesLeave", "zoom", "zoomEnd", "zoomStart", "legendItemClick", "drilldownLevelChange"], exportAs: ["kendoChart"] }, { kind: "directive", type: i1$7.SeriesTooltipTemplateDirective, selector: "[kendoChartSeriesTooltipTemplate]" }, { kind: "component", type: i1$7.CategoryAxisComponent, selector: "kendo-chart-category-axis" }, { kind: "component", type: i1$7.CategoryAxisItemComponent, selector: "kendo-chart-category-axis-item", inputs: ["autoBaseUnitSteps", "axisCrossingValue", "background", "baseUnit", "baseUnitStep", "categories", "color", "justified", "line", "majorGridLines", "majorTicks", "max", "maxDateGroups", "maxDivisions", "min", "minorGridLines", "minorTicks", "name", "pane", "plotBands", "reverse", "roundToBaseUnit", "startAngle", "type", "visible", "weekStartDay", "crosshair", "labels", "notes", "select", "title", "rangeLabels", "highlight"] }, { kind: "component", type: i1$7.CategoryAxisLabelsComponent, selector: "kendo-chart-category-axis-item-labels", inputs: ["background", "border", "color", "content", "culture", "dateFormats", "font", "format", "margin", "mirror", "padding", "position", "rotation", "skip", "step", "visible", "visual"] }, { kind: "component", type: i1$7.ChartAreaComponent, selector: "kendo-chart-area", inputs: ["background", "border", "height", "margin", "opacity", "width"] }, { kind: "component", type: i1$7.LegendComponent, selector: "kendo-chart-legend", inputs: ["align", "background", "border", "height", "labels", "margin", "offsetX", "offsetY", "orientation", "padding", "position", "reverse", "visible", "width", "markers", "spacing", "inactiveItems", "item", "title", "focusHighlight"] }, { kind: "component", type: i1$7.SeriesComponent, selector: "kendo-chart-series" }, { kind: "component", type: i1$7.SeriesItemComponent, selector: "kendo-chart-series-item", inputs: ["aggregate", "autoFit", "axis", "border", "categoryAxis", "categoryField", "closeField", "color", "colorField", "connectors", "currentField", "dashType", "data", "downColor", "downColorField", "drilldownField", "dynamicHeight", "dynamicSlope", "errorHighField", "errorLowField", "explodeField", "field", "fromField", "gap", "highField", "holeSize", "line", "lowField", "lowerField", "margin", "maxSize", "mean", "meanField", "median", "medianField", "minSize", "missingValues", "name", "neckRatio", "negativeColor", "negativeValues", "noteTextField", "opacity", "openField", "outliersField", "overlay", "padding", "q1Field", "q3Field", "segmentSpacing", "size", "sizeField", "spacing", "stack", "startAngle", "lineStyle", "summaryField", "target", "toField", "type", "upperField", "visible", "visibleInLegend", "visibleInLegendField", "visual", "width", "whiskers", "xAxis", "xErrorHighField", "xErrorLowField", "xField", "yAxis", "yErrorHighField", "yErrorLowField", "yField", "zIndex", "trendline", "for", "legendItem", "pattern", "patternField", "errorBars", "extremes", "highlight", "labels", "markers", "notes", "outliers", "tooltip"] }, { kind: "component", type: i1$7.TooltipComponent, selector: "kendo-chart-tooltip", inputs: ["background", "border", "color", "font", "format", "opacity", "padding", "shared", "visible"] }, { kind: "component", type: i1$7.ValueAxisComponent, selector: "kendo-chart-value-axis" }, { kind: "component", type: i1$7.ValueAxisItemComponent, selector: "kendo-chart-value-axis-item", inputs: ["axisCrossingValue", "background", "color", "line", "majorGridLines", "majorTicks", "majorUnit", "max", "min", "minorGridLines", "minorTicks", "minorUnit", "name", "narrowRange", "pane", "plotBands", "reverse", "type", "visible", "crosshair", "labels", "notes", "title"] }, { kind: "component", type: WidgetNotConfiguredComponent, selector: "mm-widget-not-configured" }], changeDetection: i0.ChangeDetectionStrategy.Eager });
14338
+ `, isInline: true, styles: [":host{display:block;width:100%;height:100%}.line-chart-widget{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:8px;box-sizing:border-box;overflow:hidden}.data-count{position:absolute;top:2px;right:6px;z-index:2;font-size:.7rem;line-height:1;opacity:.55;color:var(--kendo-color-subtle, #6c757d);pointer-events:none;-webkit-user-select:none;user-select:none;font-variant-numeric:tabular-nums}.line-chart-widget.loading,.line-chart-widget.error{opacity:.7}.loading-indicator,.error-message{display:flex;align-items:center;justify-content:center;height:100%;width:100%}.loading-indicator span{font-size:1.5rem;color:var(--kendo-color-subtle, #6c757d)}.error-message span{color:var(--kendo-color-error, #dc3545);font-size:.875rem}.chart-container{width:100%;height:100%}kendo-chart{width:100%;height:100%}.chart-tooltip{padding:4px 8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ChartsModule }, { kind: "component", type: i1$7.ChartComponent, selector: "kendo-chart", inputs: ["pannable", "renderAs", "seriesColors", "subtitle", "title", "noData", "observeStyles", "transitions", "zoomable", "axisDefaults", "categoryAxis", "chartArea", "legend", "panes", "paneDefaults", "plotArea", "series", "seriesDefaults", "tooltip", "valueAxis", "xAxis", "yAxis", "resizeRateLimit", "popupSettings", "drilldownLevel"], outputs: ["axisLabelClick", "drag", "dragEnd", "dragStart", "legendItemHover", "legendItemLeave", "noteClick", "noteHover", "noteLeave", "paneRender", "plotAreaClick", "plotAreaHover", "plotAreaLeave", "render", "select", "selectEnd", "selectStart", "seriesClick", "drilldown", "seriesHover", "seriesOver", "seriesLeave", "zoom", "zoomEnd", "zoomStart", "legendItemClick", "drilldownLevelChange"], exportAs: ["kendoChart"] }, { kind: "directive", type: i1$7.SeriesTooltipTemplateDirective, selector: "[kendoChartSeriesTooltipTemplate]" }, { kind: "component", type: i1$7.CategoryAxisComponent, selector: "kendo-chart-category-axis" }, { kind: "component", type: i1$7.CategoryAxisItemComponent, selector: "kendo-chart-category-axis-item", inputs: ["autoBaseUnitSteps", "axisCrossingValue", "background", "baseUnit", "baseUnitStep", "categories", "color", "justified", "line", "majorGridLines", "majorTicks", "max", "maxDateGroups", "maxDivisions", "min", "minorGridLines", "minorTicks", "name", "pane", "plotBands", "reverse", "roundToBaseUnit", "startAngle", "type", "visible", "weekStartDay", "crosshair", "labels", "notes", "select", "title", "rangeLabels", "highlight"] }, { kind: "component", type: i1$7.CategoryAxisLabelsComponent, selector: "kendo-chart-category-axis-item-labels", inputs: ["background", "border", "color", "content", "culture", "dateFormats", "font", "format", "margin", "mirror", "padding", "position", "rotation", "skip", "step", "visible", "visual"] }, { kind: "component", type: i1$7.ChartAreaComponent, selector: "kendo-chart-area", inputs: ["background", "border", "height", "margin", "opacity", "width"] }, { kind: "component", type: i1$7.LegendComponent, selector: "kendo-chart-legend", inputs: ["align", "background", "border", "height", "labels", "margin", "offsetX", "offsetY", "orientation", "padding", "position", "reverse", "visible", "width", "markers", "spacing", "inactiveItems", "item", "title", "focusHighlight"] }, { kind: "component", type: i1$7.SeriesComponent, selector: "kendo-chart-series" }, { kind: "component", type: i1$7.SeriesItemComponent, selector: "kendo-chart-series-item", inputs: ["aggregate", "autoFit", "axis", "border", "categoryAxis", "categoryField", "closeField", "color", "colorField", "connectors", "currentField", "dashType", "data", "downColor", "downColorField", "drilldownField", "dynamicHeight", "dynamicSlope", "errorHighField", "errorLowField", "explodeField", "field", "fromField", "gap", "highField", "holeSize", "line", "lowField", "lowerField", "margin", "maxSize", "mean", "meanField", "median", "medianField", "minSize", "missingValues", "name", "neckRatio", "negativeColor", "negativeValues", "noteTextField", "opacity", "openField", "outliersField", "overlay", "padding", "q1Field", "q3Field", "segmentSpacing", "size", "sizeField", "spacing", "stack", "startAngle", "lineStyle", "summaryField", "target", "toField", "type", "upperField", "visible", "visibleInLegend", "visibleInLegendField", "visual", "width", "whiskers", "xAxis", "xErrorHighField", "xErrorLowField", "xField", "yAxis", "yErrorHighField", "yErrorLowField", "yField", "zIndex", "trendline", "for", "legendItem", "pattern", "patternField", "errorBars", "extremes", "highlight", "labels", "markers", "notes", "outliers", "tooltip"] }, { kind: "component", type: i1$7.TooltipComponent, selector: "kendo-chart-tooltip", inputs: ["background", "border", "color", "font", "format", "opacity", "padding", "shared", "visible"] }, { kind: "component", type: i1$7.ValueAxisComponent, selector: "kendo-chart-value-axis" }, { kind: "component", type: i1$7.ValueAxisItemComponent, selector: "kendo-chart-value-axis-item", inputs: ["axisCrossingValue", "background", "color", "line", "majorGridLines", "majorTicks", "majorUnit", "max", "min", "minorGridLines", "minorTicks", "minorUnit", "name", "narrowRange", "pane", "plotBands", "reverse", "type", "visible", "crosshair", "labels", "notes", "title"] }, { kind: "component", type: WidgetNotConfiguredComponent, selector: "mm-widget-not-configured" }], changeDetection: i0.ChangeDetectionStrategy.Eager });
13917
14339
  }
13918
14340
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: LineChartWidgetComponent, decorators: [{
13919
14341
  type: Component,
@@ -13934,6 +14356,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
13934
14356
  <span>{{ error() }}</span>
13935
14357
  </div>
13936
14358
  } @else {
14359
+ @if (dataInfo(); as info) {
14360
+ <span class="data-count" [title]="'Loaded rows · distinct category points (totalCount ' + info.total + ')'">
14361
+ {{ info.rows }} rows · {{ info.points }} pts
14362
+ </span>
14363
+ }
13937
14364
  <kendo-chart class="chart-container" [plotArea]="{ background: 'transparent', margin: { top: 0, right: 0, bottom: 0, left: 0 } }">
13938
14365
  <kendo-chart-area [background]="'transparent'"></kendo-chart-area>
13939
14366
 
@@ -13978,12 +14405,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
13978
14405
 
13979
14406
  <kendo-chart-series>
13980
14407
  @for (series of seriesData(); track series.name) {
14408
+ @if (series.band) {
14409
+ <kendo-chart-series-item
14410
+ [type]="'rangeArea'"
14411
+ [data]="series.band"
14412
+ [fromField]="'from'"
14413
+ [toField]="'to'"
14414
+ [name]="series.name + ' (min/max)'"
14415
+ [axis]="series.axisName ?? ''"
14416
+ [color]="series.color"
14417
+ [opacity]="0.18"
14418
+ [markers]="{ visible: false }">
14419
+ </kendo-chart-series-item>
14420
+ }
13981
14421
  <kendo-chart-series-item
13982
14422
  [type]="chartType()"
13983
14423
  [style]="'smooth'"
13984
14424
  [data]="series.data"
13985
14425
  [name]="series.name"
13986
14426
  [axis]="series.axisName ?? ''"
14427
+ [color]="series.color"
13987
14428
  [opacity]="0.7"
13988
14429
  [markers]="{ visible: config.showMarkers ?? false }">
13989
14430
  </kendo-chart-series-item>
@@ -14006,7 +14447,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
14006
14447
  </kendo-chart>
14007
14448
  }
14008
14449
  </div>
14009
- `, changeDetection: ChangeDetectionStrategy.Eager, styles: [":host{display:block;width:100%;height:100%}.line-chart-widget{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:8px;box-sizing:border-box;overflow:hidden}.line-chart-widget.loading,.line-chart-widget.error{opacity:.7}.loading-indicator,.error-message{display:flex;align-items:center;justify-content:center;height:100%;width:100%}.loading-indicator span{font-size:1.5rem;color:var(--kendo-color-subtle, #6c757d)}.error-message span{color:var(--kendo-color-error, #dc3545);font-size:.875rem}.chart-container{width:100%;height:100%}kendo-chart{width:100%;height:100%}.chart-tooltip{padding:4px 8px}\n"] }]
14450
+ `, changeDetection: ChangeDetectionStrategy.Eager, styles: [":host{display:block;width:100%;height:100%}.line-chart-widget{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:8px;box-sizing:border-box;overflow:hidden}.data-count{position:absolute;top:2px;right:6px;z-index:2;font-size:.7rem;line-height:1;opacity:.55;color:var(--kendo-color-subtle, #6c757d);pointer-events:none;-webkit-user-select:none;user-select:none;font-variant-numeric:tabular-nums}.line-chart-widget.loading,.line-chart-widget.error{opacity:.7}.loading-indicator,.error-message{display:flex;align-items:center;justify-content:center;height:100%;width:100%}.loading-indicator span{font-size:1.5rem;color:var(--kendo-color-subtle, #6c757d)}.error-message span{color:var(--kendo-color-error, #dc3545);font-size:.875rem}.chart-container{width:100%;height:100%}kendo-chart{width:100%;height:100%}.chart-tooltip{padding:4px 8px}\n"] }]
14010
14451
  }], propDecorators: { config: [{
14011
14452
  type: Input
14012
14453
  }] } });
@@ -14026,6 +14467,7 @@ class LineChartConfigDialogComponent {
14026
14467
  initialQueryName;
14027
14468
  initialQueryFamily;
14028
14469
  initialIgnoreTimeFilter;
14470
+ initialEntitySelectorId;
14029
14471
  initialChartType;
14030
14472
  initialCategoryField;
14031
14473
  initialSeriesGroupField;
@@ -14046,6 +14488,12 @@ class LineChartConfigDialogComponent {
14046
14488
  nonNumericColumns = [];
14047
14489
  /** Per-widget opt-out of the MeshBoard time-filter → stream-data binding. */
14048
14490
  ignoreTimeFilter = false;
14491
+ /** Asset-scope binding: id of the entity selector whose selection scopes the stream-data query. */
14492
+ entitySelectorId;
14493
+ /** Entity selectors available on the current MeshBoard (for the scope picker). */
14494
+ get availableEntitySelectors() {
14495
+ return this.stateService.getEntitySelectors();
14496
+ }
14049
14497
  /** Family of the currently selected query — gates the time-filter opt-out toggle. */
14050
14498
  get selectedQueryFamily() {
14051
14499
  return queryFamily(this.selectedPersistentQuery?.ckTypeId) ?? this.initialQueryFamily ?? null;
@@ -14087,6 +14535,7 @@ class LineChartConfigDialogComponent {
14087
14535
  }));
14088
14536
  // Initialize form with initial values
14089
14537
  this.ignoreTimeFilter = this.initialIgnoreTimeFilter ?? false;
14538
+ this.entitySelectorId = this.initialEntitySelectorId;
14090
14539
  this.form.chartType = this.initialChartType ?? 'line';
14091
14540
  this.form.categoryField = this.initialCategoryField ?? '';
14092
14541
  this.form.seriesGroupField = this.initialSeriesGroupField ?? '';
@@ -14205,6 +14654,7 @@ class LineChartConfigDialogComponent {
14205
14654
  queryName: this.selectedPersistentQuery.name,
14206
14655
  queryFamily: family,
14207
14656
  ignoreTimeFilter: this.ignoreTimeFilter,
14657
+ entitySelectorId: this.entitySelectorId,
14208
14658
  chartType: this.form.chartType,
14209
14659
  categoryField: this.form.categoryField,
14210
14660
  seriesGroupField: this.form.seriesGroupField,
@@ -14228,7 +14678,7 @@ class LineChartConfigDialogComponent {
14228
14678
  this.windowRef.close();
14229
14679
  }
14230
14680
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: LineChartConfigDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
14231
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: LineChartConfigDialogComponent, isStandalone: true, selector: "mm-line-chart-config-dialog", inputs: { initialQueryRtId: "initialQueryRtId", initialQueryName: "initialQueryName", initialQueryFamily: "initialQueryFamily", initialIgnoreTimeFilter: "initialIgnoreTimeFilter", initialChartType: "initialChartType", initialCategoryField: "initialCategoryField", initialSeriesGroupField: "initialSeriesGroupField", initialValueField: "initialValueField", initialUnitField: "initialUnitField", initialShowLegend: "initialShowLegend", initialLegendPosition: "initialLegendPosition", initialShowMarkers: "initialShowMarkers", initialReferenceLines: "initialReferenceLines", initialFilters: "initialFilters" }, viewQueries: [{ propertyName: "querySelector", first: true, predicate: ["querySelector"], descendants: true }], ngImport: i0, template: `
14681
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: LineChartConfigDialogComponent, isStandalone: true, selector: "mm-line-chart-config-dialog", inputs: { initialQueryRtId: "initialQueryRtId", initialQueryName: "initialQueryName", initialQueryFamily: "initialQueryFamily", initialIgnoreTimeFilter: "initialIgnoreTimeFilter", initialEntitySelectorId: "initialEntitySelectorId", initialChartType: "initialChartType", initialCategoryField: "initialCategoryField", initialSeriesGroupField: "initialSeriesGroupField", initialValueField: "initialValueField", initialUnitField: "initialUnitField", initialShowLegend: "initialShowLegend", initialLegendPosition: "initialLegendPosition", initialShowMarkers: "initialShowMarkers", initialReferenceLines: "initialReferenceLines", initialFilters: "initialFilters" }, viewQueries: [{ propertyName: "querySelector", first: true, predicate: ["querySelector"], descendants: true }], ngImport: i0, template: `
14232
14682
  <div class="config-container">
14233
14683
 
14234
14684
  <div class="config-form" [class.loading]="isLoadingInitial">
@@ -14253,6 +14703,12 @@ class LineChartConfigDialogComponent {
14253
14703
  [family]="selectedQueryFamily"
14254
14704
  [(ignoreTimeFilter)]="ignoreTimeFilter">
14255
14705
  </mm-sd-time-filter-toggle>
14706
+
14707
+ <mm-entity-selector-scope-picker
14708
+ [family]="selectedQueryFamily"
14709
+ [selectors]="availableEntitySelectors"
14710
+ [(entitySelectorId)]="entitySelectorId">
14711
+ </mm-entity-selector-scope-picker>
14256
14712
  </div>
14257
14713
 
14258
14714
  <!-- Field Mapping Section -->
@@ -14436,7 +14892,7 @@ class LineChartConfigDialogComponent {
14436
14892
  </button>
14437
14893
  </div>
14438
14894
  </div>
14439
- `, isInline: true, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;gap:20px;flex:1;overflow-y:auto;padding:16px;position:relative}.config-form.loading{pointer-events:none}.config-section{padding:16px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.section-title{margin:0 0 16px;font-size:1rem;font-weight:600;color:var(--kendo-color-primary, #0d6efd)}.section-hint{margin:0 0 12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d)}.form-field{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}.form-field:last-child{margin-bottom:0}.form-field label{font-weight:600;font-size:.9rem;color:var(--kendo-color-on-app-surface, #212529)}.required{color:var(--kendo-color-error, #dc3545)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.chart-type-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}.radio-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.form-row{display:flex;gap:24px;align-items:center}.checkbox-field{flex-direction:row;align-items:center;margin-bottom:0}.checkbox-field label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.column-item{display:flex;justify-content:space-between;gap:16px}.column-path{font-weight:500}.column-type{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.form-section{margin-top:8px}.form-section h4{margin:0 0 4px;font-size:.95rem;font-weight:600}.reference-line-row{display:flex;gap:8px;align-items:center;margin-bottom:8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox]:not([ngNoCva])[formControlName],input[type=checkbox]:not([ngNoCva])[formControl],input[type=checkbox]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1$1.RadioControlValueAccessor, selector: "input[type=radio]:not([ngNoCva])[formControlName],input[type=radio]:not([ngNoCva])[formControl],input[type=radio]:not([ngNoCva])[ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i3.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "component", type: i3.NumericTextBoxComponent, selector: "kendo-numerictextbox", inputs: ["focusableId", "disabled", "readonly", "title", "autoCorrect", "format", "max", "min", "decimals", "placeholder", "step", "spinners", "rangeValidation", "tabindex", "tabIndex", "changeValueOnScroll", "selectOnFocus", "value", "maxlength", "size", "rounded", "fillMode", "inputAttributes"], outputs: ["valueChange", "focus", "blur", "inputFocus", "inputBlur"], exportAs: ["kendoNumericTextBox"] }, { kind: "directive", type: i3.CheckBoxDirective, selector: "input[kendoCheckBox]", inputs: ["size", "rounded"] }, { kind: "directive", type: i3.RadioButtonDirective, selector: "input[kendoRadioButton]", inputs: ["size"] }, { kind: "ngmodule", type: DropDownsModule }, { kind: "directive", type: i4.ItemTemplateDirective, selector: "[kendoDropDownListItemTemplate],[kendoComboBoxItemTemplate],[kendoAutoCompleteItemTemplate],[kendoMultiSelectItemTemplate]" }, { kind: "component", type: i4.ComboBoxComponent, selector: "kendo-combobox", inputs: ["icon", "svgIcon", "inputAttributes", "showStickyHeader", "focusableId", "allowCustom", "data", "value", "textField", "valueField", "valuePrimitive", "valueNormalizer", "placeholder", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "loading", "suggest", "clearButton", "disabled", "itemDisabled", "readonly", "tabindex", "tabIndex", "filterable", "virtual", "size", "rounded", "fillMode"], outputs: ["valueChange", "selectionChange", "filterChange", "open", "opened", "close", "closed", "focus", "blur", "inputFocus", "inputBlur", "escape"], exportAs: ["kendoComboBox"] }, { kind: "component", type: i4.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: FieldFilterEditorComponent, selector: "mm-field-filter-editor", inputs: ["availableAttributes", "ckTypeId", "hideNavigationProperties", "attributePaths", "enableVariables", "availableVariables", "filters"], outputs: ["filtersChange"] }, { kind: "component", type: QuerySelectorComponent, selector: "mm-query-selector", inputs: ["placeholder", "hint", "disabled", "acceptFamilies"], outputs: ["querySelected", "queriesLoaded"] }, { kind: "component", type: SdTimeFilterToggleComponent, selector: "mm-sd-time-filter-toggle", inputs: ["family", "ignoreTimeFilter"], outputs: ["ignoreTimeFilterChange"] }, { kind: "component", type: LoadingOverlayComponent, selector: "mm-loading-overlay", inputs: ["loading"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
14895
+ `, isInline: true, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;gap:20px;flex:1;overflow-y:auto;padding:16px;position:relative}.config-form.loading{pointer-events:none}.config-section{padding:16px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.section-title{margin:0 0 16px;font-size:1rem;font-weight:600;color:var(--kendo-color-primary, #0d6efd)}.section-hint{margin:0 0 12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d)}.form-field{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}.form-field:last-child{margin-bottom:0}.form-field label{font-weight:600;font-size:.9rem;color:var(--kendo-color-on-app-surface, #212529)}.required{color:var(--kendo-color-error, #dc3545)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.chart-type-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}.radio-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.form-row{display:flex;gap:24px;align-items:center}.checkbox-field{flex-direction:row;align-items:center;margin-bottom:0}.checkbox-field label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.column-item{display:flex;justify-content:space-between;gap:16px}.column-path{font-weight:500}.column-type{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.form-section{margin-top:8px}.form-section h4{margin:0 0 4px;font-size:.95rem;font-weight:600}.reference-line-row{display:flex;gap:8px;align-items:center;margin-bottom:8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox]:not([ngNoCva])[formControlName],input[type=checkbox]:not([ngNoCva])[formControl],input[type=checkbox]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1$1.RadioControlValueAccessor, selector: "input[type=radio]:not([ngNoCva])[formControlName],input[type=radio]:not([ngNoCva])[formControl],input[type=radio]:not([ngNoCva])[ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i3.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "component", type: i3.NumericTextBoxComponent, selector: "kendo-numerictextbox", inputs: ["focusableId", "disabled", "readonly", "title", "autoCorrect", "format", "max", "min", "decimals", "placeholder", "step", "spinners", "rangeValidation", "tabindex", "tabIndex", "changeValueOnScroll", "selectOnFocus", "value", "maxlength", "size", "rounded", "fillMode", "inputAttributes"], outputs: ["valueChange", "focus", "blur", "inputFocus", "inputBlur"], exportAs: ["kendoNumericTextBox"] }, { kind: "directive", type: i3.CheckBoxDirective, selector: "input[kendoCheckBox]", inputs: ["size", "rounded"] }, { kind: "directive", type: i3.RadioButtonDirective, selector: "input[kendoRadioButton]", inputs: ["size"] }, { kind: "ngmodule", type: DropDownsModule }, { kind: "directive", type: i4.ItemTemplateDirective, selector: "[kendoDropDownListItemTemplate],[kendoComboBoxItemTemplate],[kendoAutoCompleteItemTemplate],[kendoMultiSelectItemTemplate]" }, { kind: "component", type: i4.ComboBoxComponent, selector: "kendo-combobox", inputs: ["icon", "svgIcon", "inputAttributes", "showStickyHeader", "focusableId", "allowCustom", "data", "value", "textField", "valueField", "valuePrimitive", "valueNormalizer", "placeholder", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "loading", "suggest", "clearButton", "disabled", "itemDisabled", "readonly", "tabindex", "tabIndex", "filterable", "virtual", "size", "rounded", "fillMode"], outputs: ["valueChange", "selectionChange", "filterChange", "open", "opened", "close", "closed", "focus", "blur", "inputFocus", "inputBlur", "escape"], exportAs: ["kendoComboBox"] }, { kind: "component", type: i4.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: FieldFilterEditorComponent, selector: "mm-field-filter-editor", inputs: ["availableAttributes", "ckTypeId", "hideNavigationProperties", "attributePaths", "enableVariables", "availableVariables", "filters"], outputs: ["filtersChange"] }, { kind: "component", type: QuerySelectorComponent, selector: "mm-query-selector", inputs: ["placeholder", "hint", "disabled", "acceptFamilies"], outputs: ["querySelected", "queriesLoaded"] }, { kind: "component", type: SdTimeFilterToggleComponent, selector: "mm-sd-time-filter-toggle", inputs: ["family", "ignoreTimeFilter"], outputs: ["ignoreTimeFilterChange"] }, { kind: "component", type: EntitySelectorScopePickerComponent, selector: "mm-entity-selector-scope-picker", inputs: ["family", "entitySelectorId", "selectors"], outputs: ["entitySelectorIdChange"] }, { kind: "component", type: LoadingOverlayComponent, selector: "mm-loading-overlay", inputs: ["loading"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
14440
14896
  }
14441
14897
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: LineChartConfigDialogComponent, decorators: [{
14442
14898
  type: Component,
@@ -14450,6 +14906,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
14450
14906
  FieldFilterEditorComponent,
14451
14907
  QuerySelectorComponent,
14452
14908
  SdTimeFilterToggleComponent,
14909
+ EntitySelectorScopePickerComponent,
14453
14910
  LoadingOverlayComponent
14454
14911
  ], template: `
14455
14912
  <div class="config-container">
@@ -14476,6 +14933,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
14476
14933
  [family]="selectedQueryFamily"
14477
14934
  [(ignoreTimeFilter)]="ignoreTimeFilter">
14478
14935
  </mm-sd-time-filter-toggle>
14936
+
14937
+ <mm-entity-selector-scope-picker
14938
+ [family]="selectedQueryFamily"
14939
+ [selectors]="availableEntitySelectors"
14940
+ [(entitySelectorId)]="entitySelectorId">
14941
+ </mm-entity-selector-scope-picker>
14479
14942
  </div>
14480
14943
 
14481
14944
  <!-- Field Mapping Section -->
@@ -14671,6 +15134,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
14671
15134
  type: Input
14672
15135
  }], initialIgnoreTimeFilter: [{
14673
15136
  type: Input
15137
+ }], initialEntitySelectorId: [{
15138
+ type: Input
14674
15139
  }], initialChartType: [{
14675
15140
  type: Input
14676
15141
  }], initialCategoryField: [{
@@ -14866,7 +15331,12 @@ class HeatmapWidgetComponent {
14866
15331
  }
14867
15332
  buildStreamDataArgs() {
14868
15333
  const ds = this.config.dataSource;
14869
- return this.stateService.resolveStreamDataTimeArgs(ds.ignoreTimeFilter);
15334
+ const timeArgs = this.stateService.resolveStreamDataTimeArgs(ds.ignoreTimeFilter);
15335
+ const rtIds = this.stateService.resolveStreamDataRtIds(ds.entitySelectorId);
15336
+ if (!timeArgs && !rtIds) {
15337
+ return undefined;
15338
+ }
15339
+ return { ...timeArgs, rtIds };
14870
15340
  }
14871
15341
  /**
14872
15342
  * Processes query rows into heatmap data.
@@ -15219,6 +15689,7 @@ class HeatmapConfigDialogComponent {
15219
15689
  initialQueryName;
15220
15690
  initialQueryFamily;
15221
15691
  initialIgnoreTimeFilter;
15692
+ initialEntitySelectorId;
15222
15693
  initialDateField;
15223
15694
  initialDateEndField;
15224
15695
  initialValueField;
@@ -15236,6 +15707,7 @@ class HeatmapConfigDialogComponent {
15236
15707
  // Persistent Query
15237
15708
  selectedPersistentQuery = null;
15238
15709
  ignoreTimeFilter = false;
15710
+ entitySelectorId;
15239
15711
  queryColumns = [];
15240
15712
  numericColumns = [];
15241
15713
  dateTimeColumns = [];
@@ -15284,6 +15756,10 @@ class HeatmapConfigDialogComponent {
15284
15756
  get isValid() {
15285
15757
  return this.selectedPersistentQuery !== null && this.form.dateField !== '';
15286
15758
  }
15759
+ /** Entity selectors available on the current MeshBoard (for the scope picker). */
15760
+ get availableEntitySelectors() {
15761
+ return this.stateService.getEntitySelectors();
15762
+ }
15287
15763
  get selectedQueryFamily() {
15288
15764
  return queryFamily(this.selectedPersistentQuery?.ckTypeId) ?? this.initialQueryFamily ?? null;
15289
15765
  }
@@ -15306,6 +15782,7 @@ class HeatmapConfigDialogComponent {
15306
15782
  this.form.compactNumbers = this.initialCompactNumbers ?? false;
15307
15783
  this.form.valueMultiplier = this.initialValueMultiplier ?? 1;
15308
15784
  this.ignoreTimeFilter = this.initialIgnoreTimeFilter ?? false;
15785
+ this.entitySelectorId = this.initialEntitySelectorId;
15309
15786
  // Initialize filters
15310
15787
  if (this.initialFilters && this.initialFilters.length > 0) {
15311
15788
  this.filters = this.initialFilters.map((f, index) => ({
@@ -15423,6 +15900,7 @@ class HeatmapConfigDialogComponent {
15423
15900
  queryName: this.selectedPersistentQuery.name,
15424
15901
  queryFamily: family,
15425
15902
  ignoreTimeFilter: this.ignoreTimeFilter,
15903
+ entitySelectorId: this.entitySelectorId,
15426
15904
  dateField: this.form.dateField,
15427
15905
  dateEndField: this.form.dateEndField || undefined,
15428
15906
  valueField: this.form.valueField || undefined,
@@ -15441,7 +15919,7 @@ class HeatmapConfigDialogComponent {
15441
15919
  this.windowRef.close();
15442
15920
  }
15443
15921
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: HeatmapConfigDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
15444
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: HeatmapConfigDialogComponent, isStandalone: true, selector: "mm-heatmap-config-dialog", inputs: { initialQueryRtId: "initialQueryRtId", initialQueryName: "initialQueryName", initialQueryFamily: "initialQueryFamily", initialIgnoreTimeFilter: "initialIgnoreTimeFilter", initialDateField: "initialDateField", initialDateEndField: "initialDateEndField", initialValueField: "initialValueField", initialAggregation: "initialAggregation", initialColorScheme: "initialColorScheme", initialShowLegend: "initialShowLegend", initialLegendPosition: "initialLegendPosition", initialDecimalPlaces: "initialDecimalPlaces", initialCompactNumbers: "initialCompactNumbers", initialValueMultiplier: "initialValueMultiplier", initialFilters: "initialFilters" }, viewQueries: [{ propertyName: "querySelector", first: true, predicate: ["querySelector"], descendants: true }], ngImport: i0, template: `
15922
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: HeatmapConfigDialogComponent, isStandalone: true, selector: "mm-heatmap-config-dialog", inputs: { initialQueryRtId: "initialQueryRtId", initialQueryName: "initialQueryName", initialQueryFamily: "initialQueryFamily", initialIgnoreTimeFilter: "initialIgnoreTimeFilter", initialEntitySelectorId: "initialEntitySelectorId", initialDateField: "initialDateField", initialDateEndField: "initialDateEndField", initialValueField: "initialValueField", initialAggregation: "initialAggregation", initialColorScheme: "initialColorScheme", initialShowLegend: "initialShowLegend", initialLegendPosition: "initialLegendPosition", initialDecimalPlaces: "initialDecimalPlaces", initialCompactNumbers: "initialCompactNumbers", initialValueMultiplier: "initialValueMultiplier", initialFilters: "initialFilters" }, viewQueries: [{ propertyName: "querySelector", first: true, predicate: ["querySelector"], descendants: true }], ngImport: i0, template: `
15445
15923
  <div class="config-container">
15446
15924
 
15447
15925
  <div class="config-form" [class.loading]="isLoadingInitial">
@@ -15466,6 +15944,12 @@ class HeatmapConfigDialogComponent {
15466
15944
  [family]="selectedQueryFamily"
15467
15945
  [(ignoreTimeFilter)]="ignoreTimeFilter">
15468
15946
  </mm-sd-time-filter-toggle>
15947
+
15948
+ <mm-entity-selector-scope-picker
15949
+ [family]="selectedQueryFamily"
15950
+ [selectors]="availableEntitySelectors"
15951
+ [(entitySelectorId)]="entitySelectorId">
15952
+ </mm-entity-selector-scope-picker>
15469
15953
  </div>
15470
15954
 
15471
15955
  <!-- Field Mapping Section -->
@@ -15655,7 +16139,7 @@ class HeatmapConfigDialogComponent {
15655
16139
  </button>
15656
16140
  </div>
15657
16141
  </div>
15658
- `, isInline: true, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;gap:20px;flex:1;overflow-y:auto;padding:16px;position:relative}.config-form.loading{pointer-events:none}.config-section{padding:16px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.section-title{margin:0 0 16px;font-size:1rem;font-weight:600;color:var(--kendo-color-primary, #0d6efd)}.section-hint{margin:0 0 12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d)}.form-field{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}.form-field:last-child{margin-bottom:0}.form-field label{font-weight:600;font-size:.9rem;color:var(--kendo-color-on-app-surface, #212529)}.required{color:var(--kendo-color-error, #dc3545)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.color-scheme-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}.radio-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.color-scheme-preview{display:flex;align-items:center;gap:6px}.color-swatch{display:inline-block;width:16px;height:16px;border-radius:3px;border:1px solid var(--kendo-color-border, #dee2e6)}.form-row{display:flex;gap:24px}.checkbox-field{flex-direction:row;align-items:center}.checkbox-field label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.column-item{display:flex;justify-content:space-between;gap:16px}.column-path{font-weight:500}.column-type{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox]:not([ngNoCva])[formControlName],input[type=checkbox]:not([ngNoCva])[formControl],input[type=checkbox]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1$1.RadioControlValueAccessor, selector: "input[type=radio]:not([ngNoCva])[formControlName],input[type=radio]:not([ngNoCva])[formControl],input[type=radio]:not([ngNoCva])[ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i3.NumericTextBoxComponent, selector: "kendo-numerictextbox", inputs: ["focusableId", "disabled", "readonly", "title", "autoCorrect", "format", "max", "min", "decimals", "placeholder", "step", "spinners", "rangeValidation", "tabindex", "tabIndex", "changeValueOnScroll", "selectOnFocus", "value", "maxlength", "size", "rounded", "fillMode", "inputAttributes"], outputs: ["valueChange", "focus", "blur", "inputFocus", "inputBlur"], exportAs: ["kendoNumericTextBox"] }, { kind: "directive", type: i3.CheckBoxDirective, selector: "input[kendoCheckBox]", inputs: ["size", "rounded"] }, { kind: "directive", type: i3.RadioButtonDirective, selector: "input[kendoRadioButton]", inputs: ["size"] }, { kind: "ngmodule", type: DropDownsModule }, { kind: "directive", type: i4.ItemTemplateDirective, selector: "[kendoDropDownListItemTemplate],[kendoComboBoxItemTemplate],[kendoAutoCompleteItemTemplate],[kendoMultiSelectItemTemplate]" }, { kind: "component", type: i4.ComboBoxComponent, selector: "kendo-combobox", inputs: ["icon", "svgIcon", "inputAttributes", "showStickyHeader", "focusableId", "allowCustom", "data", "value", "textField", "valueField", "valuePrimitive", "valueNormalizer", "placeholder", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "loading", "suggest", "clearButton", "disabled", "itemDisabled", "readonly", "tabindex", "tabIndex", "filterable", "virtual", "size", "rounded", "fillMode"], outputs: ["valueChange", "selectionChange", "filterChange", "open", "opened", "close", "closed", "focus", "blur", "inputFocus", "inputBlur", "escape"], exportAs: ["kendoComboBox"] }, { kind: "component", type: i4.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: FieldFilterEditorComponent, selector: "mm-field-filter-editor", inputs: ["availableAttributes", "ckTypeId", "hideNavigationProperties", "attributePaths", "enableVariables", "availableVariables", "filters"], outputs: ["filtersChange"] }, { kind: "component", type: QuerySelectorComponent, selector: "mm-query-selector", inputs: ["placeholder", "hint", "disabled", "acceptFamilies"], outputs: ["querySelected", "queriesLoaded"] }, { kind: "component", type: SdTimeFilterToggleComponent, selector: "mm-sd-time-filter-toggle", inputs: ["family", "ignoreTimeFilter"], outputs: ["ignoreTimeFilterChange"] }, { kind: "component", type: LoadingOverlayComponent, selector: "mm-loading-overlay", inputs: ["loading"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
16142
+ `, isInline: true, styles: [":host{display:block;height:100%}.config-container{display:flex;flex-direction:column;height:100%}.action-bar{display:flex;justify-content:flex-end;gap:8px;padding:8px 16px;border-top:1px solid var(--kendo-color-border, #dee2e6)}.config-form{display:flex;flex-direction:column;gap:20px;flex:1;overflow-y:auto;padding:16px;position:relative}.config-form.loading{pointer-events:none}.config-section{padding:16px;background:var(--kendo-color-surface-alt, #f8f9fa);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.section-title{margin:0 0 16px;font-size:1rem;font-weight:600;color:var(--kendo-color-primary, #0d6efd)}.section-hint{margin:0 0 12px;font-size:.85rem;color:var(--kendo-color-subtle, #6c757d)}.form-field{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}.form-field:last-child{margin-bottom:0}.form-field label{font-weight:600;font-size:.9rem;color:var(--kendo-color-on-app-surface, #212529)}.required{color:var(--kendo-color-error, #dc3545)}.field-hint{margin:0;font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}.color-scheme-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}.radio-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.color-scheme-preview{display:flex;align-items:center;gap:6px}.color-swatch{display:inline-block;width:16px;height:16px;border-radius:3px;border:1px solid var(--kendo-color-border, #dee2e6)}.form-row{display:flex;gap:24px}.checkbox-field{flex-direction:row;align-items:center}.checkbox-field label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:400}.column-item{display:flex;justify-content:space-between;gap:16px}.column-path{font-weight:500}.column-type{font-size:.8rem;color:var(--kendo-color-subtle, #6c757d)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox]:not([ngNoCva])[formControlName],input[type=checkbox]:not([ngNoCva])[formControl],input[type=checkbox]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1$1.RadioControlValueAccessor, selector: "input[type=radio]:not([ngNoCva])[formControlName],input[type=radio]:not([ngNoCva])[formControl],input[type=radio]:not([ngNoCva])[ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonsModule }, { kind: "component", type: i2.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i3.NumericTextBoxComponent, selector: "kendo-numerictextbox", inputs: ["focusableId", "disabled", "readonly", "title", "autoCorrect", "format", "max", "min", "decimals", "placeholder", "step", "spinners", "rangeValidation", "tabindex", "tabIndex", "changeValueOnScroll", "selectOnFocus", "value", "maxlength", "size", "rounded", "fillMode", "inputAttributes"], outputs: ["valueChange", "focus", "blur", "inputFocus", "inputBlur"], exportAs: ["kendoNumericTextBox"] }, { kind: "directive", type: i3.CheckBoxDirective, selector: "input[kendoCheckBox]", inputs: ["size", "rounded"] }, { kind: "directive", type: i3.RadioButtonDirective, selector: "input[kendoRadioButton]", inputs: ["size"] }, { kind: "ngmodule", type: DropDownsModule }, { kind: "directive", type: i4.ItemTemplateDirective, selector: "[kendoDropDownListItemTemplate],[kendoComboBoxItemTemplate],[kendoAutoCompleteItemTemplate],[kendoMultiSelectItemTemplate]" }, { kind: "component", type: i4.ComboBoxComponent, selector: "kendo-combobox", inputs: ["icon", "svgIcon", "inputAttributes", "showStickyHeader", "focusableId", "allowCustom", "data", "value", "textField", "valueField", "valuePrimitive", "valueNormalizer", "placeholder", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "loading", "suggest", "clearButton", "disabled", "itemDisabled", "readonly", "tabindex", "tabIndex", "filterable", "virtual", "size", "rounded", "fillMode"], outputs: ["valueChange", "selectionChange", "filterChange", "open", "opened", "close", "closed", "focus", "blur", "inputFocus", "inputBlur", "escape"], exportAs: ["kendoComboBox"] }, { kind: "component", type: i4.DropDownListComponent, selector: "kendo-dropdownlist", inputs: ["customIconClass", "showStickyHeader", "icon", "svgIcon", "loading", "data", "value", "textField", "valueField", "adaptiveMode", "adaptiveTitle", "adaptiveSubtitle", "popupSettings", "listHeight", "defaultItem", "disabled", "itemDisabled", "readonly", "filterable", "virtual", "ignoreCase", "delay", "valuePrimitive", "tabindex", "tabIndex", "size", "rounded", "fillMode", "leftRightArrowsNavigation", "id"], outputs: ["valueChange", "filterChange", "selectionChange", "open", "opened", "close", "closed", "focus", "blur"], exportAs: ["kendoDropDownList"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: FieldFilterEditorComponent, selector: "mm-field-filter-editor", inputs: ["availableAttributes", "ckTypeId", "hideNavigationProperties", "attributePaths", "enableVariables", "availableVariables", "filters"], outputs: ["filtersChange"] }, { kind: "component", type: QuerySelectorComponent, selector: "mm-query-selector", inputs: ["placeholder", "hint", "disabled", "acceptFamilies"], outputs: ["querySelected", "queriesLoaded"] }, { kind: "component", type: SdTimeFilterToggleComponent, selector: "mm-sd-time-filter-toggle", inputs: ["family", "ignoreTimeFilter"], outputs: ["ignoreTimeFilterChange"] }, { kind: "component", type: EntitySelectorScopePickerComponent, selector: "mm-entity-selector-scope-picker", inputs: ["family", "entitySelectorId", "selectors"], outputs: ["entitySelectorIdChange"] }, { kind: "component", type: LoadingOverlayComponent, selector: "mm-loading-overlay", inputs: ["loading"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
15659
16143
  }
15660
16144
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: HeatmapConfigDialogComponent, decorators: [{
15661
16145
  type: Component,
@@ -15669,6 +16153,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
15669
16153
  FieldFilterEditorComponent,
15670
16154
  QuerySelectorComponent,
15671
16155
  SdTimeFilterToggleComponent,
16156
+ EntitySelectorScopePickerComponent,
15672
16157
  LoadingOverlayComponent
15673
16158
  ], template: `
15674
16159
  <div class="config-container">
@@ -15695,6 +16180,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
15695
16180
  [family]="selectedQueryFamily"
15696
16181
  [(ignoreTimeFilter)]="ignoreTimeFilter">
15697
16182
  </mm-sd-time-filter-toggle>
16183
+
16184
+ <mm-entity-selector-scope-picker
16185
+ [family]="selectedQueryFamily"
16186
+ [selectors]="availableEntitySelectors"
16187
+ [(entitySelectorId)]="entitySelectorId">
16188
+ </mm-entity-selector-scope-picker>
15698
16189
  </div>
15699
16190
 
15700
16191
  <!-- Field Mapping Section -->
@@ -15896,6 +16387,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
15896
16387
  type: Input
15897
16388
  }], initialIgnoreTimeFilter: [{
15898
16389
  type: Input
16390
+ }], initialEntitySelectorId: [{
16391
+ type: Input
15899
16392
  }], initialDateField: [{
15900
16393
  type: Input
15901
16394
  }], initialDateEndField: [{
@@ -25958,6 +26451,9 @@ function buildDataSourceFromPersisted(data, config) {
25958
26451
  if (typeof config['ignoreTimeFilter'] === 'boolean') {
25959
26452
  persisted.ignoreTimeFilter = config['ignoreTimeFilter'];
25960
26453
  }
26454
+ if (typeof config['entitySelectorId'] === 'string' && config['entitySelectorId']) {
26455
+ persisted.entitySelectorId = config['entitySelectorId'];
26456
+ }
25961
26457
  return persisted;
25962
26458
  }
25963
26459
  if (data.dataSourceType === 'static') {
@@ -26080,6 +26576,7 @@ function registerDefaultWidgets(registry) {
26080
26576
  initialQueryName: isPersistentQuery ? kpiWidget.dataSource.queryName : undefined,
26081
26577
  initialQueryFamily: isPersistentQuery ? kpiWidget.dataSource.queryFamily : undefined,
26082
26578
  initialIgnoreTimeFilter: isPersistentQuery ? kpiWidget.dataSource.ignoreTimeFilter : undefined,
26579
+ initialEntitySelectorId: isPersistentQuery ? kpiWidget.dataSource.entitySelectorId : undefined,
26083
26580
  initialQueryMode: kpiWidget.queryMode,
26084
26581
  initialQueryValueField: kpiWidget.queryValueField,
26085
26582
  initialQueryCategoryField: kpiWidget.queryCategoryField,
@@ -26126,7 +26623,8 @@ function registerDefaultWidgets(registry) {
26126
26623
  queryRtId: result.queryRtId,
26127
26624
  queryName: result.queryName,
26128
26625
  queryFamily: result.queryFamily,
26129
- ignoreTimeFilter: result.ignoreTimeFilter
26626
+ ignoreTimeFilter: result.ignoreTimeFilter,
26627
+ entitySelectorId: result.entitySelectorId
26130
26628
  };
26131
26629
  return {
26132
26630
  ...widget,
@@ -26203,6 +26701,9 @@ function registerDefaultWidgets(registry) {
26203
26701
  }),
26204
26702
  ...(widget.dataSource.ignoreTimeFilter && {
26205
26703
  ignoreTimeFilter: true
26704
+ }),
26705
+ ...(widget.dataSource.entitySelectorId && {
26706
+ entitySelectorId: widget.dataSource.entitySelectorId
26206
26707
  })
26207
26708
  })
26208
26709
  }
@@ -26369,7 +26870,8 @@ function registerDefaultWidgets(registry) {
26369
26870
  initialQueryRtId: isPersistentQuery ? dataSource.queryRtId : undefined,
26370
26871
  initialQueryName: isPersistentQuery ? dataSource.queryName : undefined,
26371
26872
  initialQueryFamily: isPersistentQuery ? dataSource.queryFamily : undefined,
26372
- initialIgnoreTimeFilter: isPersistentQuery ? dataSource.ignoreTimeFilter : undefined
26873
+ initialIgnoreTimeFilter: isPersistentQuery ? dataSource.ignoreTimeFilter : undefined,
26874
+ initialEntitySelectorId: isPersistentQuery ? dataSource.entitySelectorId : undefined
26373
26875
  };
26374
26876
  },
26375
26877
  applyConfigResult: (widget, result) => {
@@ -26380,7 +26882,8 @@ function registerDefaultWidgets(registry) {
26380
26882
  queryRtId: result.queryRtId,
26381
26883
  queryName: result.queryName,
26382
26884
  queryFamily: result.queryFamily,
26383
- ignoreTimeFilter: result.ignoreTimeFilter
26885
+ ignoreTimeFilter: result.ignoreTimeFilter,
26886
+ entitySelectorId: result.entitySelectorId
26384
26887
  };
26385
26888
  // Convert filters from DTO to widget format
26386
26889
  const filters = result.filters?.map(f => ({
@@ -26451,7 +26954,8 @@ function registerDefaultWidgets(registry) {
26451
26954
  queryName: queryDataSource.queryName,
26452
26955
  queryRtId: queryDataSource.queryRtId,
26453
26956
  ...(queryDataSource.queryFamily && { queryFamily: queryDataSource.queryFamily }),
26454
- ...(queryDataSource.ignoreTimeFilter && { ignoreTimeFilter: true })
26957
+ ...(queryDataSource.ignoreTimeFilter && { ignoreTimeFilter: true }),
26958
+ ...(queryDataSource.entitySelectorId && { entitySelectorId: queryDataSource.entitySelectorId })
26455
26959
  })
26456
26960
  }
26457
26961
  };
@@ -26498,6 +27002,7 @@ function registerDefaultWidgets(registry) {
26498
27002
  initialQueryName: isPersistentQuery ? gaugeWidget.dataSource.queryName : undefined,
26499
27003
  initialQueryFamily: isPersistentQuery ? gaugeWidget.dataSource.queryFamily : undefined,
26500
27004
  initialIgnoreTimeFilter: isPersistentQuery ? gaugeWidget.dataSource.ignoreTimeFilter : undefined,
27005
+ initialEntitySelectorId: isPersistentQuery ? gaugeWidget.dataSource.entitySelectorId : undefined,
26501
27006
  initialQueryMode: gaugeWidget.queryMode,
26502
27007
  initialQueryValueField: gaugeWidget.queryValueField,
26503
27008
  initialQueryCategoryField: gaugeWidget.queryCategoryField,
@@ -26529,7 +27034,8 @@ function registerDefaultWidgets(registry) {
26529
27034
  queryRtId: result.queryRtId,
26530
27035
  queryName: result.queryName,
26531
27036
  queryFamily: result.queryFamily,
26532
- ignoreTimeFilter: result.ignoreTimeFilter
27037
+ ignoreTimeFilter: result.ignoreTimeFilter,
27038
+ entitySelectorId: result.entitySelectorId
26533
27039
  };
26534
27040
  return {
26535
27041
  ...widget,
@@ -26617,6 +27123,9 @@ function registerDefaultWidgets(registry) {
26617
27123
  }),
26618
27124
  ...(widget.dataSource.ignoreTimeFilter && {
26619
27125
  ignoreTimeFilter: true
27126
+ }),
27127
+ ...(widget.dataSource.entitySelectorId && {
27128
+ entitySelectorId: widget.dataSource.entitySelectorId
26620
27129
  })
26621
27130
  })
26622
27131
  }
@@ -26688,6 +27197,7 @@ function registerDefaultWidgets(registry) {
26688
27197
  initialQueryName: isPersistentQuery ? dataSource.queryName : undefined,
26689
27198
  initialQueryFamily: isPersistentQuery ? dataSource.queryFamily : undefined,
26690
27199
  initialIgnoreTimeFilter: isPersistentQuery ? dataSource.ignoreTimeFilter : undefined,
27200
+ initialEntitySelectorId: isPersistentQuery ? dataSource.entitySelectorId : undefined,
26691
27201
  initialCkQueryTarget: isCkQuery ? dataSource.queryTarget : undefined,
26692
27202
  initialCkGroupBy: isCkQuery ? dataSource.groupBy : undefined,
26693
27203
  initialChartType: pieWidget.chartType,
@@ -26722,7 +27232,8 @@ function registerDefaultWidgets(registry) {
26722
27232
  queryRtId: result.queryRtId ?? '',
26723
27233
  queryName: result.queryName,
26724
27234
  queryFamily: result.queryFamily,
26725
- ignoreTimeFilter: result.ignoreTimeFilter
27235
+ ignoreTimeFilter: result.ignoreTimeFilter,
27236
+ entitySelectorId: result.entitySelectorId
26726
27237
  };
26727
27238
  }
26728
27239
  return {
@@ -26790,6 +27301,9 @@ function registerDefaultWidgets(registry) {
26790
27301
  ...(dataSource.ignoreTimeFilter && {
26791
27302
  ignoreTimeFilter: true
26792
27303
  }),
27304
+ ...(dataSource.entitySelectorId && {
27305
+ entitySelectorId: dataSource.entitySelectorId
27306
+ }),
26793
27307
  filters: widget.filters
26794
27308
  }
26795
27309
  };
@@ -26853,6 +27367,7 @@ function registerDefaultWidgets(registry) {
26853
27367
  initialQueryName: isPersistentQuery ? dataSource.queryName : undefined,
26854
27368
  initialQueryFamily: isPersistentQuery ? dataSource.queryFamily : undefined,
26855
27369
  initialIgnoreTimeFilter: isPersistentQuery ? dataSource.ignoreTimeFilter : undefined,
27370
+ initialEntitySelectorId: isPersistentQuery ? dataSource.entitySelectorId : undefined,
26856
27371
  initialChartType: barWidget.chartType,
26857
27372
  initialCategoryField: barWidget.categoryField,
26858
27373
  initialSeries: barWidget.series,
@@ -26872,7 +27387,8 @@ function registerDefaultWidgets(registry) {
26872
27387
  queryRtId: result.queryRtId,
26873
27388
  queryName: result.queryName,
26874
27389
  queryFamily: result.queryFamily,
26875
- ignoreTimeFilter: result.ignoreTimeFilter
27390
+ ignoreTimeFilter: result.ignoreTimeFilter,
27391
+ entitySelectorId: result.entitySelectorId
26876
27392
  };
26877
27393
  // Convert filters from DTO to widget format
26878
27394
  const filters = result.filters?.map(f => ({
@@ -26934,6 +27450,9 @@ function registerDefaultWidgets(registry) {
26934
27450
  ...(widget.dataSource.ignoreTimeFilter && {
26935
27451
  ignoreTimeFilter: true
26936
27452
  }),
27453
+ ...(widget.dataSource.entitySelectorId && {
27454
+ entitySelectorId: widget.dataSource.entitySelectorId
27455
+ }),
26937
27456
  filters: widget.filters
26938
27457
  }
26939
27458
  }),
@@ -26980,6 +27499,7 @@ function registerDefaultWidgets(registry) {
26980
27499
  initialQueryName: isPersistentQuery ? dataSource.queryName : undefined,
26981
27500
  initialQueryFamily: isPersistentQuery ? dataSource.queryFamily : undefined,
26982
27501
  initialIgnoreTimeFilter: isPersistentQuery ? dataSource.ignoreTimeFilter : undefined,
27502
+ initialEntitySelectorId: isPersistentQuery ? dataSource.entitySelectorId : undefined,
26983
27503
  initialChartType: lineWidget.chartType,
26984
27504
  initialCategoryField: lineWidget.categoryField,
26985
27505
  initialSeriesGroupField: lineWidget.seriesGroupField,
@@ -26998,7 +27518,8 @@ function registerDefaultWidgets(registry) {
26998
27518
  queryRtId: result.queryRtId,
26999
27519
  queryName: result.queryName,
27000
27520
  queryFamily: result.queryFamily,
27001
- ignoreTimeFilter: result.ignoreTimeFilter
27521
+ ignoreTimeFilter: result.ignoreTimeFilter,
27522
+ entitySelectorId: result.entitySelectorId
27002
27523
  };
27003
27524
  const filters = result.filters?.map(f => ({
27004
27525
  attributePath: f.attributePath,
@@ -27055,6 +27576,9 @@ function registerDefaultWidgets(registry) {
27055
27576
  ...(widget.dataSource.ignoreTimeFilter && {
27056
27577
  ignoreTimeFilter: true
27057
27578
  }),
27579
+ ...(widget.dataSource.entitySelectorId && {
27580
+ entitySelectorId: widget.dataSource.entitySelectorId
27581
+ }),
27058
27582
  filters: widget.filters
27059
27583
  }
27060
27584
  }),
@@ -27529,6 +28053,7 @@ function registerDefaultWidgets(registry) {
27529
28053
  initialQueryName: isPersistentQuery ? dataSource.queryName : undefined,
27530
28054
  initialQueryFamily: isPersistentQuery ? dataSource.queryFamily : undefined,
27531
28055
  initialIgnoreTimeFilter: isPersistentQuery ? dataSource.ignoreTimeFilter : undefined,
28056
+ initialEntitySelectorId: isPersistentQuery ? dataSource.entitySelectorId : undefined,
27532
28057
  initialDateField: heatmapWidget.dateField,
27533
28058
  initialDateEndField: heatmapWidget.dateEndField,
27534
28059
  initialValueField: heatmapWidget.valueField,
@@ -27548,7 +28073,8 @@ function registerDefaultWidgets(registry) {
27548
28073
  queryRtId: result.queryRtId,
27549
28074
  queryName: result.queryName,
27550
28075
  queryFamily: result.queryFamily,
27551
- ignoreTimeFilter: result.ignoreTimeFilter
28076
+ ignoreTimeFilter: result.ignoreTimeFilter,
28077
+ entitySelectorId: result.entitySelectorId
27552
28078
  };
27553
28079
  const filters = result.filters?.map(f => ({
27554
28080
  attributePath: f.attributePath,
@@ -27607,6 +28133,9 @@ function registerDefaultWidgets(registry) {
27607
28133
  ...(widget.dataSource.ignoreTimeFilter && {
27608
28134
  ignoreTimeFilter: true
27609
28135
  }),
28136
+ ...(widget.dataSource.entitySelectorId && {
28137
+ entitySelectorId: widget.dataSource.entitySelectorId
28138
+ }),
27610
28139
  filters: widget.filters
27611
28140
  }
27612
28141
  }),
@@ -28061,6 +28590,15 @@ class EntitySelectorEditorComponent {
28061
28590
  editMappings = [];
28062
28591
  editShowInToolbar = true;
28063
28592
  editDefaultRtId = '';
28593
+ // Child scope (stream-data): optional one-hop traversal resolving the picked
28594
+ // entity to the child source rtIds a stream-data widget scopes by.
28595
+ editChildScopeTargetCkTypeId = '';
28596
+ editChildScopeTargetCkTypeItem = null;
28597
+ editChildScopeRoleId = '';
28598
+ // Parent → children via System/ParentChild is reached INBOUND (the child holds
28599
+ // the association to its parent), so 'in' is the correct default for a
28600
+ // childScope that resolves a picked parent to its children.
28601
+ editChildScopeDirection = 'in';
28064
28602
  // Data sources for the default entity picker (created when CK type is set)
28065
28603
  defaultEntityDataSource = null;
28066
28604
  defaultEntityDialogDataSource = null;
@@ -28076,6 +28614,10 @@ class EntitySelectorEditorComponent {
28076
28614
  this.editMappings = [];
28077
28615
  this.editShowInToolbar = true;
28078
28616
  this.editDefaultRtId = '';
28617
+ this.editChildScopeTargetCkTypeId = '';
28618
+ this.editChildScopeTargetCkTypeItem = null;
28619
+ this.editChildScopeRoleId = 'System/ParentChild';
28620
+ this.editChildScopeDirection = 'in';
28079
28621
  this.defaultEntityDataSource = null;
28080
28622
  this.defaultEntityDialogDataSource = null;
28081
28623
  this.isEditing = true;
@@ -28100,6 +28642,17 @@ class EntitySelectorEditorComponent {
28100
28642
  this.editMappings = [...selector.attributeMappings];
28101
28643
  this.editShowInToolbar = selector.showInToolbar !== false;
28102
28644
  this.editDefaultRtId = selector.defaultRtId ?? '';
28645
+ this.editChildScopeTargetCkTypeId = selector.childScope?.targetCkTypeId ?? '';
28646
+ this.editChildScopeTargetCkTypeItem = selector.childScope?.targetCkTypeId
28647
+ ? {
28648
+ fullName: selector.childScope.targetCkTypeId,
28649
+ rtCkTypeId: selector.childScope.targetCkTypeId,
28650
+ isAbstract: false,
28651
+ isFinal: false
28652
+ }
28653
+ : null;
28654
+ this.editChildScopeRoleId = selector.childScope?.roleId ?? 'System/ParentChild';
28655
+ this.editChildScopeDirection = selector.childScope?.direction ?? 'in';
28103
28656
  this.updateDefaultEntityDataSources();
28104
28657
  this.isEditing = true;
28105
28658
  this.editingStateChange.emit(true);
@@ -28152,6 +28705,20 @@ class EntitySelectorEditorComponent {
28152
28705
  };
28153
28706
  });
28154
28707
  }
28708
+ /**
28709
+ * Handles child-scope target CK type selection.
28710
+ */
28711
+ onChildScopeCkTypeSelected(ckType) {
28712
+ this.editChildScopeTargetCkTypeId = ckType.rtCkTypeId;
28713
+ this.editChildScopeTargetCkTypeItem = ckType;
28714
+ }
28715
+ /**
28716
+ * Handles child-scope target CK type cleared.
28717
+ */
28718
+ onChildScopeCkTypeCleared() {
28719
+ this.editChildScopeTargetCkTypeId = '';
28720
+ this.editChildScopeTargetCkTypeItem = null;
28721
+ }
28155
28722
  /**
28156
28723
  * Handles default entity selection from the entity picker.
28157
28724
  */
@@ -28170,13 +28737,23 @@ class EntitySelectorEditorComponent {
28170
28737
  saveEdit() {
28171
28738
  if (!this.isEditValid())
28172
28739
  return;
28740
+ // Only persist a childScope when both the target type and role are set;
28741
+ // otherwise leave it undefined (the picked entity itself is the scope).
28742
+ const childScope = this.editChildScopeTargetCkTypeId && this.editChildScopeRoleId.trim()
28743
+ ? {
28744
+ targetCkTypeId: this.editChildScopeTargetCkTypeId,
28745
+ roleId: this.editChildScopeRoleId.trim(),
28746
+ direction: this.editChildScopeDirection
28747
+ }
28748
+ : undefined;
28173
28749
  const selector = {
28174
28750
  id: this.editId,
28175
28751
  label: this.editLabel.trim(),
28176
28752
  ckTypeId: this.editCkTypeId,
28177
28753
  attributeMappings: this.editMappings,
28178
28754
  showInToolbar: this.editShowInToolbar,
28179
- defaultRtId: this.editDefaultRtId.trim() || undefined
28755
+ defaultRtId: this.editDefaultRtId.trim() || undefined,
28756
+ childScope
28180
28757
  };
28181
28758
  if (this.editingIndex !== null) {
28182
28759
  this.entitySelectors = this.entitySelectors.map((es, i) => i === this.editingIndex ? selector : es);
@@ -28296,7 +28873,7 @@ class EntitySelectorEditorComponent {
28296
28873
  this.entitySelectorsChange.emit([...this.entitySelectors]);
28297
28874
  }
28298
28875
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: EntitySelectorEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
28299
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: EntitySelectorEditorComponent, isStandalone: true, selector: "mm-entity-selector-editor", inputs: { entitySelectors: "entitySelectors", existingVariableNames: "existingVariableNames" }, outputs: { entitySelectorsChange: "entitySelectorsChange", editingStateChange: "editingStateChange" }, ngImport: i0, template: "<div class=\"entity-selector-editor\">\n @if (!isEditing) {\n <!-- Selector List -->\n <div class=\"selector-list\">\n @for (selector of entitySelectors; track selector.id; let i = $index) {\n <div class=\"selector-item\">\n <div class=\"selector-info\">\n <span class=\"selector-label\">{{ selector.label }}</span>\n <span class=\"selector-mode\">{{ getModeLabel(selector) }}</span>\n <span class=\"selector-ck-type\">{{ selector.ckTypeId }}</span>\n <span class=\"selector-mappings\">{{ getMappingSummary(selector) }}</span>\n </div>\n <div class=\"selector-actions\">\n <button\n kendoButton\n [svgIcon]=\"pencilIcon\"\n (click)=\"editSelector(i)\"\n fillMode=\"flat\"\n size=\"small\"\n title=\"Edit selector\">\n </button>\n <button\n kendoButton\n [svgIcon]=\"trashIcon\"\n (click)=\"removeSelector(i)\"\n fillMode=\"flat\"\n size=\"small\"\n title=\"Remove selector\"\n class=\"remove-btn\">\n </button>\n </div>\n </div>\n }\n\n @if (entitySelectors.length === 0) {\n <div class=\"empty-state\">\n No entity selectors configured. Add one to enable entity-based variables.\n </div>\n }\n </div>\n\n <div class=\"add-button-row\">\n <button\n kendoButton\n [svgIcon]=\"plusIcon\"\n (click)=\"addSelector()\"\n themeColor=\"primary\">\n Add Entity Selector\n </button>\n </div>\n } @else {\n <!-- Edit Form -->\n <div class=\"edit-panel\">\n <div class=\"edit-panel-header\">\n {{ editingIndex !== null ? 'Edit Entity Selector' : 'New Entity Selector' }}\n </div>\n <div class=\"edit-form\">\n <div class=\"form-field\">\n <label class=\"field-label\">ID *</label>\n <kendo-textbox\n [(ngModel)]=\"editId\"\n placeholder=\"e.g., mp\"\n [readonly]=\"editingIndex !== null\">\n </kendo-textbox>\n @if (isDuplicateId(editId, editingIndex)) {\n <span class=\"field-error\">ID must be unique</span>\n }\n <span class=\"field-hint\">Used in URL parameters (es_&lt;id&gt;=&lt;rtId&gt;)</span>\n </div>\n\n <div class=\"form-field\">\n <label class=\"field-label\">Label *</label>\n <kendo-textbox\n [(ngModel)]=\"editLabel\"\n placeholder=\"e.g., Metering Point\">\n </kendo-textbox>\n </div>\n\n <div class=\"form-field\">\n <label class=\"field-label\">CK Type *</label>\n <mm-ck-type-selector-input\n [ngModel]=\"editCkTypeItem\"\n [placeholder]=\"'Select CK type...'\"\n (ckTypeSelected)=\"onCkTypeSelected($event)\"\n (ckTypeCleared)=\"onCkTypeCleared()\">\n </mm-ck-type-selector-input>\n </div>\n\n <div class=\"form-field\">\n <label class=\"field-label\">Attribute Mappings *</label>\n <button\n kendoButton\n (click)=\"selectAttributes()\"\n [disabled]=\"!editCkTypeId\"\n fillMode=\"outline\"\n size=\"small\">\n Select Attributes...\n </button>\n\n @if (editMappings.length > 0) {\n <div class=\"mappings-list\">\n <div class=\"mappings-header\">\n <span class=\"mappings-header-attr\">Attribute</span>\n <span class=\"mappings-header-var\">Variable Name</span>\n </div>\n @for (mapping of editMappings; track mapping.attributePath; let j = $index) {\n <div class=\"mapping-item\">\n <span class=\"mapping-path\">{{ mapping.attributePath }}</span>\n <span class=\"mapping-arrow\">&rarr;</span>\n <div class=\"mapping-var-field\">\n <div class=\"mapping-var-input-wrapper\">\n <span class=\"mapping-var-prefix\">$</span>\n <kendo-textbox\n [(ngModel)]=\"mapping.variableName\"\n size=\"small\"\n class=\"mapping-var-input\"\n placeholder=\"variableName\"\n [class.invalid]=\"getVariableNameError(mapping)\">\n </kendo-textbox>\n </div>\n @if (getVariableNameError(mapping); as error) {\n <span class=\"field-error\">{{ error }}</span>\n }\n </div>\n </div>\n }\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <div class=\"checkbox-row\">\n <input\n type=\"checkbox\"\n kendoCheckBox\n [(ngModel)]=\"editShowInToolbar\"\n id=\"showInToolbar\" />\n <label class=\"field-label checkbox-label\" for=\"showInToolbar\">Show in Toolbar</label>\n </div>\n <span class=\"field-hint\">\n @if (editShowInToolbar) {\n Users can select an entity from a dropdown in the toolbar.\n } @else {\n Hidden selector \u2014 variables are resolved from the default entity below.\n }\n </span>\n </div>\n\n <div class=\"form-field\">\n <label class=\"field-label\">Default Entity{{ !editShowInToolbar ? ' *' : '' }}</label>\n @if (defaultEntityDataSource) {\n <mm-entity-select-input\n [dataSource]=\"defaultEntityDataSource\"\n [dialogDataSource]=\"defaultEntityDialogDataSource!\"\n [placeholder]=\"'Select default entity...'\"\n [dialogTitle]=\"'Select Default ' + editLabel\"\n (entitySelected)=\"onDefaultEntitySelected($event)\"\n (entityCleared)=\"onDefaultEntityCleared()\">\n </mm-entity-select-input>\n } @else {\n <span class=\"field-hint\">Select a CK type first to pick a default entity.</span>\n }\n @if (editDefaultRtId) {\n <span class=\"field-hint\">Selected: {{ editDefaultRtId }}</span>\n }\n @if (!editShowInToolbar && !editDefaultRtId) {\n <span class=\"field-error\">A default entity is required when not shown in toolbar.</span>\n }\n <span class=\"field-hint\">\n @if (editShowInToolbar) {\n Optional \u2014 pre-selected entity when the MeshBoard loads.\n } @else {\n This entity's attributes will be used to populate the variables.\n }\n </span>\n </div>\n\n <div class=\"edit-actions mm-dialog-actions\">\n <button kendoButton (click)=\"cancelEdit()\" fillMode=\"flat\">\n Cancel\n </button>\n <button\n kendoButton\n (click)=\"saveEdit()\"\n [disabled]=\"!isEditValid()\"\n themeColor=\"primary\">\n {{ editingIndex !== null ? 'Update' : 'Add' }}\n </button>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: [".entity-selector-editor{display:flex;flex-direction:column;gap:1.25rem}.selector-list{display:flex;flex-direction:column;gap:.5rem}.selector-item{display:flex;justify-content:space-between;align-items:center;padding:.75rem 1rem;background:var(--kendo-color-surface-alt, #fafafa);border:1px solid var(--kendo-color-border, #e0e0e0);border-radius:4px}.selector-item .selector-info{display:flex;flex-direction:column;gap:.25rem;min-width:0;flex:1}.selector-item .selector-label{font-weight:500;color:var(--kendo-color-on-app-surface, #424242)}.selector-item .selector-mode{font-size:.75rem;font-weight:500;color:var(--kendo-color-info, #0288d1)}.selector-item .selector-ck-type{font-size:.8rem;color:var(--kendo-color-subtle, #757575);font-family:Consolas,Monaco,monospace}.selector-item .selector-mappings{font-size:.8rem;color:var(--kendo-color-primary, #3f51b5);font-family:Consolas,Monaco,monospace}.selector-item .selector-actions{display:flex;gap:.25rem;flex-shrink:0}.selector-item .remove-btn{color:var(--kendo-color-error, #f44336)}.empty-state{padding:1.5rem;text-align:center;color:var(--kendo-color-subtle, #757575);font-size:.875rem}.add-button-row{display:flex;justify-content:flex-start}.edit-panel{border:1px solid var(--kendo-color-border, #e0e0e0);border-radius:4px;overflow:hidden}.edit-panel-header{padding:.625rem 1rem;font-weight:600;font-size:.875rem;color:var(--kendo-color-on-primary, #ffffff);background:var(--kendo-color-primary, #3f51b5)}.edit-form{display:flex;flex-direction:column;gap:1.25rem;padding:1.25rem}.edit-form .form-field{display:flex;flex-direction:column;gap:.5rem}.edit-form .field-label{font-weight:500;font-size:.875rem;text-transform:uppercase;color:var(--kendo-color-on-app-surface, #424242)}.edit-form .field-hint{font-size:.75rem;color:var(--kendo-color-subtle, #757575)}.edit-form .field-error{font-size:.75rem;color:var(--kendo-color-error, #f44336)}.edit-form .checkbox-row{display:flex;align-items:center;gap:.5rem}.edit-form .checkbox-row .checkbox-label{margin:0;cursor:pointer}.mappings-list{display:flex;flex-direction:column;gap:.5rem;margin-top:.5rem;padding:.75rem;background:var(--kendo-color-surface-alt, #fafafa);border:1px solid var(--kendo-color-border, #e0e0e0);border-radius:4px}.mappings-header{display:flex;align-items:center;gap:.5rem;font-size:.75rem;font-weight:600;color:var(--kendo-color-subtle, #757575);padding-bottom:.25rem;border-bottom:1px solid var(--kendo-color-border, #e0e0e0)}.mappings-header .mappings-header-attr{min-width:150px}.mappings-header .mappings-header-var{margin-left:1.75rem}.mapping-item{display:flex;align-items:flex-start;gap:.5rem}.mapping-item .mapping-path{font-size:.8rem;font-family:Consolas,Monaco,monospace;color:var(--kendo-color-subtle, #757575);min-width:150px;padding-top:5px}.mapping-item .mapping-arrow{color:var(--kendo-color-subtle, #757575);flex-shrink:0;padding-top:5px}.mapping-item .mapping-var-field{display:flex;flex-direction:column;gap:2px;flex:1;min-width:100px}.mapping-item .mapping-var-input-wrapper{display:flex;align-items:center;gap:0}.mapping-item .mapping-var-prefix{font-family:Consolas,Monaco,monospace;font-size:.875rem;font-weight:600;color:var(--kendo-color-primary, #3f51b5);padding-right:2px;flex-shrink:0}.mapping-item .mapping-var-input{flex:1}.mapping-item .mapping-var-input.invalid ::ng-deep .k-input{border-color:var(--kendo-color-error, #f44336)}.edit-actions{display:flex;justify-content:flex-end;gap:.5rem;margin-top:.5rem;padding-top:.75rem;border-top:1px solid var(--kendo-color-border, #e0e0e0)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox]:not([ngNoCva])[formControlName],input[type=checkbox]:not([ngNoCva])[formControl],input[type=checkbox]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i3.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "directive", type: i3.CheckBoxDirective, selector: "input[kendoCheckBox]", inputs: ["size", "rounded"] }, { kind: "ngmodule", type: LabelModule }, { kind: "directive", type: i4$1.LabelDirective, selector: "label[for]", inputs: ["for", "labelClass"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: CkTypeSelectorInputComponent, selector: "mm-ck-type-selector-input", inputs: ["placeholder", "minSearchLength", "maxResults", "debounceMs", "ckModelIds", "allowAbstract", "dialogTitle", "advancedSearchLabel", "derivedFromRtCkTypeId", "messages", "dialogMessages", "disabled", "required"], outputs: ["ckTypeSelected", "ckTypeCleared"] }, { kind: "component", type: EntitySelectInputComponent, selector: "mm-entity-select-input", inputs: ["dataSource", "placeholder", "minSearchLength", "maxResults", "debounceMs", "prefix", "initialDisplayValue", "dialogDataSource", "dialogTitle", "multiSelect", "advancedSearchLabel", "dialogMessages", "messages", "disabled", "required"], outputs: ["entitySelected", "entityCleared", "entitiesSelected"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
28876
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: EntitySelectorEditorComponent, isStandalone: true, selector: "mm-entity-selector-editor", inputs: { entitySelectors: "entitySelectors", existingVariableNames: "existingVariableNames" }, outputs: { entitySelectorsChange: "entitySelectorsChange", editingStateChange: "editingStateChange" }, ngImport: i0, template: "<div class=\"entity-selector-editor\">\n @if (!isEditing) {\n <!-- Selector List -->\n <div class=\"selector-list\">\n @for (selector of entitySelectors; track selector.id; let i = $index) {\n <div class=\"selector-item\">\n <div class=\"selector-info\">\n <span class=\"selector-label\">{{ selector.label }}</span>\n <span class=\"selector-mode\">{{ getModeLabel(selector) }}</span>\n <span class=\"selector-ck-type\">{{ selector.ckTypeId }}</span>\n <span class=\"selector-mappings\">{{ getMappingSummary(selector) }}</span>\n </div>\n <div class=\"selector-actions\">\n <button\n kendoButton\n [svgIcon]=\"pencilIcon\"\n (click)=\"editSelector(i)\"\n fillMode=\"flat\"\n size=\"small\"\n title=\"Edit selector\">\n </button>\n <button\n kendoButton\n [svgIcon]=\"trashIcon\"\n (click)=\"removeSelector(i)\"\n fillMode=\"flat\"\n size=\"small\"\n title=\"Remove selector\"\n class=\"remove-btn\">\n </button>\n </div>\n </div>\n }\n\n @if (entitySelectors.length === 0) {\n <div class=\"empty-state\">\n No entity selectors configured. Add one to enable entity-based variables.\n </div>\n }\n </div>\n\n <div class=\"add-button-row\">\n <button\n kendoButton\n [svgIcon]=\"plusIcon\"\n (click)=\"addSelector()\"\n themeColor=\"primary\">\n Add Entity Selector\n </button>\n </div>\n } @else {\n <!-- Edit Form -->\n <div class=\"edit-panel\">\n <div class=\"edit-panel-header\">\n {{ editingIndex !== null ? 'Edit Entity Selector' : 'New Entity Selector' }}\n </div>\n <div class=\"edit-form\">\n <div class=\"form-field\">\n <label class=\"field-label\">ID *</label>\n <kendo-textbox\n [(ngModel)]=\"editId\"\n placeholder=\"e.g., mp\"\n [readonly]=\"editingIndex !== null\">\n </kendo-textbox>\n @if (isDuplicateId(editId, editingIndex)) {\n <span class=\"field-error\">ID must be unique</span>\n }\n <span class=\"field-hint\">Used in URL parameters (es_&lt;id&gt;=&lt;rtId&gt;)</span>\n </div>\n\n <div class=\"form-field\">\n <label class=\"field-label\">Label *</label>\n <kendo-textbox\n [(ngModel)]=\"editLabel\"\n placeholder=\"e.g., Metering Point\">\n </kendo-textbox>\n </div>\n\n <div class=\"form-field\">\n <label class=\"field-label\">CK Type *</label>\n <mm-ck-type-selector-input\n [ngModel]=\"editCkTypeItem\"\n [placeholder]=\"'Select CK type...'\"\n (ckTypeSelected)=\"onCkTypeSelected($event)\"\n (ckTypeCleared)=\"onCkTypeCleared()\">\n </mm-ck-type-selector-input>\n </div>\n\n <div class=\"form-field\">\n <label class=\"field-label\">Attribute Mappings *</label>\n <button\n kendoButton\n (click)=\"selectAttributes()\"\n [disabled]=\"!editCkTypeId\"\n fillMode=\"outline\"\n size=\"small\">\n Select Attributes...\n </button>\n\n @if (editMappings.length > 0) {\n <div class=\"mappings-list\">\n <div class=\"mappings-header\">\n <span class=\"mappings-header-attr\">Attribute</span>\n <span class=\"mappings-header-var\">Variable Name</span>\n </div>\n @for (mapping of editMappings; track mapping.attributePath; let j = $index) {\n <div class=\"mapping-item\">\n <span class=\"mapping-path\">{{ mapping.attributePath }}</span>\n <span class=\"mapping-arrow\">&rarr;</span>\n <div class=\"mapping-var-field\">\n <div class=\"mapping-var-input-wrapper\">\n <span class=\"mapping-var-prefix\">$</span>\n <kendo-textbox\n [(ngModel)]=\"mapping.variableName\"\n size=\"small\"\n class=\"mapping-var-input\"\n placeholder=\"variableName\"\n [class.invalid]=\"getVariableNameError(mapping)\">\n </kendo-textbox>\n </div>\n @if (getVariableNameError(mapping); as error) {\n <span class=\"field-error\">{{ error }}</span>\n }\n </div>\n </div>\n }\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <div class=\"checkbox-row\">\n <input\n type=\"checkbox\"\n kendoCheckBox\n [(ngModel)]=\"editShowInToolbar\"\n id=\"showInToolbar\" />\n <label class=\"field-label checkbox-label\" for=\"showInToolbar\">Show in Toolbar</label>\n </div>\n <span class=\"field-hint\">\n @if (editShowInToolbar) {\n Users can select an entity from a dropdown in the toolbar.\n } @else {\n Hidden selector \u2014 variables are resolved from the default entity below.\n }\n </span>\n </div>\n\n <div class=\"form-field\">\n <label class=\"field-label\">Default Entity{{ !editShowInToolbar ? ' *' : '' }}</label>\n @if (defaultEntityDataSource) {\n <mm-entity-select-input\n [dataSource]=\"defaultEntityDataSource\"\n [dialogDataSource]=\"defaultEntityDialogDataSource!\"\n [placeholder]=\"'Select default entity...'\"\n [dialogTitle]=\"'Select Default ' + editLabel\"\n (entitySelected)=\"onDefaultEntitySelected($event)\"\n (entityCleared)=\"onDefaultEntityCleared()\">\n </mm-entity-select-input>\n } @else {\n <span class=\"field-hint\">Select a CK type first to pick a default entity.</span>\n }\n @if (editDefaultRtId) {\n <span class=\"field-hint\">Selected: {{ editDefaultRtId }}</span>\n }\n @if (!editShowInToolbar && !editDefaultRtId) {\n <span class=\"field-error\">A default entity is required when not shown in toolbar.</span>\n }\n <span class=\"field-hint\">\n @if (editShowInToolbar) {\n Optional \u2014 pre-selected entity when the MeshBoard loads.\n } @else {\n This entity's attributes will be used to populate the variables.\n }\n </span>\n </div>\n\n <div class=\"form-field\">\n <label class=\"field-label\">Child Scope (stream-data)</label>\n <span class=\"field-hint\">\n Optional. Resolves the picked entity to its child source rtIds (one\n association hop) so a stream-data widget can scope to them \u2014 e.g. a\n MeteringPoint resolved to its EnergyMeasurement rtIds. Leave empty to\n scope by the picked entity's own rtId.\n </span>\n <mm-ck-type-selector-input\n [ngModel]=\"editChildScopeTargetCkTypeItem\"\n [placeholder]=\"'Child CK type (e.g. EnergyMeasurement)...'\"\n (ckTypeSelected)=\"onChildScopeCkTypeSelected($event)\"\n (ckTypeCleared)=\"onChildScopeCkTypeCleared()\">\n </mm-ck-type-selector-input>\n <kendo-textbox\n [(ngModel)]=\"editChildScopeRoleId\"\n placeholder=\"Association role (e.g. System/ParentChild)\">\n </kendo-textbox>\n <label class=\"field-label\">Direction</label>\n <select [(ngModel)]=\"editChildScopeDirection\" class=\"child-scope-direction\">\n <option value=\"out\">Outbound (parent \u2192 child)</option>\n <option value=\"in\">Inbound (child \u2192 parent)</option>\n </select>\n </div>\n\n <div class=\"edit-actions mm-dialog-actions\">\n <button kendoButton (click)=\"cancelEdit()\" fillMode=\"flat\">\n Cancel\n </button>\n <button\n kendoButton\n (click)=\"saveEdit()\"\n [disabled]=\"!isEditValid()\"\n themeColor=\"primary\">\n {{ editingIndex !== null ? 'Update' : 'Add' }}\n </button>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: [".entity-selector-editor{display:flex;flex-direction:column;gap:1.25rem}.selector-list{display:flex;flex-direction:column;gap:.5rem}.selector-item{display:flex;justify-content:space-between;align-items:center;padding:.75rem 1rem;background:var(--kendo-color-surface-alt, #fafafa);border:1px solid var(--kendo-color-border, #e0e0e0);border-radius:4px}.selector-item .selector-info{display:flex;flex-direction:column;gap:.25rem;min-width:0;flex:1}.selector-item .selector-label{font-weight:500;color:var(--kendo-color-on-app-surface, #424242)}.selector-item .selector-mode{font-size:.75rem;font-weight:500;color:var(--kendo-color-info, #0288d1)}.selector-item .selector-ck-type{font-size:.8rem;color:var(--kendo-color-subtle, #757575);font-family:Consolas,Monaco,monospace}.selector-item .selector-mappings{font-size:.8rem;color:var(--kendo-color-primary, #3f51b5);font-family:Consolas,Monaco,monospace}.selector-item .selector-actions{display:flex;gap:.25rem;flex-shrink:0}.selector-item .remove-btn{color:var(--kendo-color-error, #f44336)}.empty-state{padding:1.5rem;text-align:center;color:var(--kendo-color-subtle, #757575);font-size:.875rem}.add-button-row{display:flex;justify-content:flex-start}.edit-panel{border:1px solid var(--kendo-color-border, #e0e0e0);border-radius:4px;overflow:hidden}.edit-panel-header{padding:.625rem 1rem;font-weight:600;font-size:.875rem;color:var(--kendo-color-on-primary, #ffffff);background:var(--kendo-color-primary, #3f51b5)}.edit-form{display:flex;flex-direction:column;gap:1.25rem;padding:1.25rem}.edit-form .form-field{display:flex;flex-direction:column;gap:.5rem}.edit-form .field-label{font-weight:500;font-size:.875rem;text-transform:uppercase;color:var(--kendo-color-on-app-surface, #424242)}.edit-form .field-hint{font-size:.75rem;color:var(--kendo-color-subtle, #757575)}.edit-form .field-error{font-size:.75rem;color:var(--kendo-color-error, #f44336)}.edit-form .checkbox-row{display:flex;align-items:center;gap:.5rem}.edit-form .checkbox-row .checkbox-label{margin:0;cursor:pointer}.mappings-list{display:flex;flex-direction:column;gap:.5rem;margin-top:.5rem;padding:.75rem;background:var(--kendo-color-surface-alt, #fafafa);border:1px solid var(--kendo-color-border, #e0e0e0);border-radius:4px}.mappings-header{display:flex;align-items:center;gap:.5rem;font-size:.75rem;font-weight:600;color:var(--kendo-color-subtle, #757575);padding-bottom:.25rem;border-bottom:1px solid var(--kendo-color-border, #e0e0e0)}.mappings-header .mappings-header-attr{min-width:150px}.mappings-header .mappings-header-var{margin-left:1.75rem}.mapping-item{display:flex;align-items:flex-start;gap:.5rem}.mapping-item .mapping-path{font-size:.8rem;font-family:Consolas,Monaco,monospace;color:var(--kendo-color-subtle, #757575);min-width:150px;padding-top:5px}.mapping-item .mapping-arrow{color:var(--kendo-color-subtle, #757575);flex-shrink:0;padding-top:5px}.mapping-item .mapping-var-field{display:flex;flex-direction:column;gap:2px;flex:1;min-width:100px}.mapping-item .mapping-var-input-wrapper{display:flex;align-items:center;gap:0}.mapping-item .mapping-var-prefix{font-family:Consolas,Monaco,monospace;font-size:.875rem;font-weight:600;color:var(--kendo-color-primary, #3f51b5);padding-right:2px;flex-shrink:0}.mapping-item .mapping-var-input{flex:1}.mapping-item .mapping-var-input.invalid ::ng-deep .k-input{border-color:var(--kendo-color-error, #f44336)}.edit-actions{display:flex;justify-content:flex-end;gap:.5rem;margin-top:.5rem;padding-top:.75rem;border-top:1px solid var(--kendo-color-border, #e0e0e0)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox]:not([ngNoCva])[formControlName],input[type=checkbox]:not([ngNoCva])[formControl],input[type=checkbox]:not([ngNoCva])[ngModel]" }, { kind: "directive", type: i1$1.SelectControlValueAccessor, selector: "select:not([multiple]):not([ngNoCva])[formControlName],select:not([multiple]):not([ngNoCva])[formControl],select:not([multiple]):not([ngNoCva])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2.ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconPosition", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "ngmodule", type: InputsModule }, { kind: "component", type: i3.TextBoxComponent, selector: "kendo-textbox", inputs: ["focusableId", "title", "type", "disabled", "readonly", "tabindex", "value", "selectOnFocus", "showSuccessIcon", "showErrorIcon", "clearButton", "successIcon", "successSvgIcon", "errorIcon", "errorSvgIcon", "clearButtonIcon", "clearButtonSvgIcon", "size", "rounded", "fillMode", "tabIndex", "placeholder", "maxlength", "inputAttributes"], outputs: ["valueChange", "inputFocus", "inputBlur", "focus", "blur"], exportAs: ["kendoTextBox"] }, { kind: "directive", type: i3.CheckBoxDirective, selector: "input[kendoCheckBox]", inputs: ["size", "rounded"] }, { kind: "ngmodule", type: LabelModule }, { kind: "directive", type: i4$1.LabelDirective, selector: "label[for]", inputs: ["for", "labelClass"] }, { kind: "ngmodule", type: SVGIconModule }, { kind: "component", type: CkTypeSelectorInputComponent, selector: "mm-ck-type-selector-input", inputs: ["placeholder", "minSearchLength", "maxResults", "debounceMs", "ckModelIds", "allowAbstract", "dialogTitle", "advancedSearchLabel", "derivedFromRtCkTypeId", "messages", "dialogMessages", "disabled", "required"], outputs: ["ckTypeSelected", "ckTypeCleared"] }, { kind: "component", type: EntitySelectInputComponent, selector: "mm-entity-select-input", inputs: ["dataSource", "placeholder", "minSearchLength", "maxResults", "debounceMs", "prefix", "initialDisplayValue", "dialogDataSource", "dialogTitle", "multiSelect", "advancedSearchLabel", "dialogMessages", "messages", "disabled", "required"], outputs: ["entitySelected", "entityCleared", "entitiesSelected"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
28300
28877
  }
28301
28878
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: EntitySelectorEditorComponent, decorators: [{
28302
28879
  type: Component,
@@ -28309,7 +28886,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
28309
28886
  SVGIconModule,
28310
28887
  CkTypeSelectorInputComponent,
28311
28888
  EntitySelectInputComponent
28312
- ], changeDetection: ChangeDetectionStrategy.Eager, template: "<div class=\"entity-selector-editor\">\n @if (!isEditing) {\n <!-- Selector List -->\n <div class=\"selector-list\">\n @for (selector of entitySelectors; track selector.id; let i = $index) {\n <div class=\"selector-item\">\n <div class=\"selector-info\">\n <span class=\"selector-label\">{{ selector.label }}</span>\n <span class=\"selector-mode\">{{ getModeLabel(selector) }}</span>\n <span class=\"selector-ck-type\">{{ selector.ckTypeId }}</span>\n <span class=\"selector-mappings\">{{ getMappingSummary(selector) }}</span>\n </div>\n <div class=\"selector-actions\">\n <button\n kendoButton\n [svgIcon]=\"pencilIcon\"\n (click)=\"editSelector(i)\"\n fillMode=\"flat\"\n size=\"small\"\n title=\"Edit selector\">\n </button>\n <button\n kendoButton\n [svgIcon]=\"trashIcon\"\n (click)=\"removeSelector(i)\"\n fillMode=\"flat\"\n size=\"small\"\n title=\"Remove selector\"\n class=\"remove-btn\">\n </button>\n </div>\n </div>\n }\n\n @if (entitySelectors.length === 0) {\n <div class=\"empty-state\">\n No entity selectors configured. Add one to enable entity-based variables.\n </div>\n }\n </div>\n\n <div class=\"add-button-row\">\n <button\n kendoButton\n [svgIcon]=\"plusIcon\"\n (click)=\"addSelector()\"\n themeColor=\"primary\">\n Add Entity Selector\n </button>\n </div>\n } @else {\n <!-- Edit Form -->\n <div class=\"edit-panel\">\n <div class=\"edit-panel-header\">\n {{ editingIndex !== null ? 'Edit Entity Selector' : 'New Entity Selector' }}\n </div>\n <div class=\"edit-form\">\n <div class=\"form-field\">\n <label class=\"field-label\">ID *</label>\n <kendo-textbox\n [(ngModel)]=\"editId\"\n placeholder=\"e.g., mp\"\n [readonly]=\"editingIndex !== null\">\n </kendo-textbox>\n @if (isDuplicateId(editId, editingIndex)) {\n <span class=\"field-error\">ID must be unique</span>\n }\n <span class=\"field-hint\">Used in URL parameters (es_&lt;id&gt;=&lt;rtId&gt;)</span>\n </div>\n\n <div class=\"form-field\">\n <label class=\"field-label\">Label *</label>\n <kendo-textbox\n [(ngModel)]=\"editLabel\"\n placeholder=\"e.g., Metering Point\">\n </kendo-textbox>\n </div>\n\n <div class=\"form-field\">\n <label class=\"field-label\">CK Type *</label>\n <mm-ck-type-selector-input\n [ngModel]=\"editCkTypeItem\"\n [placeholder]=\"'Select CK type...'\"\n (ckTypeSelected)=\"onCkTypeSelected($event)\"\n (ckTypeCleared)=\"onCkTypeCleared()\">\n </mm-ck-type-selector-input>\n </div>\n\n <div class=\"form-field\">\n <label class=\"field-label\">Attribute Mappings *</label>\n <button\n kendoButton\n (click)=\"selectAttributes()\"\n [disabled]=\"!editCkTypeId\"\n fillMode=\"outline\"\n size=\"small\">\n Select Attributes...\n </button>\n\n @if (editMappings.length > 0) {\n <div class=\"mappings-list\">\n <div class=\"mappings-header\">\n <span class=\"mappings-header-attr\">Attribute</span>\n <span class=\"mappings-header-var\">Variable Name</span>\n </div>\n @for (mapping of editMappings; track mapping.attributePath; let j = $index) {\n <div class=\"mapping-item\">\n <span class=\"mapping-path\">{{ mapping.attributePath }}</span>\n <span class=\"mapping-arrow\">&rarr;</span>\n <div class=\"mapping-var-field\">\n <div class=\"mapping-var-input-wrapper\">\n <span class=\"mapping-var-prefix\">$</span>\n <kendo-textbox\n [(ngModel)]=\"mapping.variableName\"\n size=\"small\"\n class=\"mapping-var-input\"\n placeholder=\"variableName\"\n [class.invalid]=\"getVariableNameError(mapping)\">\n </kendo-textbox>\n </div>\n @if (getVariableNameError(mapping); as error) {\n <span class=\"field-error\">{{ error }}</span>\n }\n </div>\n </div>\n }\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <div class=\"checkbox-row\">\n <input\n type=\"checkbox\"\n kendoCheckBox\n [(ngModel)]=\"editShowInToolbar\"\n id=\"showInToolbar\" />\n <label class=\"field-label checkbox-label\" for=\"showInToolbar\">Show in Toolbar</label>\n </div>\n <span class=\"field-hint\">\n @if (editShowInToolbar) {\n Users can select an entity from a dropdown in the toolbar.\n } @else {\n Hidden selector \u2014 variables are resolved from the default entity below.\n }\n </span>\n </div>\n\n <div class=\"form-field\">\n <label class=\"field-label\">Default Entity{{ !editShowInToolbar ? ' *' : '' }}</label>\n @if (defaultEntityDataSource) {\n <mm-entity-select-input\n [dataSource]=\"defaultEntityDataSource\"\n [dialogDataSource]=\"defaultEntityDialogDataSource!\"\n [placeholder]=\"'Select default entity...'\"\n [dialogTitle]=\"'Select Default ' + editLabel\"\n (entitySelected)=\"onDefaultEntitySelected($event)\"\n (entityCleared)=\"onDefaultEntityCleared()\">\n </mm-entity-select-input>\n } @else {\n <span class=\"field-hint\">Select a CK type first to pick a default entity.</span>\n }\n @if (editDefaultRtId) {\n <span class=\"field-hint\">Selected: {{ editDefaultRtId }}</span>\n }\n @if (!editShowInToolbar && !editDefaultRtId) {\n <span class=\"field-error\">A default entity is required when not shown in toolbar.</span>\n }\n <span class=\"field-hint\">\n @if (editShowInToolbar) {\n Optional \u2014 pre-selected entity when the MeshBoard loads.\n } @else {\n This entity's attributes will be used to populate the variables.\n }\n </span>\n </div>\n\n <div class=\"edit-actions mm-dialog-actions\">\n <button kendoButton (click)=\"cancelEdit()\" fillMode=\"flat\">\n Cancel\n </button>\n <button\n kendoButton\n (click)=\"saveEdit()\"\n [disabled]=\"!isEditValid()\"\n themeColor=\"primary\">\n {{ editingIndex !== null ? 'Update' : 'Add' }}\n </button>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: [".entity-selector-editor{display:flex;flex-direction:column;gap:1.25rem}.selector-list{display:flex;flex-direction:column;gap:.5rem}.selector-item{display:flex;justify-content:space-between;align-items:center;padding:.75rem 1rem;background:var(--kendo-color-surface-alt, #fafafa);border:1px solid var(--kendo-color-border, #e0e0e0);border-radius:4px}.selector-item .selector-info{display:flex;flex-direction:column;gap:.25rem;min-width:0;flex:1}.selector-item .selector-label{font-weight:500;color:var(--kendo-color-on-app-surface, #424242)}.selector-item .selector-mode{font-size:.75rem;font-weight:500;color:var(--kendo-color-info, #0288d1)}.selector-item .selector-ck-type{font-size:.8rem;color:var(--kendo-color-subtle, #757575);font-family:Consolas,Monaco,monospace}.selector-item .selector-mappings{font-size:.8rem;color:var(--kendo-color-primary, #3f51b5);font-family:Consolas,Monaco,monospace}.selector-item .selector-actions{display:flex;gap:.25rem;flex-shrink:0}.selector-item .remove-btn{color:var(--kendo-color-error, #f44336)}.empty-state{padding:1.5rem;text-align:center;color:var(--kendo-color-subtle, #757575);font-size:.875rem}.add-button-row{display:flex;justify-content:flex-start}.edit-panel{border:1px solid var(--kendo-color-border, #e0e0e0);border-radius:4px;overflow:hidden}.edit-panel-header{padding:.625rem 1rem;font-weight:600;font-size:.875rem;color:var(--kendo-color-on-primary, #ffffff);background:var(--kendo-color-primary, #3f51b5)}.edit-form{display:flex;flex-direction:column;gap:1.25rem;padding:1.25rem}.edit-form .form-field{display:flex;flex-direction:column;gap:.5rem}.edit-form .field-label{font-weight:500;font-size:.875rem;text-transform:uppercase;color:var(--kendo-color-on-app-surface, #424242)}.edit-form .field-hint{font-size:.75rem;color:var(--kendo-color-subtle, #757575)}.edit-form .field-error{font-size:.75rem;color:var(--kendo-color-error, #f44336)}.edit-form .checkbox-row{display:flex;align-items:center;gap:.5rem}.edit-form .checkbox-row .checkbox-label{margin:0;cursor:pointer}.mappings-list{display:flex;flex-direction:column;gap:.5rem;margin-top:.5rem;padding:.75rem;background:var(--kendo-color-surface-alt, #fafafa);border:1px solid var(--kendo-color-border, #e0e0e0);border-radius:4px}.mappings-header{display:flex;align-items:center;gap:.5rem;font-size:.75rem;font-weight:600;color:var(--kendo-color-subtle, #757575);padding-bottom:.25rem;border-bottom:1px solid var(--kendo-color-border, #e0e0e0)}.mappings-header .mappings-header-attr{min-width:150px}.mappings-header .mappings-header-var{margin-left:1.75rem}.mapping-item{display:flex;align-items:flex-start;gap:.5rem}.mapping-item .mapping-path{font-size:.8rem;font-family:Consolas,Monaco,monospace;color:var(--kendo-color-subtle, #757575);min-width:150px;padding-top:5px}.mapping-item .mapping-arrow{color:var(--kendo-color-subtle, #757575);flex-shrink:0;padding-top:5px}.mapping-item .mapping-var-field{display:flex;flex-direction:column;gap:2px;flex:1;min-width:100px}.mapping-item .mapping-var-input-wrapper{display:flex;align-items:center;gap:0}.mapping-item .mapping-var-prefix{font-family:Consolas,Monaco,monospace;font-size:.875rem;font-weight:600;color:var(--kendo-color-primary, #3f51b5);padding-right:2px;flex-shrink:0}.mapping-item .mapping-var-input{flex:1}.mapping-item .mapping-var-input.invalid ::ng-deep .k-input{border-color:var(--kendo-color-error, #f44336)}.edit-actions{display:flex;justify-content:flex-end;gap:.5rem;margin-top:.5rem;padding-top:.75rem;border-top:1px solid var(--kendo-color-border, #e0e0e0)}\n"] }]
28889
+ ], changeDetection: ChangeDetectionStrategy.Eager, template: "<div class=\"entity-selector-editor\">\n @if (!isEditing) {\n <!-- Selector List -->\n <div class=\"selector-list\">\n @for (selector of entitySelectors; track selector.id; let i = $index) {\n <div class=\"selector-item\">\n <div class=\"selector-info\">\n <span class=\"selector-label\">{{ selector.label }}</span>\n <span class=\"selector-mode\">{{ getModeLabel(selector) }}</span>\n <span class=\"selector-ck-type\">{{ selector.ckTypeId }}</span>\n <span class=\"selector-mappings\">{{ getMappingSummary(selector) }}</span>\n </div>\n <div class=\"selector-actions\">\n <button\n kendoButton\n [svgIcon]=\"pencilIcon\"\n (click)=\"editSelector(i)\"\n fillMode=\"flat\"\n size=\"small\"\n title=\"Edit selector\">\n </button>\n <button\n kendoButton\n [svgIcon]=\"trashIcon\"\n (click)=\"removeSelector(i)\"\n fillMode=\"flat\"\n size=\"small\"\n title=\"Remove selector\"\n class=\"remove-btn\">\n </button>\n </div>\n </div>\n }\n\n @if (entitySelectors.length === 0) {\n <div class=\"empty-state\">\n No entity selectors configured. Add one to enable entity-based variables.\n </div>\n }\n </div>\n\n <div class=\"add-button-row\">\n <button\n kendoButton\n [svgIcon]=\"plusIcon\"\n (click)=\"addSelector()\"\n themeColor=\"primary\">\n Add Entity Selector\n </button>\n </div>\n } @else {\n <!-- Edit Form -->\n <div class=\"edit-panel\">\n <div class=\"edit-panel-header\">\n {{ editingIndex !== null ? 'Edit Entity Selector' : 'New Entity Selector' }}\n </div>\n <div class=\"edit-form\">\n <div class=\"form-field\">\n <label class=\"field-label\">ID *</label>\n <kendo-textbox\n [(ngModel)]=\"editId\"\n placeholder=\"e.g., mp\"\n [readonly]=\"editingIndex !== null\">\n </kendo-textbox>\n @if (isDuplicateId(editId, editingIndex)) {\n <span class=\"field-error\">ID must be unique</span>\n }\n <span class=\"field-hint\">Used in URL parameters (es_&lt;id&gt;=&lt;rtId&gt;)</span>\n </div>\n\n <div class=\"form-field\">\n <label class=\"field-label\">Label *</label>\n <kendo-textbox\n [(ngModel)]=\"editLabel\"\n placeholder=\"e.g., Metering Point\">\n </kendo-textbox>\n </div>\n\n <div class=\"form-field\">\n <label class=\"field-label\">CK Type *</label>\n <mm-ck-type-selector-input\n [ngModel]=\"editCkTypeItem\"\n [placeholder]=\"'Select CK type...'\"\n (ckTypeSelected)=\"onCkTypeSelected($event)\"\n (ckTypeCleared)=\"onCkTypeCleared()\">\n </mm-ck-type-selector-input>\n </div>\n\n <div class=\"form-field\">\n <label class=\"field-label\">Attribute Mappings *</label>\n <button\n kendoButton\n (click)=\"selectAttributes()\"\n [disabled]=\"!editCkTypeId\"\n fillMode=\"outline\"\n size=\"small\">\n Select Attributes...\n </button>\n\n @if (editMappings.length > 0) {\n <div class=\"mappings-list\">\n <div class=\"mappings-header\">\n <span class=\"mappings-header-attr\">Attribute</span>\n <span class=\"mappings-header-var\">Variable Name</span>\n </div>\n @for (mapping of editMappings; track mapping.attributePath; let j = $index) {\n <div class=\"mapping-item\">\n <span class=\"mapping-path\">{{ mapping.attributePath }}</span>\n <span class=\"mapping-arrow\">&rarr;</span>\n <div class=\"mapping-var-field\">\n <div class=\"mapping-var-input-wrapper\">\n <span class=\"mapping-var-prefix\">$</span>\n <kendo-textbox\n [(ngModel)]=\"mapping.variableName\"\n size=\"small\"\n class=\"mapping-var-input\"\n placeholder=\"variableName\"\n [class.invalid]=\"getVariableNameError(mapping)\">\n </kendo-textbox>\n </div>\n @if (getVariableNameError(mapping); as error) {\n <span class=\"field-error\">{{ error }}</span>\n }\n </div>\n </div>\n }\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <div class=\"checkbox-row\">\n <input\n type=\"checkbox\"\n kendoCheckBox\n [(ngModel)]=\"editShowInToolbar\"\n id=\"showInToolbar\" />\n <label class=\"field-label checkbox-label\" for=\"showInToolbar\">Show in Toolbar</label>\n </div>\n <span class=\"field-hint\">\n @if (editShowInToolbar) {\n Users can select an entity from a dropdown in the toolbar.\n } @else {\n Hidden selector \u2014 variables are resolved from the default entity below.\n }\n </span>\n </div>\n\n <div class=\"form-field\">\n <label class=\"field-label\">Default Entity{{ !editShowInToolbar ? ' *' : '' }}</label>\n @if (defaultEntityDataSource) {\n <mm-entity-select-input\n [dataSource]=\"defaultEntityDataSource\"\n [dialogDataSource]=\"defaultEntityDialogDataSource!\"\n [placeholder]=\"'Select default entity...'\"\n [dialogTitle]=\"'Select Default ' + editLabel\"\n (entitySelected)=\"onDefaultEntitySelected($event)\"\n (entityCleared)=\"onDefaultEntityCleared()\">\n </mm-entity-select-input>\n } @else {\n <span class=\"field-hint\">Select a CK type first to pick a default entity.</span>\n }\n @if (editDefaultRtId) {\n <span class=\"field-hint\">Selected: {{ editDefaultRtId }}</span>\n }\n @if (!editShowInToolbar && !editDefaultRtId) {\n <span class=\"field-error\">A default entity is required when not shown in toolbar.</span>\n }\n <span class=\"field-hint\">\n @if (editShowInToolbar) {\n Optional \u2014 pre-selected entity when the MeshBoard loads.\n } @else {\n This entity's attributes will be used to populate the variables.\n }\n </span>\n </div>\n\n <div class=\"form-field\">\n <label class=\"field-label\">Child Scope (stream-data)</label>\n <span class=\"field-hint\">\n Optional. Resolves the picked entity to its child source rtIds (one\n association hop) so a stream-data widget can scope to them \u2014 e.g. a\n MeteringPoint resolved to its EnergyMeasurement rtIds. Leave empty to\n scope by the picked entity's own rtId.\n </span>\n <mm-ck-type-selector-input\n [ngModel]=\"editChildScopeTargetCkTypeItem\"\n [placeholder]=\"'Child CK type (e.g. EnergyMeasurement)...'\"\n (ckTypeSelected)=\"onChildScopeCkTypeSelected($event)\"\n (ckTypeCleared)=\"onChildScopeCkTypeCleared()\">\n </mm-ck-type-selector-input>\n <kendo-textbox\n [(ngModel)]=\"editChildScopeRoleId\"\n placeholder=\"Association role (e.g. System/ParentChild)\">\n </kendo-textbox>\n <label class=\"field-label\">Direction</label>\n <select [(ngModel)]=\"editChildScopeDirection\" class=\"child-scope-direction\">\n <option value=\"out\">Outbound (parent \u2192 child)</option>\n <option value=\"in\">Inbound (child \u2192 parent)</option>\n </select>\n </div>\n\n <div class=\"edit-actions mm-dialog-actions\">\n <button kendoButton (click)=\"cancelEdit()\" fillMode=\"flat\">\n Cancel\n </button>\n <button\n kendoButton\n (click)=\"saveEdit()\"\n [disabled]=\"!isEditValid()\"\n themeColor=\"primary\">\n {{ editingIndex !== null ? 'Update' : 'Add' }}\n </button>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: [".entity-selector-editor{display:flex;flex-direction:column;gap:1.25rem}.selector-list{display:flex;flex-direction:column;gap:.5rem}.selector-item{display:flex;justify-content:space-between;align-items:center;padding:.75rem 1rem;background:var(--kendo-color-surface-alt, #fafafa);border:1px solid var(--kendo-color-border, #e0e0e0);border-radius:4px}.selector-item .selector-info{display:flex;flex-direction:column;gap:.25rem;min-width:0;flex:1}.selector-item .selector-label{font-weight:500;color:var(--kendo-color-on-app-surface, #424242)}.selector-item .selector-mode{font-size:.75rem;font-weight:500;color:var(--kendo-color-info, #0288d1)}.selector-item .selector-ck-type{font-size:.8rem;color:var(--kendo-color-subtle, #757575);font-family:Consolas,Monaco,monospace}.selector-item .selector-mappings{font-size:.8rem;color:var(--kendo-color-primary, #3f51b5);font-family:Consolas,Monaco,monospace}.selector-item .selector-actions{display:flex;gap:.25rem;flex-shrink:0}.selector-item .remove-btn{color:var(--kendo-color-error, #f44336)}.empty-state{padding:1.5rem;text-align:center;color:var(--kendo-color-subtle, #757575);font-size:.875rem}.add-button-row{display:flex;justify-content:flex-start}.edit-panel{border:1px solid var(--kendo-color-border, #e0e0e0);border-radius:4px;overflow:hidden}.edit-panel-header{padding:.625rem 1rem;font-weight:600;font-size:.875rem;color:var(--kendo-color-on-primary, #ffffff);background:var(--kendo-color-primary, #3f51b5)}.edit-form{display:flex;flex-direction:column;gap:1.25rem;padding:1.25rem}.edit-form .form-field{display:flex;flex-direction:column;gap:.5rem}.edit-form .field-label{font-weight:500;font-size:.875rem;text-transform:uppercase;color:var(--kendo-color-on-app-surface, #424242)}.edit-form .field-hint{font-size:.75rem;color:var(--kendo-color-subtle, #757575)}.edit-form .field-error{font-size:.75rem;color:var(--kendo-color-error, #f44336)}.edit-form .checkbox-row{display:flex;align-items:center;gap:.5rem}.edit-form .checkbox-row .checkbox-label{margin:0;cursor:pointer}.mappings-list{display:flex;flex-direction:column;gap:.5rem;margin-top:.5rem;padding:.75rem;background:var(--kendo-color-surface-alt, #fafafa);border:1px solid var(--kendo-color-border, #e0e0e0);border-radius:4px}.mappings-header{display:flex;align-items:center;gap:.5rem;font-size:.75rem;font-weight:600;color:var(--kendo-color-subtle, #757575);padding-bottom:.25rem;border-bottom:1px solid var(--kendo-color-border, #e0e0e0)}.mappings-header .mappings-header-attr{min-width:150px}.mappings-header .mappings-header-var{margin-left:1.75rem}.mapping-item{display:flex;align-items:flex-start;gap:.5rem}.mapping-item .mapping-path{font-size:.8rem;font-family:Consolas,Monaco,monospace;color:var(--kendo-color-subtle, #757575);min-width:150px;padding-top:5px}.mapping-item .mapping-arrow{color:var(--kendo-color-subtle, #757575);flex-shrink:0;padding-top:5px}.mapping-item .mapping-var-field{display:flex;flex-direction:column;gap:2px;flex:1;min-width:100px}.mapping-item .mapping-var-input-wrapper{display:flex;align-items:center;gap:0}.mapping-item .mapping-var-prefix{font-family:Consolas,Monaco,monospace;font-size:.875rem;font-weight:600;color:var(--kendo-color-primary, #3f51b5);padding-right:2px;flex-shrink:0}.mapping-item .mapping-var-input{flex:1}.mapping-item .mapping-var-input.invalid ::ng-deep .k-input{border-color:var(--kendo-color-error, #f44336)}.edit-actions{display:flex;justify-content:flex-end;gap:.5rem;margin-top:.5rem;padding-top:.75rem;border-top:1px solid var(--kendo-color-border, #e0e0e0)}\n"] }]
28313
28890
  }], propDecorators: { entitySelectors: [{
28314
28891
  type: Input
28315
28892
  }], existingVariableNames: [{
@@ -30602,6 +31179,8 @@ class MeshBoardViewComponent {
30602
31179
  this.stateService.updateEntitySelectorSelection(selectorId, undefined);
30603
31180
  // Clear variables for this selector
30604
31181
  this.stateService.clearEntitySelectorVariables(selectorId);
31182
+ // Clear the resolved stream-data scope rtIds for this selector
31183
+ this.stateService.clearEntitySelectorRtIds(selectorId);
30605
31184
  // Sync to URL
30606
31185
  this.writeEntitySelectorsToUrl();
30607
31186
  // Refresh widgets
@@ -30662,6 +31241,11 @@ class MeshBoardViewComponent {
30662
31241
  values.push({ name: rtIdVariableName, value: rtId, type: 'string' });
30663
31242
  }
30664
31243
  this.stateService.setEntitySelectorVariables(selector.id, values);
31244
+ // Resolve the stream-data scope rtIds for this selection (one-hop
31245
+ // childScope traversal, or the picked entity itself). Runs alongside the
31246
+ // variable resolution so widgets bound to this selector can scope their
31247
+ // stream-data query by the resolved source rtIds.
31248
+ await this.resolveEntitySelectorScopeRtIds(selector, rtId);
30665
31249
  return entity.rtWellKnownName || entity.rtId;
30666
31250
  }
30667
31251
  catch (error) {
@@ -30669,6 +31253,38 @@ class MeshBoardViewComponent {
30669
31253
  return undefined;
30670
31254
  }
30671
31255
  }
31256
+ /**
31257
+ * Resolves the set of source rtIds that a stream-data widget bound to this
31258
+ * selector should be scoped to, and caches them on the state service.
31259
+ *
31260
+ * When the selector defines a {@link EntitySelectorChildScope}, the picked
31261
+ * entity's children of `targetCkTypeId` reached via `roleId`/`direction`
31262
+ * become the scope (e.g. MeteringPoint → its EnergyMeasurement rtIds). With
31263
+ * no childScope, the picked entity's own rtId is the scope (direct keying).
31264
+ */
31265
+ async resolveEntitySelectorScopeRtIds(selector, rtId) {
31266
+ const childScope = selector.childScope;
31267
+ if (!childScope?.targetCkTypeId || !childScope.roleId) {
31268
+ this.stateService.setEntitySelectorRtIds(selector.id, [rtId]);
31269
+ return;
31270
+ }
31271
+ try {
31272
+ const targets = await firstValueFrom(this.dataService.fetchAssociationTargets(rtId, selector.ckTypeId, childScope.targetCkTypeId, childScope.roleId,
31273
+ // Parent → children via System/ParentChild is an INBOUND hop (the child
31274
+ // owns the association to its parent), so 'in' is the default.
31275
+ childScope.direction ?? 'in'));
31276
+ const childRtIds = (targets ?? [])
31277
+ .map(t => String(t.rtId))
31278
+ .filter(id => id.length > 0);
31279
+ this.stateService.setEntitySelectorRtIds(selector.id, childRtIds);
31280
+ }
31281
+ catch (error) {
31282
+ console.error(`Error resolving child scope for selector '${selector.id}':`, error);
31283
+ // Empty scope is safer than a stale one: the bound widget falls back to
31284
+ // its persisted scope rather than querying the wrong asset.
31285
+ this.stateService.setEntitySelectorRtIds(selector.id, []);
31286
+ }
31287
+ }
30672
31288
  /**
30673
31289
  * Writes entity selector selections to URL query parameters.
30674
31290
  * Format: es_<selectorId>=<rtId>