@koobiq/components 19.8.7 → 19.8.9

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.
@@ -934,26 +934,15 @@ class KbqTreeOption extends KbqTreeNode {
934
934
  this._id = `kbq-tree-option-${uniqueIdCounter++}`;
935
935
  this.hasFocus = false;
936
936
  this.updateCheckboxState = () => {
937
- if (this.checkboxThirdState && this.isExpandable) {
938
- if (this.descendantsAllSelected()) {
939
- this.checkboxState = 'checked';
940
- }
941
- else if (this.descendantsPartiallySelected()) {
942
- this.checkboxState = 'indeterminate';
943
- }
944
- else {
945
- this.checkboxState = this.selected ? 'checked' : 'unchecked';
946
- }
947
- }
948
- else {
949
- this.checkboxState = this.selected ? 'checked' : 'unchecked';
950
- }
937
+ this.syncCheckboxState();
951
938
  this.updateParentsCheckboxState(this.data);
952
939
  };
953
940
  inject(DestroyRef).onDestroy(() => (this.isDestroyed = true));
954
941
  }
955
942
  ngAfterContentInit() {
956
- Promise.resolve().then(this.updateCheckboxState);
943
+ // Its own state only: rendering a row changes no ancestor's, and the tree recomputes every row once the data
944
+ // nodes change.
945
+ Promise.resolve().then(() => this.syncCheckboxState());
957
946
  this.value = this.tree.treeControl.getValue(this.data);
958
947
  }
959
948
  descendantsAllSelected() {
@@ -965,9 +954,13 @@ class KbqTreeOption extends KbqTreeNode {
965
954
  return descendants.some((child) => this.tree?.selectionModel.isSelected(child));
966
955
  }
967
956
  updateParentsCheckboxState(node) {
968
- this.tree.treeControl.getParents(node, []).forEach((parent) => {
969
- const parentOption = this.tree.unorderedOptions.find((option) => option.data === parent);
970
- parentOption?.updateCheckboxState();
957
+ const parents = new Set(this.tree.treeControl.getParents(node, []));
958
+ if (!parents.size)
959
+ return;
960
+ // Each ancestor reads its state off the selection rather than off its children, so recomputing it is enough.
961
+ this.tree.unorderedOptions.forEach((option) => {
962
+ if (parents.has(option.data))
963
+ option.syncCheckboxState();
971
964
  });
972
965
  }
973
966
  toggle() {
@@ -1073,8 +1066,9 @@ class KbqTreeOption extends KbqTreeNode {
1073
1066
  }
1074
1067
  markForCheck() {
1075
1068
  this.changeDetectorRef.markForCheck();
1069
+ // Its own state only: after a selection or data change the tree calls this on every option.
1076
1070
  if (this.showCheckbox) {
1077
- this.updateCheckboxState();
1071
+ this.syncCheckboxState();
1078
1072
  }
1079
1073
  }
1080
1074
  /** @docs-private */
@@ -1083,6 +1077,23 @@ class KbqTreeOption extends KbqTreeNode {
1083
1077
  return;
1084
1078
  this.focus('mouse');
1085
1079
  }
1080
+ /** Recomputes the option's own checkbox state, leaving its ancestors as they are. */
1081
+ syncCheckboxState() {
1082
+ if (this.checkboxThirdState && this.isExpandable) {
1083
+ if (this.descendantsAllSelected()) {
1084
+ this.checkboxState = 'checked';
1085
+ }
1086
+ else if (this.descendantsPartiallySelected()) {
1087
+ this.checkboxState = 'indeterminate';
1088
+ }
1089
+ else {
1090
+ this.checkboxState = this.selected ? 'checked' : 'unchecked';
1091
+ }
1092
+ }
1093
+ else {
1094
+ this.checkboxState = this.selected ? 'checked' : 'unchecked';
1095
+ }
1096
+ }
1086
1097
  /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: KbqTreeOption, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }, { token: i0.NgZone }, { token: KBQ_TREE_OPTION_PARENT_COMPONENT }], target: i0.ɵɵFactoryTarget.Component }); }
1087
1098
  /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: KbqTreeOption, isStandalone: true, selector: "kbq-tree-option", inputs: { checkboxThirdState: "checkboxThirdState", disabled: "disabled", selectable: ["selectable", "selectable", booleanAttribute], showCheckbox: "showCheckbox" }, outputs: { onSelectionChange: "onSelectionChange" }, host: { listeners: { "focusin": "focus()", "mouseenter": "onMouseenter()", "blur": "blur()", "click": "selectViaInteraction($event)", "keydown": "onKeydown($event)" }, properties: { "class.kbq-tree-option_multiple": "tree.multiple", "class.kbq-selected": "selected", "class.kbq-focused": "hasFocus", "class.kbq-disabled": "disabled", "class.kbq-action-button-focused": "actionButton?.active", "attr.id": "id", "attr.tabindex": "-1", "attr.disabled": "disabled || null" }, classAttribute: "kbq-tree-option" }, providers: [
1088
1099
  { provide: KbqTreeNode, useExisting: KbqTreeOption },
@@ -1366,6 +1377,10 @@ class KbqTreeSelection extends KbqTreeBase {
1366
1377
  this.userTabIndex = null;
1367
1378
  // this parameter used when select has a search field
1368
1379
  this.optionShouldHoldFocusOnBlur = false;
1380
+ /** `treeControl.dataNodes` the options' checkbox states were last recomputed against. */
1381
+ this.checkboxSyncedDataNodes = null;
1382
+ // Angular 19 has no `DestroyRef.destroyed`.
1383
+ this.isDestroyed = false;
1369
1384
  this.navigationChange = new EventEmitter();
1370
1385
  this.selectionChange = new EventEmitter();
1371
1386
  this.onSelectAll = new EventEmitter();
@@ -1449,6 +1464,7 @@ class KbqTreeSelection extends KbqTreeBase {
1449
1464
  }
1450
1465
  ngOnDestroy() {
1451
1466
  super.ngOnDestroy();
1467
+ this.isDestroyed = true;
1452
1468
  this.focusMonitor.stopMonitoring(this.elementRef);
1453
1469
  }
1454
1470
  focus($event) {
@@ -1618,6 +1634,21 @@ class KbqTreeSelection extends KbqTreeBase {
1618
1634
  super.renderNodeChanges(data, dataDiffer, viewContainer, parentData);
1619
1635
  this.sortedNodes = this.getSortedNodes(viewContainer);
1620
1636
  this.nodeOutlet.changeDetectorRef.detectChanges();
1637
+ this.scheduleCheckboxSync();
1638
+ }
1639
+ /**
1640
+ * Recomputes every option's checkbox state after `dataNodes` is replaced, which moves the descendants a branch
1641
+ * counts: no selection change follows to do it. A microtask later, as a flat data source assigns `dataNodes` only
1642
+ * after it emits.
1643
+ */
1644
+ scheduleCheckboxSync() {
1645
+ Promise.resolve().then(() => {
1646
+ const dataNodes = this.treeControl?.dataNodes ?? null;
1647
+ if (this.isDestroyed || dataNodes === this.checkboxSyncedDataNodes)
1648
+ return;
1649
+ this.checkboxSyncedDataNodes = dataNodes;
1650
+ this.unorderedOptions?.forEach((option) => option.markForCheck());
1651
+ });
1621
1652
  }
1622
1653
  emitNavigationEvent(option) {
1623
1654
  this.navigationChange.emit(new KbqTreeNavigationChange(this, option));