@acorex/platform 20.6.0-next.21 → 20.6.0-next.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,9 +4,9 @@ import { AXTranslationService, AXTranslationModule } from '@acorex/core/translat
4
4
  import * as i4 from '@acorex/platform/common';
5
5
  import { AXPSettingService, AXPFilterOperatorMiddlewareService, AXPEntityCommandScope, getEntityInfo, AXPRefreshEvent, AXPReloadEvent, AXPCommonSettings, AXPCleanNestedFilters, AXPWorkflowNavigateAction, AXPToastAction, AXP_SEARCH_DEFINITION_PROVIDER } from '@acorex/platform/common';
6
6
  import * as i1$2 from '@acorex/platform/core';
7
- import { AXPDeviceService, AXPBroadcastEventService, resolveActionLook, AXPExpressionEvaluatorService, AXPDistributedEventListenerService, AXPPlatformScope, extractValue, setSmart, getChangedPaths, AXPSystemActionType } from '@acorex/platform/core';
7
+ import { AXPDeviceService, AXPBroadcastEventService, resolveActionLook, AXPExpressionEvaluatorService, AXPDistributedEventListenerService, AXPPlatformScope, AXHighlightService, extractValue, setSmart, getChangedPaths, AXPSystemActionType } from '@acorex/platform/core';
8
8
  import * as i0 from '@angular/core';
9
- import { InjectionToken, inject, Injector, runInInjectionContext, Injectable, input, viewChild, signal, ElementRef, ChangeDetectionStrategy, Component, ApplicationRef, EnvironmentInjector, createComponent, computed, afterNextRender, ViewEncapsulation, ChangeDetectorRef, effect, viewChildren, linkedSignal, untracked, HostBinding, ViewChild, NgModule } from '@angular/core';
9
+ import { InjectionToken, inject, Injector, runInInjectionContext, Injectable, input, viewChild, signal, ElementRef, ChangeDetectionStrategy, Component, ApplicationRef, EnvironmentInjector, createComponent, computed, effect, Input, afterNextRender, ViewEncapsulation, ChangeDetectorRef, viewChildren, linkedSignal, untracked, HostBinding, ViewChild, NgModule } from '@angular/core';
10
10
  import { Subject, takeUntil } from 'rxjs';
11
11
  import { AXPLayoutBuilderService } from '@acorex/platform/layout/builder';
12
12
  import { merge, castArray, get, cloneDeep, set, orderBy, isNil, isEqual, isEmpty, sortBy } from 'lodash-es';
@@ -33,6 +33,7 @@ import { AXDialogService } from '@acorex/components/dialog';
33
33
  import { AXLoadingDialogService } from '@acorex/components/loading-dialog';
34
34
  import { AXPopupService } from '@acorex/components/popup';
35
35
  import { AXPlatform } from '@acorex/core/platform';
36
+ import { AXCheckBoxModule } from '@acorex/components/check-box';
36
37
  import * as i2$1 from '@acorex/components/decorators';
37
38
  import { AXDecoratorModule } from '@acorex/components/decorators';
38
39
  import { AXBasePageComponent } from '@acorex/components/page';
@@ -41,6 +42,9 @@ import { AXSearchBoxModule, AXSearchBoxComponent } from '@acorex/components/sear
41
42
  import * as i4$1 from '@acorex/components/skeleton';
42
43
  import { AXSkeletonModule } from '@acorex/components/skeleton';
43
44
  import { AXTreeViewComponent } from '@acorex/components/tree-view';
45
+ import { AXPStateMessageComponent, AXPDataSelectorService, AXPWidgetPropertyViewerComponent } from '@acorex/platform/layout/components';
46
+ import * as i1$1 from '@angular/forms';
47
+ import { FormsModule } from '@angular/forms';
44
48
  import * as i2$2 from '@acorex/components/badge';
45
49
  import { AXBadgeModule } from '@acorex/components/badge';
46
50
  import * as i5$1 from '@acorex/components/form';
@@ -49,15 +53,12 @@ import { AXValidationModule } from '@acorex/core/validation';
49
53
  import { AXP_DISABLED_PROPERTY, AXP_ALLOW_CLEAR_PROPERTY, AXP_DATA_PATH_PROPERTY, AXP_DATA_PROPERTY_GROUP, AXP_ALLOW_MULTIPLE_PROPERTY, AXP_NAME_PROPERTY, AXPFileUploaderWidgetService } from '@acorex/platform/layout/widgets';
50
54
  import * as i4$2 from '@acorex/components/dropdown';
51
55
  import { AXDropdownModule } from '@acorex/components/dropdown';
52
- import * as i1$1 from '@angular/forms';
53
- import { FormsModule } from '@angular/forms';
54
56
  import * as i7 from '@acorex/components/select-box';
55
57
  import { AXSelectBoxModule } from '@acorex/components/select-box';
56
58
  import * as i2$3 from '@acorex/components/text-box';
57
59
  import { AXTextBoxModule, AXTextBoxComponent } from '@acorex/components/text-box';
58
60
  import * as i6$1 from '@acorex/components/tag-box';
59
61
  import { AXTagBoxComponent, AXTagBoxModule } from '@acorex/components/tag-box';
60
- import { AXPDataSelectorService, AXPWidgetPropertyViewerComponent } from '@acorex/platform/layout/components';
61
62
  import { transform, isEqual as isEqual$1 } from 'lodash';
62
63
 
