@meshmakers/octo-ui 3.3.580 → 3.3.600

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,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, Component, Injectable, EventEmitter, Output, Input, ElementRef, forwardRef, ViewChild, signal, computed, makeEnvironmentProviders } from '@angular/core';
2
+ import { inject, Component, Injectable, EventEmitter, Output, Input, ElementRef, forwardRef, ViewChild, signal, computed, HostListener, makeEnvironmentProviders } from '@angular/core';
3
3
  import { AttributeSelectorService, AttributeValueTypeDto as AttributeValueTypeDto$1, CkTypeAttributeService, CkTypeSelectorService, GetEntitiesByCkTypeDtoGQL, RuntimeEntitySelectDataSource, RuntimeEntityDialogDataSource, SearchFilterTypesDto, SortOrdersDto, FieldFilterOperatorsDto, provideOctoServices } from '@meshmakers/octo-services';
4
4
  import { WindowStateService, EntitySelectInputComponent, DataSourceTyped, HierarchyDataSourceBase, NotificationDisplayService, provideMmSharedUi } from '@meshmakers/shared-ui';
5
5
  import { provideMmSharedAuth } from '@meshmakers/shared-auth';
@@ -10,14 +10,14 @@ import { FormsModule, FormControl, ReactiveFormsModule, NG_VALUE_ACCESSOR, NG_VA
10
10
  import * as i3 from '@progress/kendo-angular-grid';
11
11
  import { GridModule } from '@progress/kendo-angular-grid';
12
12
  import * as i4 from '@progress/kendo-angular-buttons';
13
- import { ButtonsModule, ButtonModule } from '@progress/kendo-angular-buttons';
13
+ import { ButtonsModule, ButtonModule, ButtonComponent } from '@progress/kendo-angular-buttons';
14
14
  import * as i5 from '@progress/kendo-angular-inputs';
15
15
  import { InputsModule } from '@progress/kendo-angular-inputs';
16
16
  import * as i3$1 from '@progress/kendo-angular-dropdowns';
17
17
  import { DropDownListModule, DropDownsModule, AutoCompleteModule } from '@progress/kendo-angular-dropdowns';
18
18
  import * as i5$1 from '@progress/kendo-angular-icons';
19
- import { IconsModule, SVGIconModule } from '@progress/kendo-angular-icons';
20
- import { searchIcon, sortAscSmallIcon, sortDescSmallIcon, chevronRightIcon, chevronDownIcon, downloadIcon, fileIcon, folderIcon, calendarIcon, checkboxCheckedIcon, listUnorderedIcon, filterClearIcon, arrowRightIcon, arrowLeftIcon, chevronDoubleRightIcon, chevronDoubleLeftIcon, arrowUpIcon, arrowDownIcon, pencilIcon, trashIcon, plusIcon, minusIcon, dollarIcon, copyIcon } from '@progress/kendo-svg-icons';
19
+ import { IconsModule, SVGIconModule, SVGIconComponent } from '@progress/kendo-angular-icons';
20
+ import { searchIcon, sortAscSmallIcon, sortDescSmallIcon, chevronRightIcon, chevronDownIcon, downloadIcon, fileIcon, folderIcon, calendarIcon, checkboxCheckedIcon, listUnorderedIcon, filterClearIcon, arrowRightIcon, arrowLeftIcon, chevronDoubleRightIcon, chevronDoubleLeftIcon, arrowUpIcon, arrowDownIcon, pencilIcon, trashIcon, plusIcon, minusIcon, dollarIcon, copyIcon, arrowRotateCwIcon } from '@progress/kendo-svg-icons';
21
21
  import { WindowRef, WindowModule, WindowService, WindowCloseResult } from '@progress/kendo-angular-dialog';
22
22
  import { Subject, firstValueFrom, of, forkJoin, Subscription } from 'rxjs';
23
23
  import { debounceTime, distinctUntilChanged, switchMap, map, tap, catchError } from 'rxjs/operators';
@@ -25,7 +25,7 @@ import { LoaderModule } from '@progress/kendo-angular-indicators';
25
25
  import { isCompositeFilterDescriptor } from '@progress/kendo-data-query';
26
26
  import * as i6 from '@progress/kendo-angular-dateinputs';
27
27
  import { DateInputsModule } from '@progress/kendo-angular-dateinputs';
28
- import { PopupModule } from '@progress/kendo-angular-popup';
28
+ import { PopupModule, PopupComponent } from '@progress/kendo-angular-popup';
29
29
  import { IntlModule } from '@progress/kendo-angular-intl';
30
30
 
31
31
  class AttributeSortSelectorDialogComponent {
@@ -35,6 +35,8 @@ class AttributeSortSelectorDialogComponent {
35
35
  // Dialog data
36
36
  data;
37
37
  ckTypeId;
38
+ includeNavigationProperties = undefined;
39
+ attributePathsSet = null;
38
40
  searchText = '';
39
41
  currentSortOrder = 'standard';
40
42
  selectedValueTypeFilter = null;
@@ -77,6 +79,8 @@ class AttributeSortSelectorDialogComponent {
77
79
  if (this.data) {
78
80
  this.ckTypeId = this.data.ckTypeId;
79
81
  this.dialogTitle = this.data.dialogTitle || 'Select Attributes with Sort Order';
82
+ this.includeNavigationProperties = this.data.includeNavigationProperties;
83
+ this.attributePathsSet = this.data.attributePaths ? new Set(this.data.attributePaths) : null;
80
84
  if (this.data.selectedAttributes && this.data.selectedAttributes.length > 0) {
81
85
  this.selectedAttributes = [...this.data.selectedAttributes];
82
86
  this.updateSelectedGrid();
@@ -90,10 +94,14 @@ class AttributeSortSelectorDialogComponent {
90
94
  this.loadAvailableAttributes();
91
95
  }
92
96
  loadAvailableAttributes(searchTerm) {
93
- this.attributeService.getAvailableAttributes(this.ckTypeId, undefined, undefined, undefined, this.selectedValueTypeFilter || undefined, searchTerm || undefined).subscribe(result => {
97
+ this.attributeService.getAvailableAttributes(this.ckTypeId, undefined, undefined, undefined, this.selectedValueTypeFilter || undefined, searchTerm || undefined, this.includeNavigationProperties, undefined).subscribe(result => {
94
98
  // Filter out already selected attributes
95
99
  const selectedPaths = new Set(this.selectedAttributes.map(a => a.attributePath));
96
- this.availableAttributes = result.items.filter(item => !selectedPaths.has(item.attributePath));
100
+ // Apply client-side attribute path restriction if set
101
+ const filteredItems = this.attributePathsSet
102
+ ? result.items.filter(item => this.attributePathsSet.has(item.attributePath))
103
+ : result.items;
104
+ this.availableAttributes = filteredItems.filter(item => !selectedPaths.has(item.attributePath));
97
105
  this.updateAvailableGrid();
98
106
  });
99
107
  }
@@ -499,13 +507,18 @@ class AttributeSortSelectorDialogService {
499
507
  * @param ckTypeId The CkType ID to fetch attributes for
500
508
  * @param selectedAttributes Optional array of pre-selected attributes with sort orders
501
509
  * @param dialogTitle Optional custom dialog title
510
+ * @param includeNavigationProperties Optional flag to control navigation property inclusion
511
+ * @param hideNavigationControls Optional flag to hide the navigation property controls
502
512
  * @returns Promise that resolves with the result containing selected attributes with sort orders and confirmation status
503
513
  */
504
- async openAttributeSortSelector(ckTypeId, selectedAttributes, dialogTitle) {
514
+ async openAttributeSortSelector(ckTypeId, selectedAttributes, dialogTitle, includeNavigationProperties, hideNavigationControls, attributePaths) {
505
515
  const data = {
506
516
  ckTypeId,
507
517
  selectedAttributes,
508
- dialogTitle
518
+ dialogTitle,
519
+ includeNavigationProperties,
520
+ hideNavigationControls,
521
+ attributePaths
509
522
  };
510
523
  const size = this.windowStateService.resolveWindowSize('attribute-sort-selector', { width: 1200, height: 750 });
511
524
  const windowRef = this.windowService.open({
@@ -2701,11 +2714,14 @@ class AttributeSelectorDialogComponent {
2701
2714
  dialogTitle = 'Select Attributes';
2702
2715
  rtCkTypeId;
2703
2716
  singleSelect = false;
2717
+ additionalAttributes = [];
2704
2718
  searchText = '';
2705
2719
  selectedSingleKey = [];
2706
2720
  selectedValueTypeFilter = null;
2707
2721
  includeNavigationProperties = true;
2708
2722
  maxDepth = null;
2723
+ hideNavigationControls = false;
2724
+ attributePathsSet = null;
2709
2725
  availableAttributes = [];
2710
2726
  selectedAttributes = [];
2711
2727
  availableGridData = { data: [], total: 0 };
@@ -2734,6 +2750,9 @@ class AttributeSelectorDialogComponent {
2734
2750
  this.singleSelect = this.data.singleSelect ?? false;
2735
2751
  this.includeNavigationProperties = this.data.includeNavigationProperties ?? true;
2736
2752
  this.maxDepth = this.data.maxDepth ?? null;
2753
+ this.additionalAttributes = this.data.additionalAttributes ?? [];
2754
+ this.hideNavigationControls = this.data.hideNavigationControls ?? false;
2755
+ this.attributePathsSet = this.data.attributePaths ? new Set(this.data.attributePaths) : null;
2737
2756
  if (this.data.selectedAttributes && this.data.selectedAttributes.length > 0) {
2738
2757
  if (this.singleSelect) {
2739
2758
  this.selectedSingleKey = [this.data.selectedAttributes[0]];
@@ -2755,15 +2774,35 @@ class AttributeSelectorDialogComponent {
2755
2774
  this.attributeService.getAvailableAttributes(this.rtCkTypeId, undefined, undefined, undefined, this.selectedValueTypeFilter || undefined, searchTerm || undefined, this.includeNavigationProperties, this.maxDepth ?? undefined).subscribe(result => {
2756
2775
  // Filter out already selected attributes
2757
2776
  const selectedPaths = new Set(this.selectedAttributes.map(a => a.attributePath));
2758
- this.availableAttributes = result.items.filter(item => !selectedPaths.has(item.attributePath));
2777
+ // Apply client-side attribute path restriction if set (additionalAttributes bypass this filter intentionally)
2778
+ const filteredItems = this.attributePathsSet
2779
+ ? result.items.filter(item => this.attributePathsSet.has(item.attributePath))
2780
+ : result.items;
2781
+ // Include additional virtual attributes (e.g., Timestamp for stream data), filtered by search/type
2782
+ const filteredAdditional = this.additionalAttributes.filter(attr => {
2783
+ if (selectedPaths.has(attr.attributePath))
2784
+ return false;
2785
+ if (searchTerm && !attr.attributePath.toLowerCase().includes(searchTerm.toLowerCase()))
2786
+ return false;
2787
+ if (this.selectedValueTypeFilter && attr.attributeValueType !== this.selectedValueTypeFilter)
2788
+ return false;
2789
+ return true;
2790
+ });
2791
+ this.availableAttributes = [
2792
+ ...filteredAdditional,
2793
+ ...filteredItems.filter(item => !selectedPaths.has(item.attributePath))
2794
+ ];
2759
2795
  this.updateAvailableGrid();
2760
2796
  });
2761
2797
  }
2762
2798
  loadInitialSelectedAttributes(attributePaths) {
2763
2799
  // Load all attributes to get the details for selected ones
2764
2800
  this.attributeService.getAvailableAttributes(this.rtCkTypeId).subscribe(result => {
2765
- // Create a map for quick lookup
2801
+ // Create a map for quick lookup, including additional virtual attributes
2766
2802
  const attributeMap = new Map(result.items.map(item => [item.attributePath, item]));
2803
+ for (const attr of this.additionalAttributes) {
2804
+ attributeMap.set(attr.attributePath, attr);
2805
+ }
2767
2806
  // Preserve the order from attributePaths
2768
2807
  this.selectedAttributes = attributePaths
2769
2808
  .map(path => attributeMap.get(path))
@@ -2771,7 +2810,10 @@ class AttributeSelectorDialogComponent {
2771
2810
  this.updateSelectedGrid();
2772
2811
  // Filter out selected from available
2773
2812
  const selectedPaths = new Set(this.selectedAttributes.map(a => a.attributePath));
2774
- this.availableAttributes = result.items.filter(item => !selectedPaths.has(item.attributePath));
2813
+ this.availableAttributes = [
2814
+ ...this.additionalAttributes.filter(attr => !selectedPaths.has(attr.attributePath)),
2815
+ ...result.items.filter(item => !selectedPaths.has(item.attributePath))
2816
+ ];
2775
2817
  this.updateAvailableGrid();
2776
2818
  });
2777
2819
  }
@@ -2979,7 +3021,7 @@ class AttributeSelectorDialogComponent {
2979
3021
  this.updateGrids();
2980
3022
  }
2981
3023
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: AttributeSelectorDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2982
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.0", type: AttributeSelectorDialogComponent, isStandalone: true, selector: "mm-attribute-selector-dialog", ngImport: i0, template: `
3024
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: AttributeSelectorDialogComponent, isStandalone: true, selector: "mm-attribute-selector-dialog", ngImport: i0, template: `
2983
3025
  <div class="attribute-selector-container">
2984
3026
  <div class="filter-container">
2985
3027
  <kendo-textbox
@@ -3002,23 +3044,25 @@ class AttributeSelectorDialogComponent {
3002
3044
  </kendo-dropdownlist>
3003
3045
  </div>
3004
3046
 
3005
- <div class="options-container">
3006
- <input type="checkbox" kendoCheckBox
3007
- [(ngModel)]="includeNavigationProperties"
3008
- (ngModelChange)="onNavigationPropertiesChange()" />
3009
- <label class="option-label">Include Navigation Properties</label>
3047
+ @if (!hideNavigationControls) {
3048
+ <div class="options-container">
3049
+ <input type="checkbox" kendoCheckBox
3050
+ [(ngModel)]="includeNavigationProperties"
3051
+ (ngModelChange)="onNavigationPropertiesChange()" />
3052
+ <label class="option-label">Include Navigation Properties</label>
3010
3053
 
3011
- <kendo-numerictextbox
3012
- [(ngModel)]="maxDepth"
3013
- [min]="1" [max]="5" [step]="1" [format]="'n0'"
3014
- [placeholder]="'Depth'"
3015
- [spinners]="true"
3016
- [disabled]="!includeNavigationProperties"
3017
- (valueChange)="onMaxDepthChange($event)"
3018
- class="depth-input">
3019
- </kendo-numerictextbox>
3020
- <label class="option-label">Max Depth</label>
3021
- </div>
3054
+ <kendo-numerictextbox
3055
+ [(ngModel)]="maxDepth"
3056
+ [min]="1" [max]="5" [step]="1" [format]="'n0'"
3057
+ [placeholder]="'Depth'"
3058
+ [spinners]="true"
3059
+ [disabled]="!includeNavigationProperties"
3060
+ (valueChange)="onMaxDepthChange($event)"
3061
+ class="depth-input">
3062
+ </kendo-numerictextbox>
3063
+ <label class="option-label">Max Depth</label>
3064
+ </div>
3065
+ }
3022
3066
 
3023
3067
  <div class="lists-container" *ngIf="!singleSelect">
3024
3068
  <div class="list-section">
@@ -3166,23 +3210,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
3166
3210
  </kendo-dropdownlist>
3167
3211
  </div>
3168
3212
 
3169
- <div class="options-container">
3170
- <input type="checkbox" kendoCheckBox
3171
- [(ngModel)]="includeNavigationProperties"
3172
- (ngModelChange)="onNavigationPropertiesChange()" />
3173
- <label class="option-label">Include Navigation Properties</label>
3213
+ @if (!hideNavigationControls) {
3214
+ <div class="options-container">
3215
+ <input type="checkbox" kendoCheckBox
3216
+ [(ngModel)]="includeNavigationProperties"
3217
+ (ngModelChange)="onNavigationPropertiesChange()" />
3218
+ <label class="option-label">Include Navigation Properties</label>
3174
3219
 
3175
- <kendo-numerictextbox
3176
- [(ngModel)]="maxDepth"
3177
- [min]="1" [max]="5" [step]="1" [format]="'n0'"
3178
- [placeholder]="'Depth'"
3179
- [spinners]="true"
3180
- [disabled]="!includeNavigationProperties"
3181
- (valueChange)="onMaxDepthChange($event)"
3182
- class="depth-input">
3183
- </kendo-numerictextbox>
3184
- <label class="option-label">Max Depth</label>
3185
- </div>
3220
+ <kendo-numerictextbox
3221
+ [(ngModel)]="maxDepth"
3222
+ [min]="1" [max]="5" [step]="1" [format]="'n0'"
3223
+ [placeholder]="'Depth'"
3224
+ [spinners]="true"
3225
+ [disabled]="!includeNavigationProperties"
3226
+ (valueChange)="onMaxDepthChange($event)"
3227
+ class="depth-input">
3228
+ </kendo-numerictextbox>
3229
+ <label class="option-label">Max Depth</label>
3230
+ </div>
3231
+ }
3186
3232
 
3187
3233
  <div class="lists-container" *ngIf="!singleSelect">
3188
3234
  <div class="list-section">
@@ -3306,14 +3352,23 @@ class AttributeSelectorDialogService {
3306
3352
  * @param selectedAttributes Optional array of pre-selected attribute paths
3307
3353
  * @param dialogTitle Optional custom dialog title
3308
3354
  * @param singleSelect Optional flag for single-select mode
3355
+ * @param additionalAttributes Optional virtual attributes to include (e.g., Timestamp for stream data)
3356
+ * @param includeNavigationProperties Optional flag to control navigation property inclusion
3357
+ * @param maxDepth Optional max depth for navigation properties
3358
+ * @param hideNavigationControls Optional flag to hide the navigation property controls in the dialog
3309
3359
  * @returns Promise that resolves with the result containing selected attributes and confirmation status
3310
3360
  */
3311
- async openAttributeSelector(rtCkTypeId, selectedAttributes, dialogTitle, singleSelect) {
3361
+ async openAttributeSelector(rtCkTypeId, selectedAttributes, dialogTitle, singleSelect, additionalAttributes, includeNavigationProperties, maxDepth, hideNavigationControls, attributePaths) {
3312
3362
  const data = {
3313
3363
  rtCkTypeId,
3314
3364
  selectedAttributes,
3315
3365
  dialogTitle,
3316
- singleSelect
3366
+ singleSelect,
3367
+ additionalAttributes,
3368
+ includeNavigationProperties,
3369
+ maxDepth,
3370
+ hideNavigationControls,
3371
+ attributePaths
3317
3372
  };
3318
3373
  const dialogKey = singleSelect ? 'attribute-selector-single' : 'attribute-selector';
3319
3374
  const defaultWidth = singleSelect ? 550 : 1000;
@@ -3883,6 +3938,17 @@ class FieldFilterEditorComponent {
3883
3938
  * When not set, the component uses the externally provided availableAttributes input.
3884
3939
  */
3885
3940
  ckTypeId;
3941
+ /**
3942
+ * When true, hides the "Include Navigation Properties" checkbox and Max Depth controls,
3943
+ * and forces attribute loading without navigation properties.
3944
+ * Used for stream data queries which don't support navigation properties.
3945
+ */
3946
+ hideNavigationProperties = false;
3947
+ /**
3948
+ * When set, restricts the available attributes to only these attribute paths (filtered client-side after fetching).
3949
+ * Used for stream data queries to show only stream-data-enabled attributes.
3950
+ */
3951
+ attributePaths;
3886
3952
  /** Enable variable mode - allows using variables instead of literal values */
3887
3953
  enableVariables = false;
3888
3954
  /** Available variables for selection when enableVariables is true */
@@ -3905,8 +3971,13 @@ class FieldFilterEditorComponent {
3905
3971
  filtersChange = new EventEmitter();
3906
3972
  selectedKeys = [];
3907
3973
  ngOnChanges(changes) {
3908
- // Self-load attributes when ckTypeId changes
3909
- if (changes?.['ckTypeId'] && this.ckTypeId) {
3974
+ // When hideNavigationProperties changes to true, reset nav props
3975
+ if (changes?.['hideNavigationProperties'] && this.hideNavigationProperties) {
3976
+ this.includeNavigationProperties = false;
3977
+ this.maxDepth = null;
3978
+ }
3979
+ // Reload attributes when ckTypeId, hideNavigationProperties, or attributePaths changes
3980
+ if ((changes?.['ckTypeId'] || changes?.['hideNavigationProperties'] || changes?.['attributePaths']) && this.ckTypeId) {
3910
3981
  this.loadAttributesFromCkType();
3911
3982
  }
3912
3983
  this.filteredAttributeList = [...this.availableAttributes];
@@ -3950,14 +4021,19 @@ class FieldFilterEditorComponent {
3950
4021
  if (!this.ckTypeId || !this.attributeService)
3951
4022
  return;
3952
4023
  this.isLoadingAttributes = true;
4024
+ const includeNavProps = this.hideNavigationProperties ? false : this.includeNavigationProperties;
3953
4025
  try {
3954
4026
  const result = await firstValueFrom(this.attributeService.getAvailableAttributes(this.ckTypeId, undefined, // filter
3955
4027
  1000, // first
3956
4028
  undefined, // after
3957
4029
  undefined, // attributeValueType
3958
4030
  undefined, // searchTerm
3959
- this.includeNavigationProperties, this.maxDepth ?? undefined));
3960
- this.availableAttributes = result.items;
4031
+ includeNavProps, this.maxDepth ?? undefined));
4032
+ // Apply client-side attribute path restriction if set
4033
+ const allowedPathsSet = this.attributePaths ? new Set(this.attributePaths) : null;
4034
+ this.availableAttributes = allowedPathsSet
4035
+ ? result.items.filter(item => allowedPathsSet.has(item.attributePath))
4036
+ : result.items;
3961
4037
  this.filteredAttributeList = [...this.availableAttributes];
3962
4038
  this.buildAttributeTypeMap();
3963
4039
  }
@@ -4294,9 +4370,9 @@ class FieldFilterEditorComponent {
4294
4370
  return values;
4295
4371
  }
4296
4372
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: FieldFilterEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4297
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: FieldFilterEditorComponent, isStandalone: true, selector: "mm-field-filter-editor", inputs: { availableAttributes: "availableAttributes", ckTypeId: "ckTypeId", enableVariables: "enableVariables", availableVariables: "availableVariables", filters: "filters" }, outputs: { filtersChange: "filtersChange" }, usesOnChanges: true, ngImport: i0, template: `
4373
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: FieldFilterEditorComponent, isStandalone: true, selector: "mm-field-filter-editor", inputs: { availableAttributes: "availableAttributes", ckTypeId: "ckTypeId", hideNavigationProperties: "hideNavigationProperties", attributePaths: "attributePaths", enableVariables: "enableVariables", availableVariables: "availableVariables", filters: "filters" }, outputs: { filtersChange: "filtersChange" }, usesOnChanges: true, ngImport: i0, template: `
4298
4374
  <div class="field-filter-editor">
4299
- @if (ckTypeId) {
4375
+ @if (ckTypeId && !hideNavigationProperties) {
4300
4376
  <div class="attribute-options">
4301
4377
  <label class="inline-checkbox">
4302
4378
  <input type="checkbox" kendoCheckBox
@@ -4565,7 +4641,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
4565
4641
  IntlModule
4566
4642
  ], template: `
4567
4643
  <div class="field-filter-editor">
4568
- @if (ckTypeId) {
4644
+ @if (ckTypeId && !hideNavigationProperties) {
4569
4645
  <div class="attribute-options">
4570
4646
  <label class="inline-checkbox">
4571
4647
  <input type="checkbox" kendoCheckBox
@@ -4822,6 +4898,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
4822
4898
  type: Input
4823
4899
  }], ckTypeId: [{
4824
4900
  type: Input
4901
+ }], hideNavigationProperties: [{
4902
+ type: Input
4903
+ }], attributePaths: [{
4904
+ type: Input
4825
4905
  }], enableVariables: [{
4826
4906
  type: Input
4827
4907
  }], availableVariables: [{
@@ -5155,6 +5235,159 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
5155
5235
  type: Input
5156
5236
  }] } });
5157
5237
 
5238
+ class TenantSwitcherComponent {
5239
+ currentTenantId = null;
5240
+ allowedTenants = [];
5241
+ isDenied = false;
5242
+ tenantSelected = new EventEmitter();
5243
+ refreshRequested = new EventEmitter();
5244
+ refreshIcon = arrowRotateCwIcon;
5245
+ isRefreshing = false;
5246
+ anchor = null;
5247
+ popup = null;
5248
+ showPopup = false;
5249
+ onKeydown(event) {
5250
+ if (event.code === 'Escape') {
5251
+ this.showPopup = false;
5252
+ }
5253
+ }
5254
+ onDocumentClick(event) {
5255
+ if (!this.contains(event.target)) {
5256
+ this.showPopup = false;
5257
+ }
5258
+ }
5259
+ onToggle() {
5260
+ if (!this.isDenied) {
5261
+ this.showPopup = !this.showPopup;
5262
+ }
5263
+ }
5264
+ onSelectTenant(tenantId) {
5265
+ if (tenantId !== this.currentTenantId) {
5266
+ this.tenantSelected.emit(tenantId);
5267
+ }
5268
+ this.showPopup = false;
5269
+ }
5270
+ onRefresh(event) {
5271
+ event.stopPropagation();
5272
+ if (!this.isRefreshing) {
5273
+ this.isRefreshing = true;
5274
+ this.refreshRequested.emit();
5275
+ // Reset spinning after a short delay to give visual feedback
5276
+ setTimeout(() => this.isRefreshing = false, 1500);
5277
+ }
5278
+ }
5279
+ contains(target) {
5280
+ return ((this.anchor?.nativeElement.contains(target) ?? false) ||
5281
+ (this.popup?.nativeElement.contains(target) ?? false));
5282
+ }
5283
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: TenantSwitcherComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5284
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: TenantSwitcherComponent, isStandalone: true, selector: "mm-tenant-switcher", inputs: { currentTenantId: "currentTenantId", allowedTenants: "allowedTenants", isDenied: "isDenied" }, outputs: { tenantSelected: "tenantSelected", refreshRequested: "refreshRequested" }, host: { listeners: { "document:keydown": "onKeydown($event)", "document:click": "onDocumentClick($event)" } }, viewQueries: [{ propertyName: "anchor", first: true, predicate: ["badgeEl"], descendants: true, read: ElementRef }, { propertyName: "popup", first: true, predicate: ["popupContent"], descendants: true, read: ElementRef }], ngImport: i0, template: `
5285
+ @if (currentTenantId) {
5286
+ <div #badgeEl class="tenant-badge" [class.denied]="isDenied" (click)="onToggle()">
5287
+ <span class="tenant-icon">{{ isDenied ? '\u26A0' : '\u25C6' }}</span>
5288
+ <span class="tenant-name">{{ currentTenantId }}</span>
5289
+ @if (isDenied) {
5290
+ <span class="denied-label">NO ACCESS</span>
5291
+ }
5292
+ </div>
5293
+
5294
+ @if (showPopup && !isDenied) {
5295
+ <kendo-popup #popupContent [anchor]="badgeEl"
5296
+ (anchorViewportLeave)="showPopup = false">
5297
+ <div class="tenant-popup">
5298
+ <div class="tenant-popup-header">
5299
+ <span>Switch Tenant</span>
5300
+ <button kendoButton fillMode="flat" size="small" class="refresh-btn"
5301
+ [disabled]="isRefreshing"
5302
+ title="Refresh tenant list"
5303
+ (click)="onRefresh($event)">
5304
+ <kendo-svgicon [icon]="refreshIcon" size="small"
5305
+ [class.spinning]="isRefreshing"></kendo-svgicon>
5306
+ </button>
5307
+ </div>
5308
+ <ul class="tenant-list">
5309
+ @for (tenant of allowedTenants; track tenant) {
5310
+ <li class="tenant-list-item" [class.active]="tenant === currentTenantId"
5311
+ (click)="onSelectTenant(tenant)">
5312
+ <span class="tenant-list-icon">&#9670;</span>
5313
+ <span>{{ tenant }}</span>
5314
+ </li>
5315
+ }
5316
+ </ul>
5317
+ </div>
5318
+ </kendo-popup>
5319
+ }
5320
+ }
5321
+ `, isInline: true, styles: [":host{display:inline-flex;align-items:center}.tenant-badge{display:flex;align-items:center;gap:8px;padding:6px 14px;background:var(--mm-tenant-switcher-bg, var(--kendo-color-surface, transparent));border:1px solid var(--mm-tenant-switcher-border, var(--kendo-color-border, #dee2e6));border-radius:var(--mm-tenant-switcher-radius, 4px 16px 16px 4px);box-shadow:var(--mm-tenant-switcher-shadow, none);cursor:pointer;transition:all .2s ease}.tenant-badge:hover{background:var(--mm-tenant-switcher-bg-hover, var(--kendo-color-base-hover, rgba(0, 0, 0, .04)));box-shadow:var(--mm-tenant-switcher-shadow-hover, var(--mm-tenant-switcher-shadow, none))}.tenant-icon{font-size:.7rem;color:var(--mm-tenant-switcher-accent, var(--kendo-color-primary, #ff6358));animation:var(--mm-tenant-switcher-icon-animation, none)}.tenant-name{font-family:var(--mm-tenant-switcher-font, inherit);font-size:.85rem;font-weight:600;letter-spacing:1px;color:var(--mm-tenant-switcher-accent, var(--kendo-color-primary, #ff6358));text-transform:uppercase;text-shadow:var(--mm-tenant-switcher-text-shadow, none)}.denied{background:var(--mm-tenant-switcher-denied-bg, var(--mm-tenant-switcher-bg, var(--kendo-color-surface, transparent)));border-color:var(--mm-tenant-switcher-denied-border, var(--kendo-color-error, #d9534f));box-shadow:var(--mm-tenant-switcher-denied-shadow, none)}.denied .tenant-icon,.denied .tenant-name{color:var(--mm-tenant-switcher-denied-accent, var(--kendo-color-error, #d9534f));text-shadow:var(--mm-tenant-switcher-denied-text-shadow, none)}.denied-label{font-family:var(--mm-tenant-switcher-font, inherit);font-size:.55rem;font-weight:700;letter-spacing:1px;color:var(--mm-tenant-switcher-denied-accent, var(--kendo-color-error, #d9534f));background:var(--mm-tenant-switcher-denied-label-bg, color-mix(in srgb, var(--kendo-color-error, #d9534f) 15%, transparent));padding:2px 6px;border-radius:3px}.tenant-popup{min-width:220px;padding:8px 0;background:var(--kendo-color-surface, #fff);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.tenant-popup-header{display:flex;align-items:center;justify-content:space-between;padding:8px 16px;font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-subtle, #666);border-bottom:1px solid var(--kendo-color-border, #dee2e6);margin-bottom:4px}.refresh-btn{padding:2px;min-width:unset}.spinning{animation:spin .8s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.tenant-list{list-style:none;margin:0;padding:0}.tenant-list-item{display:flex;align-items:center;gap:10px;padding:8px 16px;cursor:pointer;font-size:.85rem;transition:background .15s ease}.tenant-list-item:hover{background:var(--kendo-color-base-hover, rgba(0, 0, 0, .04))}.tenant-list-item.active{color:var(--kendo-color-primary, #ff6358);font-weight:600}.tenant-list-icon{font-size:.5rem;color:var(--kendo-color-subtle, #666)}.tenant-list-item.active .tenant-list-icon{color:var(--kendo-color-primary, #ff6358)}@keyframes mm-icon-pulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.7;transform:scale(1.1)}}\n"], dependencies: [{ kind: "component", type: PopupComponent, selector: "kendo-popup", inputs: ["animate", "anchor", "anchorAlign", "collision", "popupAlign", "copyAnchorStyles", "popupClass", "positionMode", "offset", "margin"], outputs: ["anchorViewportLeave", "close", "open", "positionChange"], exportAs: ["kendo-popup"] }, { kind: "component", type: ButtonComponent, selector: "button[kendoButton]", inputs: ["arrowIcon", "toggleable", "togglable", "selected", "tabIndex", "imageUrl", "iconClass", "icon", "disabled", "size", "rounded", "fillMode", "themeColor", "svgIcon", "primary", "look"], outputs: ["selectedChange", "click"], exportAs: ["kendoButton"] }, { kind: "component", type: SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon"], exportAs: ["kendoSVGIcon"] }] });
5322
+ }
5323
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: TenantSwitcherComponent, decorators: [{
5324
+ type: Component,
5325
+ args: [{ selector: 'mm-tenant-switcher', standalone: true, imports: [
5326
+ PopupComponent,
5327
+ ButtonComponent,
5328
+ SVGIconComponent
5329
+ ], template: `
5330
+ @if (currentTenantId) {
5331
+ <div #badgeEl class="tenant-badge" [class.denied]="isDenied" (click)="onToggle()">
5332
+ <span class="tenant-icon">{{ isDenied ? '\u26A0' : '\u25C6' }}</span>
5333
+ <span class="tenant-name">{{ currentTenantId }}</span>
5334
+ @if (isDenied) {
5335
+ <span class="denied-label">NO ACCESS</span>
5336
+ }
5337
+ </div>
5338
+
5339
+ @if (showPopup && !isDenied) {
5340
+ <kendo-popup #popupContent [anchor]="badgeEl"
5341
+ (anchorViewportLeave)="showPopup = false">
5342
+ <div class="tenant-popup">
5343
+ <div class="tenant-popup-header">
5344
+ <span>Switch Tenant</span>
5345
+ <button kendoButton fillMode="flat" size="small" class="refresh-btn"
5346
+ [disabled]="isRefreshing"
5347
+ title="Refresh tenant list"
5348
+ (click)="onRefresh($event)">
5349
+ <kendo-svgicon [icon]="refreshIcon" size="small"
5350
+ [class.spinning]="isRefreshing"></kendo-svgicon>
5351
+ </button>
5352
+ </div>
5353
+ <ul class="tenant-list">
5354
+ @for (tenant of allowedTenants; track tenant) {
5355
+ <li class="tenant-list-item" [class.active]="tenant === currentTenantId"
5356
+ (click)="onSelectTenant(tenant)">
5357
+ <span class="tenant-list-icon">&#9670;</span>
5358
+ <span>{{ tenant }}</span>
5359
+ </li>
5360
+ }
5361
+ </ul>
5362
+ </div>
5363
+ </kendo-popup>
5364
+ }
5365
+ }
5366
+ `, styles: [":host{display:inline-flex;align-items:center}.tenant-badge{display:flex;align-items:center;gap:8px;padding:6px 14px;background:var(--mm-tenant-switcher-bg, var(--kendo-color-surface, transparent));border:1px solid var(--mm-tenant-switcher-border, var(--kendo-color-border, #dee2e6));border-radius:var(--mm-tenant-switcher-radius, 4px 16px 16px 4px);box-shadow:var(--mm-tenant-switcher-shadow, none);cursor:pointer;transition:all .2s ease}.tenant-badge:hover{background:var(--mm-tenant-switcher-bg-hover, var(--kendo-color-base-hover, rgba(0, 0, 0, .04)));box-shadow:var(--mm-tenant-switcher-shadow-hover, var(--mm-tenant-switcher-shadow, none))}.tenant-icon{font-size:.7rem;color:var(--mm-tenant-switcher-accent, var(--kendo-color-primary, #ff6358));animation:var(--mm-tenant-switcher-icon-animation, none)}.tenant-name{font-family:var(--mm-tenant-switcher-font, inherit);font-size:.85rem;font-weight:600;letter-spacing:1px;color:var(--mm-tenant-switcher-accent, var(--kendo-color-primary, #ff6358));text-transform:uppercase;text-shadow:var(--mm-tenant-switcher-text-shadow, none)}.denied{background:var(--mm-tenant-switcher-denied-bg, var(--mm-tenant-switcher-bg, var(--kendo-color-surface, transparent)));border-color:var(--mm-tenant-switcher-denied-border, var(--kendo-color-error, #d9534f));box-shadow:var(--mm-tenant-switcher-denied-shadow, none)}.denied .tenant-icon,.denied .tenant-name{color:var(--mm-tenant-switcher-denied-accent, var(--kendo-color-error, #d9534f));text-shadow:var(--mm-tenant-switcher-denied-text-shadow, none)}.denied-label{font-family:var(--mm-tenant-switcher-font, inherit);font-size:.55rem;font-weight:700;letter-spacing:1px;color:var(--mm-tenant-switcher-denied-accent, var(--kendo-color-error, #d9534f));background:var(--mm-tenant-switcher-denied-label-bg, color-mix(in srgb, var(--kendo-color-error, #d9534f) 15%, transparent));padding:2px 6px;border-radius:3px}.tenant-popup{min-width:220px;padding:8px 0;background:var(--kendo-color-surface, #fff);border:1px solid var(--kendo-color-border, #dee2e6);border-radius:4px}.tenant-popup-header{display:flex;align-items:center;justify-content:space-between;padding:8px 16px;font-size:.75rem;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--kendo-color-subtle, #666);border-bottom:1px solid var(--kendo-color-border, #dee2e6);margin-bottom:4px}.refresh-btn{padding:2px;min-width:unset}.spinning{animation:spin .8s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.tenant-list{list-style:none;margin:0;padding:0}.tenant-list-item{display:flex;align-items:center;gap:10px;padding:8px 16px;cursor:pointer;font-size:.85rem;transition:background .15s ease}.tenant-list-item:hover{background:var(--kendo-color-base-hover, rgba(0, 0, 0, .04))}.tenant-list-item.active{color:var(--kendo-color-primary, #ff6358);font-weight:600}.tenant-list-icon{font-size:.5rem;color:var(--kendo-color-subtle, #666)}.tenant-list-item.active .tenant-list-icon{color:var(--kendo-color-primary, #ff6358)}@keyframes mm-icon-pulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.7;transform:scale(1.1)}}\n"] }]
5367
+ }], propDecorators: { currentTenantId: [{
5368
+ type: Input
5369
+ }], allowedTenants: [{
5370
+ type: Input
5371
+ }], isDenied: [{
5372
+ type: Input
5373
+ }], tenantSelected: [{
5374
+ type: Output
5375
+ }], refreshRequested: [{
5376
+ type: Output
5377
+ }], anchor: [{
5378
+ type: ViewChild,
5379
+ args: ['badgeEl', { read: ElementRef }]
5380
+ }], popup: [{
5381
+ type: ViewChild,
5382
+ args: ['popupContent', { read: ElementRef }]
5383
+ }], onKeydown: [{
5384
+ type: HostListener,
5385
+ args: ['document:keydown', ['$event']]
5386
+ }], onDocumentClick: [{
5387
+ type: HostListener,
5388
+ args: ['document:click', ['$event']]
5389
+ }] } });
5390
+
5158
5391
  /*
5159
5392
  * Public API Surface of octo-ui
5160
5393
  */
@@ -5178,5 +5411,5 @@ function provideOctoUi() {
5178
5411
  * Generated bundle index. Do not edit.
5179
5412
  */
5180
5413
 
5181
- export { AttributeSelectorDialogComponent, AttributeSelectorDialogService, AttributeSortSelectorDialogComponent, AttributeSortSelectorDialogService, AttributeValueTypeDto, CkTypeSelectorDialogComponent, CkTypeSelectorDialogService, CkTypeSelectorInputComponent, DefaultPropertyCategory, EntityIdInfoComponent, FieldFilterEditorComponent, OctoGraphQlDataSource, OctoGraphQlHierarchyDataSource, OctoLoaderComponent, PropertyConverterService, PropertyDisplayMode, PropertyGridComponent, PropertyValueDisplayComponent, RuntimeEntityVariableDialogComponent, RuntimeEntityVariableDialogService, provideOctoUi };
5414
+ export { AttributeSelectorDialogComponent, AttributeSelectorDialogService, AttributeSortSelectorDialogComponent, AttributeSortSelectorDialogService, AttributeValueTypeDto, CkTypeSelectorDialogComponent, CkTypeSelectorDialogService, CkTypeSelectorInputComponent, DefaultPropertyCategory, EntityIdInfoComponent, FieldFilterEditorComponent, OctoGraphQlDataSource, OctoGraphQlHierarchyDataSource, OctoLoaderComponent, PropertyConverterService, PropertyDisplayMode, PropertyGridComponent, PropertyValueDisplayComponent, RuntimeEntityVariableDialogComponent, RuntimeEntityVariableDialogService, TenantSwitcherComponent, provideOctoUi };
5182
5415
  //# sourceMappingURL=meshmakers-octo-ui.mjs.map