63
64
  function ensureListActions(ctx) {
@@ -5897,13 +5898,14 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
5897
5898
  //#region ---- Services & Dependencies ----
5898
5899
  this.categoryTreeService = inject(AXPCategoryTreeService);
5899
5900
  this.translationService = inject(AXTranslationService);
5901
+ this.highlightService = inject(AXHighlightService);
5900
5902
  //#endregion
5901
5903
  //#region ---- Properties (Set by popup service) ----
5902
5904
  this.entityKey = signal('', ...(ngDevMode ? [{ debugName: "entityKey" }] : []));
5903
5905
  this.textField = signal('title', ...(ngDevMode ? [{ debugName: "textField" }] : []));
5904
5906
  this.valueField = signal('id', ...(ngDevMode ? [{ debugName: "valueField" }] : []));
5905
5907
  this.allowMultiple = signal(false, ...(ngDevMode ? [{ debugName: "allowMultiple" }] : []));
5906
- this.selectionBehavior = signal('leaf', ...(ngDevMode ? [{ debugName: "selectionBehavior" }] : []));
5908
+ this.selectionBehavior = signal('all', ...(ngDevMode ? [{ debugName: "selectionBehavior" }] : []));
5907
5909
  this.selectedValues = signal([], ...(ngDevMode ? [{ debugName: "selectedValues" }] : []));
5908
5910
  this.searchPlaceholder = signal('@general:terms.interface.category.search.placeholder', ...(ngDevMode ? [{ debugName: "searchPlaceholder" }] : []));
5909
5911
  this.excludedNodeId = signal(undefined, ...(ngDevMode ? [{ debugName: "excludedNodeId" }] : [])); // Node ID to disable
@@ -6069,11 +6071,11 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6069
6071
  return;
6070
6072
  }
6071
6073
  // Prevent concurrent searches
6072
- if (this.isSearching() && this.currentSearchTerm !== null) {
6073
- if (this.currentSearchTerm === searchTerm) {
6074
- return;
6075
- }
6076
- }
6074
+ // if (this.isSearching() && this.currentSearchTerm !== null) {
6075
+ // if (this.currentSearchTerm === searchTerm) {
6076
+ // return;
6077
+ // }
6078
+ // }
6077
6079
  this.searchValue.set(event.value ?? '');
6078
6080
  this.currentSearchTerm = searchTerm;
6079
6081
  const treeComponent = this.tree();
@@ -6113,8 +6115,12 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6113
6115
  }
6114
6116
  if (!searchResults || searchResults.length === 0) {
6115
6117
  this.matchingNodeIds.clear();
6118
+ this.relevantNodeIds.clear();
6116
6119
  this.searchResultCount.set(0);
6117
6120
  await this.updateTranslatedMessages(0, searchTerm);
6121
+ // Clear highlighting and reload tree to show all nodes
6122
+ this.highlightService.clear();
6123
+ await treeComponent.reloadData();
6118
6124
  return;
6119
6125
  }
6120
6126
  // Store matching node IDs from search results
@@ -6167,11 +6173,25 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6167
6173
  }
6168
6174
  }
6169
6175
  }
6176
+ // Step 5: Apply highlighting after tree is rendered
6177
+ // Use setTimeout to ensure DOM is updated after tree reload
6178
+ setTimeout(() => {
6179
+ if (this.searchValue().trim()) {
6180
+ this.highlightService.highlight('ax-tree-view .ax-truncate', this.searchValue().trim());
6181
+ }
6182
+ }, 100);
6170
6183
  }
6171
6184
  catch (error) {
6172
6185
  console.error('Error searching categories:', error);
6173
6186
  this.matchingNodeIds.clear();
6187
+ this.relevantNodeIds.clear();
6174
6188
  this.searchResultCount.set(0);
6189
+ this.highlightService.clear();
6190
+ // Reload tree to clear any filters
6191
+ const treeComponent = this.tree();
6192
+ if (treeComponent) {
6193
+ await treeComponent.reloadData();
6194
+ }
6175
6195
  }
6176
6196
  finally {
6177
6197
  if (this.currentSearchTerm === searchTerm) {
@@ -6438,6 +6458,8 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6438
6458
  this.isSearching.set(false);
6439
6459
  this.matchingNodeIds.clear();
6440
6460
  this.relevantNodeIds.clear();
6461
+ // Clear highlighting
6462
+ this.highlightService.clear();
6441
6463
  const treeComponent = this.tree();
6442
6464
  if (!treeComponent) {
6443
6465
  this.expandedNodesBeforeSearch = [];
@@ -6534,6 +6556,36 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6534
6556
  isMatchingNode(nodeId) {
6535
6557
  return this.matchingNodeIds.has(nodeId);
6536
6558
  }
6559
+ /**
6560
+ * Checks if a node is currently selected
6561
+ */
6562
+ isNodeSelected(nodeId) {
6563
+ if (!nodeId || nodeId === 'all') {
6564
+ return false;
6565
+ }
6566
+ const id = String(nodeId);
6567
+ return this.selectedNodeIds().includes(id);
6568
+ }
6569
+ /**
6570
+ * Handles checkbox change event to toggle node selection
6571
+ */
6572
+ // protected handleCheckboxChange(nodeId: string | number | undefined, checked: boolean): void {
6573
+ // if (!nodeId || nodeId === 'all') {
6574
+ // return;
6575
+ // }
6576
+ // const id = String(nodeId);
6577
+ // const treeComponent = this.tree();
6578
+ // if (!treeComponent) {
6579
+ // return;
6580
+ // }
6581
+ // if (checked) {
6582
+ // // Select the node
6583
+ // treeComponent.selectNode(id);
6584
+ // } else {
6585
+ // // Deselect the node
6586
+ // treeComponent.deselectNode(id);
6587
+ // }
6588
+ // }
6537
6589
  //#endregion
6538
6590
  async updateSelectedNodes(selectedIds) {
6539
6591
  if (!selectedIds || selectedIds.length === 0) {
@@ -6905,23 +6957,18 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6905
6957
  <div class="ax-text-xs ax-text-muted ax-flex ax-items-center ax-gap-1">
6906
6958
  @if (searchResultCount() > 0) {
6907
6959
  <span>{{ resultsFoundText() }}</span>
6908
- } @else {
6909
- <span>{{ '@general:terms.interface.category.search.no-results' | translate | async }}</span>
6910
6960
  }
6911
6961
  </div>
6912
6962
  }
6913
6963
  </div>
6914
6964
  @if (showNoSearchResults()) {
6915
- <div class="ax-flex-1 ax-flex ax-items-center ax-justify-center ax-p-4">
6916
- <div class="ax-text-center ax-text-muted">
6917
- <ax-icon class="fa-light fa-search ax-text-4xl ax-mb-2 ax-opacity-50"></ax-icon>
6918
- <p class="ax-text-sm">
6919
- {{ '@general:terms.interface.category.search.no-results-found.title' | translate | async }}
6920
- </p>
6921
- <p class="ax-text-xs ax-opacity-70">
6922
- {{ '@general:terms.interface.category.search.no-results-found.description' | translate | async }}
6923
- </p>
6924
- </div>
6965
+ <div class="ax-flex-1 ax-flex ax-items-center ax-justify-center">
6966
+ <axp-state-message
6967
+ icon="fa-light fa-search"
6968
+ [title]="'@general:terms.interface.category.search.no-results-found.title'"
6969
+ [description]="'@general:terms.interface.category.search.no-results-found.description'"
6970
+ >
6971
+ </axp-state-message>
6925
6972
  </div>
6926
6973
  } @else {
6927
6974
  <div class="ax-flex-1 ax-overflow-auto ax-p-4">
@@ -6944,18 +6991,18 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6944
6991
  <ng-template #itemTemplate let-node="node" let-level="level">
6945
6992
  @if (node) {
6946
6993
  @let item = node.data || node;
6947
- @let isMatch = isMatchingNode(node.id);
6948
- <div
6949
- class="ax-flex ax-items-center ax-gap-2 ax-w-full ax-overflow-hidden ax-p-2 ax-pe-0"
6950
- [class.ax-bg-secondary-100]="isMatch"
6951
- [class.ax-rounded]="isMatch"
6952
- [class.ax-px-1]="isMatch"
6953
- >
6994
+ <div class="ax-flex ax-items-center ax-gap-2 ax-w-full ax-overflow-hidden ax-p-2 ax-pe-0">
6995
+ <!-- <ax-check-box
6996
+ [ngModel]="isNodeSelected(node.id)"
6997
+ (onValueChanged)="handleCheckboxChange(node.id, $event.value)"
6998
+ (click)="$event.stopPropagation()"
6999
+ [disabled]="node.disabled || node.id === 'all'"
7000
+ ></ax-check-box> -->
6954
7001
  <ax-icon
6955
7002
  class="fas fa-folder"
6956
7003
  [style.color]="item.color ?? 'rgba(var(--ax-sys-color-warning-500), 1)'"
6957
7004
  ></ax-icon>
6958
- <span class="ax-truncate" [class.ax-font-semibold]="isMatch">{{ node.title }}</span>
7005
+ <span class="ax-truncate">{{ node.title }}</span>
6959
7006
  </div>
6960
7007
  }
6961
7008
  </ng-template>
@@ -6963,8 +7010,13 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6963
7010
  }
6964
7011
  </div>
6965
7012
  } @else {
6966
- <div class="ax-p-4 ax-text-center ax-text-muted">
6967
- <span>---</span>
7013
+ <div class="ax-flex-1 ax-flex ax-items-center ax-justify-center">
7014
+ <axp-state-message
7015
+ icon="fa-light fa-folder-open"
7016
+ [title]="'@general:terms.interface.category.search.no-records.title'"
7017
+ [description]="'@general:terms.interface.category.search.no-records.description'"
7018
+ >
7019
+ </axp-state-message>
6968
7020
  </div>
6969
7021
  }
6970
7022
  </div>
@@ -6979,11 +7031,12 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6979
7031
  look="solid"
6980
7032
  color="primary"
6981
7033
  [text]="'@general:actions.ok.title' | translate | async"
7034
+ [disabled]="selectedNodeIds().length === 0"
6982
7035
  (onClick)="onConfirm()"
6983
7036
  ></ax-button>
6984
7037
  </ax-suffix>
6985
7038
  </ax-footer>
6986
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i3.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2$1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXSearchBoxModule }, { kind: "component", type: i3$2.AXSearchBoxComponent, selector: "ax-search-box", inputs: ["disabled", "readonly", "tabIndex", "placeholder", "value", "state", "name", "id", "look", "class", "delayTime", "type", "autoSearch"], outputs: ["valueChange", "stateChange", "onValueChanged", "onBlur", "onFocus", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i4$1.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "component", type: AXTreeViewComponent, selector: "ax-tree-view", inputs: ["datasource", "selectMode", "selectionBehavior", "dragArea", "dragBehavior", "showIcons", "showChildrenBadge", "expandedIcon", "collapsedIcon", "indentSize", "look", "nodeTemplate", "idField", "titleField", "tooltipField", "iconField", "expandedField", "selectedField", "indeterminateField", "disabledField", "hiddenField", "childrenField", "childrenCountField", "dataField", "inheritDisabled", "expandOnDoubleClick"], outputs: ["datasourceChange", "onBeforeDrop", "onNodeToggle", "onNodeSelect", "onNodeDoubleClick", "onNodeClick", "onSelectionChange", "onOrderChange", "onMoveChange", "onItemsChange"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
7039
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i3.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXCheckBoxModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2$1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXSearchBoxModule }, { kind: "component", type: i3$2.AXSearchBoxComponent, selector: "ax-search-box", inputs: ["disabled", "readonly", "tabIndex", "placeholder", "value", "state", "name", "id", "look", "class", "delayTime", "type", "autoSearch"], outputs: ["valueChange", "stateChange", "onValueChanged", "onBlur", "onFocus", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i4$1.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "component", type: AXTreeViewComponent, selector: "ax-tree-view", inputs: ["datasource", "selectMode", "selectionBehavior", "dragArea", "dragBehavior", "showIcons", "showChildrenBadge", "expandedIcon", "collapsedIcon", "indentSize", "look", "nodeTemplate", "idField", "titleField", "tooltipField", "iconField", "expandedField", "selectedField", "indeterminateField", "disabledField", "hiddenField", "childrenField", "childrenCountField", "dataField", "inheritDisabled", "expandOnDoubleClick"], outputs: ["datasourceChange", "onBeforeDrop", "onNodeToggle", "onNodeSelect", "onNodeDoubleClick", "onNodeClick", "onSelectionChange", "onOrderChange", "onMoveChange", "onItemsChange"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "variant"] }, { kind: "ngmodule", type: FormsModule }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6987
7040
  }
6988
7041
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPEntityCategoryTreeSelectorComponent, decorators: [{
6989
7042
  type: Component,
@@ -7014,23 +7067,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
7014
7067
  <div class="ax-text-xs ax-text-muted ax-flex ax-items-center ax-gap-1">
7015
7068
  @if (searchResultCount() > 0) {
7016
7069
  <span>{{ resultsFoundText() }}</span>
7017
- } @else {
7018
- <span>{{ '@general:terms.interface.category.search.no-results' | translate | async }}</span>
7019
7070
  }
7020
7071
  </div>
7021
7072
  }
7022
7073
  </div>
7023
7074
  @if (showNoSearchResults()) {
7024
- <div class="ax-flex-1 ax-flex ax-items-center ax-justify-center ax-p-4">
7025
- <div class="ax-text-center ax-text-muted">
7026
- <ax-icon class="fa-light fa-search ax-text-4xl ax-mb-2 ax-opacity-50"></ax-icon>
7027
- <p class="ax-text-sm">
7028
- {{ '@general:terms.interface.category.search.no-results-found.title' | translate | async }}
7029
- </p>
7030
- <p class="ax-text-xs ax-opacity-70">
7031
- {{ '@general:terms.interface.category.search.no-results-found.description' | translate | async }}
7032
- </p>
7033
- </div>
7075
+ <div class="ax-flex-1 ax-flex ax-items-center ax-justify-center">
7076
+ <axp-state-message
7077
+ icon="fa-light fa-search"
7078
+ [title]="'@general:terms.interface.category.search.no-results-found.title'"
7079
+ [description]="'@general:terms.interface.category.search.no-results-found.description'"
7080
+ >
7081
+ </axp-state-message>
7034
7082
  </div>
7035
7083
  } @else {
7036
7084
  <div class="ax-flex-1 ax-overflow-auto ax-p-4">
@@ -7053,18 +7101,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
7053
7101
  <ng-template #itemTemplate let-node="node" let-level="level">
7054
7102
  @if (node) {
7055
7103
  @let item = node.data || node;
7056
- @let isMatch = isMatchingNode(node.id);
7057
- <div
7058
- class="ax-flex ax-items-center ax-gap-2 ax-w-full ax-overflow-hidden ax-p-2 ax-pe-0"
7059
- [class.ax-bg-secondary-100]="isMatch"
7060
- [class.ax-rounded]="isMatch"
7061
- [class.ax-px-1]="isMatch"
7062
- >
7104
+ <div class="ax-flex ax-items-center ax-gap-2 ax-w-full ax-overflow-hidden ax-p-2 ax-pe-0">
7105
+ <!-- <ax-check-box
7106
+ [ngModel]="isNodeSelected(node.id)"
7107
+ (onValueChanged)="handleCheckboxChange(node.id, $event.value)"
7108
+ (click)="$event.stopPropagation()"
7109
+ [disabled]="node.disabled || node.id === 'all'"
7110
+ ></ax-check-box> -->
7063
7111
  <ax-icon
7064
7112
  class="fas fa-folder"
7065
7113
  [style.color]="item.color ?? 'rgba(var(--ax-sys-color-warning-500), 1)'"
7066
7114
  ></ax-icon>
7067
- <span class="ax-truncate" [class.ax-font-semibold]="isMatch">{{ node.title }}</span>
7115
+ <span class="ax-truncate">{{ node.title }}</span>
7068
7116
  </div>
7069
7117
  }
7070
7118
  </ng-template>
@@ -7072,8 +7120,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
7072
7120
  }
7073
7121
  </div>
7074
7122
  } @else {
7075
- <div class="ax-p-4 ax-text-center ax-text-muted">
7076
- <span>---</span>
7123
+ <div class="ax-flex-1 ax-flex ax-items-center ax-justify-center">
7124
+ <axp-state-message
7125
+ icon="fa-light fa-folder-open"
7126
+ [title]="'@general:terms.interface.category.search.no-records.title'"
7127
+ [description]="'@general:terms.interface.category.search.no-records.description'"
7128
+ >
7129
+ </axp-state-message>
7077
7130
  </div>
7078
7131
  }
7079
7132
  </div>
@@ -7088,6 +7141,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
7088
7141
  look="solid"
7089
7142
  color="primary"
7090
7143
  [text]="'@general:actions.ok.title' | translate | async"
7144
+ [disabled]="selectedNodeIds().length === 0"
7091
7145
  (onClick)="onConfirm()"
7092
7146
  ></ax-button>
7093
7147
  </ax-suffix>
@@ -7097,11 +7151,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
7097
7151
  imports: [
7098
7152
  CommonModule,
7099
7153
  AXButtonModule,
7154
+ AXCheckBoxModule,
7100
7155
  AXDecoratorModule,
7101
7156
  AXSearchBoxModule,
7102
7157
  AXSkeletonModule,
7103
7158
  AXTreeViewComponent,
7104
7159
  AXTranslationModule,
7160
+ AXPStateMessageComponent,
7161
+ FormsModule,
7105
7162
  ],
7106
7163
  }]
7107
7164
  }], propDecorators: { tree: [{ type: i0.ViewChild, args: ['tree', { isSignal: true }] }] } });
@@ -7109,177 +7166,275 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
7109
7166
  class AXPEntityCategoryWidgetColumnComponent extends AXPColumnWidgetComponent {
7110
7167
  constructor() {
7111
7168
  super(...arguments);
7112
- //#region ---- Dependencies ----
7169
+ //#region ---- Services & Dependencies ----
7113
7170
  this.entityDetailPopoverService = inject(AXPEntityDetailPopoverService);
7114
7171
  this.formatService = inject(AXFormatService);
7172
+ this.entityResolver = inject(AXPEntityResolver);
7115
7173
  //#endregion
7116
- //#region ---- View Children ----
7117
- this.moreButton = viewChild('moreButton', ...(ngDevMode ? [{ debugName: "moreButton" }] : []));
7118
- this.morePopover = viewChild('morePopover', ...(ngDevMode ? [{ debugName: "morePopover" }] : []));
7174
+ //#region ---- Inputs ----
7175
+ this.rawValueSignal = signal(null, ...(ngDevMode ? [{ debugName: "rawValueSignal" }] : []));
7119
7176
  //#endregion
7120
- //#region ---- Properties ----
7121
- this.host = inject(ElementRef);
7122
- this.valueField = this.options['valueField'] ?? 'id';
7123
- this.textField = this.options['textField'] ?? 'title';
7124
- this.entity = this.options['entity'] ?? '';
7125
- this.columnName = this.options['columnName'] ?? '';
7126
- this.maxVisible = this.options['maxVisible'] ?? 2;
7177
+ //#region ---- Computed Properties ----
7178
+ this.entity = computed(() => this.options['entity'] ?? '', ...(ngDevMode ? [{ debugName: "entity" }] : []));
7179
+ this.valueField = computed(() => this.options['valueField'] ?? 'id', ...(ngDevMode ? [{ debugName: "valueField" }] : []));
7180
+ this.textField = computed(() => this.options['textField'] ?? 'title', ...(ngDevMode ? [{ debugName: "textField" }] : []));
7181
+ this.columnName = computed(() => this.options['columnName'] ?? '', ...(ngDevMode ? [{ debugName: "columnName" }] : []));
7182
+ this.defaultTextField = computed(() => {
7183
+ const textField = this.entityDef()?.formats.lookup ?? this.entityDef()?.properties.find((c) => c.name != 'id')?.name ?? 'title';
7184
+ return textField;
7185
+ }, ...(ngDevMode ? [{ debugName: "defaultTextField" }] : []));
7186
+ this.displayField = computed(() => {
7187
+ if (this.textField()) {
7188
+ return this.textField();
7189
+ }
7190
+ return this.defaultTextField();
7191
+ }, ...(ngDevMode ? [{ debugName: "displayField" }] : []));
7127
7192
  //#endregion
7128
- //#region ---- Signals ----
7129
- this.isMorePopoverOpen = signal(false, ...(ngDevMode ? [{ debugName: "isMorePopoverOpen" }] : []));
7193
+ //#region ---- Component State ----
7194
+ this.host = inject(ElementRef);
7195
+ this.entityDef = signal(null, ...(ngDevMode ? [{ debugName: "entityDef" }] : []));
7196
+ this.displayItems = signal([], ...(ngDevMode ? [{ debugName: "displayItems" }] : []));
7130
7197
  this.selectedItemIndex = signal(-1, ...(ngDevMode ? [{ debugName: "selectedItemIndex" }] : []));
7131
7198
  //#endregion
7132
- //#region ---- Computed Properties ----
7133
- this.displayItems = computed(() => isNil(this.rawValue)
7134
- ? []
7135
- : castArray(this.rawValue)
7136
- .map((item) => this.extractItem(item))
7137
- .filter((c) => c != null), ...(ngDevMode ? [{ debugName: "displayItems" }] : []));
7138
- this.allItems = computed(() => this.displayItems(), ...(ngDevMode ? [{ debugName: "allItems" }] : []));
7139
- this.visibleItems = computed(() => {
7140
- const items = this.allItems();
7141
- return items.slice(0, this.maxVisible);
7142
- }, ...(ngDevMode ? [{ debugName: "visibleItems" }] : []));
7143
- this.remainingItems = computed(() => {
7144
- const items = this.allItems();
7145
- return items.slice(this.maxVisible);
7146
- }, ...(ngDevMode ? [{ debugName: "remainingItems" }] : []));
7147
- this.hasMoreItems = computed(() => {
7148
- return this.allItems().length > this.maxVisible;
7149
- }, ...(ngDevMode ? [{ debugName: "hasMoreItems" }] : []));
7150
- this.remainingItemsCount = computed(() => {
7151
- return this.allItems().length - this.maxVisible;
7152
- }, ...(ngDevMode ? [{ debugName: "remainingItemsCount" }] : []));
7199
+ //#region ---- Effects ----
7200
+ this.efEntity = effect(async () => {
7201
+ const entityKey = this.entity();
7202
+ if (!entityKey) {
7203
+ return;
7204
+ }
7205
+ const [module, entity] = entityKey.split('.');
7206
+ if (!module || !entity) {
7207
+ return;
7208
+ }
7209
+ this.entityDef.set(await this.entityResolver.get(module, entity));
7210
+ }, ...(ngDevMode ? [{ debugName: "efEntity" }] : []));
7211
+ this.efDisplay = effect(async () => {
7212
+ const value = this.rawValueSignal();
7213
+ const entity = this.entityDef();
7214
+ // Wait for entity definition to be loaded before processing value
7215
+ if (!entity) {
7216
+ this.displayItems.set([]);
7217
+ return;
7218
+ }
7219
+ if (isNil(value)) {
7220
+ this.displayItems.set([]);
7221
+ }
7222
+ else {
7223
+ const items = castArray(value);
7224
+ const itemsWithPaths = await Promise.all(items.map((item) => this.extractItemWithPath(item)));
7225
+ this.displayItems.set(itemsWithPaths.filter((c) => c != null));
7226
+ }
7227
+ }, ...(ngDevMode ? [{ debugName: "efDisplay" }] : []));
7228
+ //#endregion
7229
+ //#region ---- Computed Properties (Derived) ----
7230
+ this.visibleItems = computed(() => this.displayItems(), ...(ngDevMode ? [{ debugName: "visibleItems" }] : []));
7231
+ }
7232
+ set rawValueInput(value) {
7233
+ this.rawValueSignal.set(value);
7153
7234
  }
7154
7235
  //#endregion
7155
7236
  //#region ---- Public Methods ----
7156
- showMoreItems() {
7157
- this.entityDetailPopoverService.hide();
7158
- this.openMorePopover();
7159
- }
7160
- onMorePopoverOpenChange(event) {
7161
- this.isMorePopoverOpen.set(event);
7162
- }
7163
7237
  async showItemDetail(item, index) {
7164
- const columnData = this.rowData[this.columnName];
7238
+ const columnData = this.rowData[this.columnName()];
7165
7239
  const id = Array.isArray(columnData) ? columnData[index] : columnData;
7166
7240
  this.selectedItemIndex.set(index);
7167
- this.closeMorePopover();
7168
- if (this.entity && id) {
7241
+ const entityKey = this.entity();
7242
+ if (entityKey && id) {
7169
7243
  await this.entityDetailPopoverService.show(this.host, {
7170
- entity: this.entity,
7244
+ entity: entityKey,
7171
7245
  id: id,
7172
- textField: this.textField,
7173
- valueField: this.valueField,
7246
+ textField: this.textField(),
7247
+ valueField: this.valueField(),
7174
7248
  item,
7175
7249
  });
7176
7250
  }
7177
7251
  }
7178
7252
  handleItemClick(index) {
7179
- const items = this.allItems();
7253
+ const items = this.visibleItems();
7180
7254
  if (index < items.length) {
7181
7255
  const item = items[index];
7182
7256
  this.showItemDetail(item, index);
7183
7257
  }
7184
7258
  }
7185
- //#endregion
7186
- //#region ---- Private Methods ----
7187
- openMorePopover() {
7188
- if (this.morePopover() && this.moreButton()) {
7189
- this.morePopover().target = this.moreButton().nativeElement;
7190
- this.morePopover().open();
7191
- this.isMorePopoverOpen.set(true);
7259
+ getItemPath(item) {
7260
+ if (!item) {
7261
+ return [];
7262
+ }
7263
+ // If path is available as array, return it
7264
+ if (item.path && Array.isArray(item.path) && item.path.length > 0) {
7265
+ return item.path;
7192
7266
  }
7267
+ // Fall back to display field if no path
7268
+ const displayValue = get(item, this.displayField());
7269
+ return displayValue ? [String(displayValue)] : [];
7193
7270
  }
7194
- closeMorePopover() {
7195
- if (this.morePopover()) {
7196
- this.morePopover().close();
7197
- this.isMorePopoverOpen.set(false);
7271
+ hasParent(item) {
7272
+ const path = this.getItemPath(item);
7273
+ return path.length > 1;
7274
+ }
7275
+ getItemText(item) {
7276
+ const path = this.getItemPath(item);
7277
+ if (path.length === 0) {
7278
+ return '';
7198
7279
  }
7280
+ // Return the last item in the path (the actual node)
7281
+ return path[path.length - 1];
7199
7282
  }
7200
- extractItem(item) {
7283
+ getItemId(item) {
7284
+ if (!item) {
7285
+ return '';
7286
+ }
7287
+ return String(get(item, this.valueField()) ?? '');
7288
+ }
7289
+ //#endregion
7290
+ //#region ---- Private Methods ----
7291
+ async extractItemWithPath(item) {
7201
7292
  if (isNil(item)) {
7202
7293
  return null;
7203
7294
  }
7295
+ let itemObj;
7204
7296
  if (typeof item === 'object') {
7205
- return item;
7297
+ itemObj = item;
7206
7298
  }
7207
- return {
7208
- [this.valueField]: item,
7209
- [this.textField]: item,
7210
- };
7299
+ else {
7300
+ // If item is just an ID, fetch the full item
7301
+ const byKey = this.entityDef()?.queries.byKey?.execute;
7302
+ if (byKey) {
7303
+ try {
7304
+ itemObj = await byKey(item);
7305
+ }
7306
+ catch (error) {
7307
+ console.error('Error fetching item:', error);
7308
+ return null;
7309
+ }
7310
+ }
7311
+ else {
7312
+ itemObj = {
7313
+ [this.valueField()]: item,
7314
+ [this.textField()]: item,
7315
+ };
7316
+ }
7317
+ }
7318
+ // If item already has a path array, return it as is
7319
+ if (itemObj.path && Array.isArray(itemObj.path) && itemObj.path.length > 0) {
7320
+ return itemObj;
7321
+ }
7322
+ // Calculate path for the item
7323
+ return await this.calculateItemPath(itemObj);
7211
7324
  }
7212
- getDisplayText(item) {
7213
- if (!item) {
7214
- return '';
7325
+ /**
7326
+ * Calculate the full path from root to the item.
7327
+ * Returns an array of strings like ["C", "B"] for item B under parent C.
7328
+ */
7329
+ async calculateItemPath(item) {
7330
+ if (!item || !this.entityDef()) {
7331
+ return item;
7332
+ }
7333
+ // If item already has a path array, return it as is
7334
+ if (item.path && Array.isArray(item.path) && item.path.length > 0) {
7335
+ return item;
7215
7336
  }
7216
- return item?.[this.textField] ?? String(item);
7337
+ const parentKey = this.entityDef()?.parentKey;
7338
+ if (!parentKey) {
7339
+ // No parent key means flat structure, return item with just its title as path array
7340
+ const title = get(item, this.displayField()) ?? '';
7341
+ return { ...item, path: title ? [title] : [] };
7342
+ }
7343
+ const valueField = this.valueField();
7344
+ const textField = this.displayField();
7345
+ const byKey = this.entityDef()?.queries.byKey?.execute;
7346
+ if (!byKey) {
7347
+ return item;
7348
+ }
7349
+ const path = [];
7350
+ let currentItem = item;
7351
+ const visitedIds = new Set(); // Prevent infinite loops
7352
+ // Build path by traversing up the parent chain
7353
+ while (currentItem) {
7354
+ const currentId = String(get(currentItem, valueField) ?? '');
7355
+ if (!currentId || visitedIds.has(currentId)) {
7356
+ break; // Prevent infinite loops
7357
+ }
7358
+ visitedIds.add(currentId);
7359
+ const title = String(get(currentItem, textField) ?? '');
7360
+ if (title) {
7361
+ path.unshift(title); // Add to beginning to maintain hierarchy order
7362
+ }
7363
+ // Get parent ID from current item
7364
+ const parentId = get(currentItem, parentKey);
7365
+ if (!parentId || parentId === 'all' || parentId === currentId) {
7366
+ break; // Reached root or invalid parent
7367
+ }
7368
+ try {
7369
+ // Fetch parent item
7370
+ const parentItem = await byKey(parentId);
7371
+ if (!parentItem) {
7372
+ break; // Parent not found, stop traversal
7373
+ }
7374
+ currentItem = parentItem;
7375
+ }
7376
+ catch (error) {
7377
+ console.error('Error fetching parent item:', error);
7378
+ break;
7379
+ }
7380
+ }
7381
+ // Return item with path array, or just the item title if no path
7382
+ const pathArray = path.length > 0 ? path : get(item, textField) ? [String(get(item, textField))] : [];
7383
+ return { ...item, path: pathArray };
7217
7384
  }
7218
7385
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPEntityCategoryWidgetColumnComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
7219
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", type: AXPEntityCategoryWidgetColumnComponent, isStandalone: true, selector: "axp-entity-category-widget-column", inputs: { rawValue: "rawValue", rowData: "rowData" }, viewQueries: [{ propertyName: "moreButton", first: true, predicate: ["moreButton"], descendants: true, isSignal: true }, { propertyName: "morePopover", first: true, predicate: ["morePopover"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: `
7220
- <div class="ax-flex ax-items-center ax-gap-1 ax-flex-wrap">
7221
- @for (item of visibleItems(); track $index) {
7222
- <ax-badge
7223
- class="ax-p-0.5 ax-cursor-pointer"
7224
- [text]="getDisplayText(item)"
7225
- (click)="handleItemClick($index)"
7226
- ></ax-badge>
7227
- }
7228
- @if (hasMoreItems()) {
7229
- <span class="ax-cursor-pointer ax-px-1" (click)="showMoreItems()" #moreButton>
7230
- <i class="fas fa-ellipsis-h"></i>
7231
- </span>
7232
- <ax-popover #morePopover [openOn]="'manual'" (openChange)="onMorePopoverOpenChange($event)">
7233
- <div class="ax-p-2 ax-flex ax-flex-col ax-gap-1">
7234
- @for (item of remainingItems(); track $index) {
7235
- <ax-badge
7236
- class="ax-p-0.5 ax-cursor-pointer"
7237
- [text]="getDisplayText(item)"
7238
- (click)="handleItemClick(visibleItems().length + $index)"
7239
- ></ax-badge>
7240
- }
7241
- </div>
7242
- </ax-popover>
7386
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", type: AXPEntityCategoryWidgetColumnComponent, isStandalone: true, selector: "axp-entity-category-widget-column", inputs: { rawValue: "rawValue", rowData: "rowData", rawValueInput: ["rawValue", "rawValueInput"] }, host: { classAttribute: "ax-w-full" }, usesInheritance: true, ngImport: i0, template: `
7387
+ <div class="ax-flex ax-flex-col ax-gap-1.5">
7388
+ @for (item of visibleItems(); track getItemId(item)) {
7389
+ <div class=" ax-cursor-pointer" (click)="handleItemClick($index)">
7390
+ @if (hasParent(item)) {
7391
+ <ax-badge [color]="'primary'" [look]="'twotone'" class="ax-p-0.5" [text]="getItemText(item)">
7392
+ <ax-prefix>
7393
+ <i class="fas fa-ellipsis-h ax-text-neutral-400 ax-text-xs ax-ps-1"></i>
7394
+ <i class="fas fa-chevron-right ax-text-neutral-400 ax-text-xs ax-px-1"></i>
7395
+ </ax-prefix>
7396
+ </ax-badge>
7397
+ } @else {
7398
+ <ax-badge [color]="'primary'" [look]="'twotone'" class="ax-p-0.5" [text]="getItemText(item)"> </ax-badge>
7399
+ }
7400
+ </div>
7243
7401
  }
7244
7402
  </div>
7245
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i2$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "ngmodule", type: AXPopoverModule }, { kind: "component", type: i2.AXPopoverComponent, selector: "ax-popover", inputs: ["width", "disabled", "offsetX", "offsetY", "target", "placement", "content", "openOn", "closeOn", "hasBackdrop", "openAfter", "closeAfter", "backdropClass", "panelClass", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
7403
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i2$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
7246
7404
  }
7247
7405
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPEntityCategoryWidgetColumnComponent, decorators: [{
7248
7406
  type: Component,
7249
7407
  args: [{
7250
7408
  selector: 'axp-entity-category-widget-column',
7409
+ host: {
7410
+ class: 'ax-w-full',
7411
+ },
7251
7412
  template: `
7252
- <div class="ax-flex ax-items-center ax-gap-1 ax-flex-wrap">
7253
- @for (item of visibleItems(); track $index) {
7254
- <ax-badge
7255
- class="ax-p-0.5 ax-cursor-pointer"
7256
- [text]="getDisplayText(item)"
7257
- (click)="handleItemClick($index)"
7258
- ></ax-badge>
7259
- }
7260
- @if (hasMoreItems()) {
7261
- <span class="ax-cursor-pointer ax-px-1" (click)="showMoreItems()" #moreButton>
7262
- <i class="fas fa-ellipsis-h"></i>
7263
- </span>
7264
- <ax-popover #morePopover [openOn]="'manual'" (openChange)="onMorePopoverOpenChange($event)">
7265
- <div class="ax-p-2 ax-flex ax-flex-col ax-gap-1">
7266
- @for (item of remainingItems(); track $index) {
7267
- <ax-badge
7268
- class="ax-p-0.5 ax-cursor-pointer"
7269
- [text]="getDisplayText(item)"
7270
- (click)="handleItemClick(visibleItems().length + $index)"
7271
- ></ax-badge>
7272
- }
7273
- </div>
7274
- </ax-popover>
7413
+ <div class="ax-flex ax-flex-col ax-gap-1.5">
7414
+ @for (item of visibleItems(); track getItemId(item)) {
7415
+ <div class=" ax-cursor-pointer" (click)="handleItemClick($index)">
7416
+ @if (hasParent(item)) {
7417
+ <ax-badge [color]="'primary'" [look]="'twotone'" class="ax-p-0.5" [text]="getItemText(item)">
7418
+ <ax-prefix>
7419
+ <i class="fas fa-ellipsis-h ax-text-neutral-400 ax-text-xs ax-ps-1"></i>
7420
+ <i class="fas fa-chevron-right ax-text-neutral-400 ax-text-xs ax-px-1"></i>
7421
+ </ax-prefix>
7422
+ </ax-badge>
7423
+ } @else {
7424
+ <ax-badge [color]="'primary'" [look]="'twotone'" class="ax-p-0.5" [text]="getItemText(item)"> </ax-badge>
7425
+ }
7426
+ </div>
7275
7427
  }
7276
7428
  </div>
7277
7429
  `,
7278
7430
  changeDetection: ChangeDetectionStrategy.OnPush,
7279
- imports: [CommonModule, AXBadgeModule, AXButtonModule, AXPopoverModule],
7431
+ imports: [CommonModule, AXBadgeModule, AXDecoratorModule],
7280
7432
  inputs: ['rawValue', 'rowData'],
7281
7433
  }]
7282
- }], propDecorators: { moreButton: [{ type: i0.ViewChild, args: ['moreButton', { isSignal: true }] }], morePopover: [{ type: i0.ViewChild, args: ['morePopover', { isSignal: true }] }] } });
7434
+ }], propDecorators: { rawValueInput: [{
7435
+ type: Input,
7436
+ args: ['rawValue']
7437
+ }] } });
7283
7438
 
7284
7439
  var entityCategoryWidgetColumn_component = /*#__PURE__*/Object.freeze({
7285
7440
  __proto__: null,