@toolbox-web/grid-angular 0.18.1 → 0.18.3

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.
@@ -1789,40 +1789,33 @@ class GridAdapter {
1789
1789
  this.editorViewRefs.push(viewRef);
1790
1790
  // Trigger change detection
1791
1791
  viewRef.detectChanges();
1792
- // Get the first root node (the component's host element)
1793
- const rootNode = viewRef.rootNodes[0];
1792
+ // Use a stable wrapper so Angular's rootNodes (which may include comment
1793
+ // placeholders from <ng-container>) are always inside one element node.
1794
+ const container = document.createElement('span');
1795
+ container.style.display = 'contents';
1796
+ syncRootNodes(viewRef, container);
1794
1797
  // Auto-wire: Listen for commit/cancel events on the rendered component.
1795
1798
  // This allows components to just emit (commit) and (cancel) without
1796
1799
  // requiring explicit template bindings like (commit)="onCommit($event)".
1797
- if (rootNode && rootNode.addEventListener) {
1798
- rootNode.addEventListener('commit', (e) => {
1799
- const customEvent = e;
1800
- ctx.commit(customEvent.detail);
1801
- });
1802
- rootNode.addEventListener('cancel', () => {
1803
- ctx.cancel();
1804
- });
1805
- }
1806
- // Auto-update editor when value changes externally (e.g., via updateRow cascade).
1807
- // This keeps Angular template editors in sync without manual DOM patching.
1800
+ container.addEventListener('commit', (e) => {
1801
+ const customEvent = e;
1802
+ ctx.commit(customEvent.detail);
1803
+ });
1804
+ container.addEventListener('cancel', () => {
1805
+ ctx.cancel();
1806
+ });
1807
+ // Auto-update editor when value changes externally (e.g., via updateRow cascade
1808
+ // or Escape-revert in grid mode). Update the template context and run synchronous
1809
+ // detectChanges() Angular's own bindings and control flow (@for, @if) handle
1810
+ // re-rendering regardless of editor type (inputs, chips, contenteditable, etc.).
1808
1811
  ctx.onValueChange?.((newVal) => {
1809
1812
  context.$implicit = newVal;
1810
1813
  context.value = newVal;
1811
- viewRef.markForCheck();
1812
- // Also patch raw DOM inputs as a fallback for editors that don't bind to context
1813
- if (rootNode) {
1814
- const input = rootNode.querySelector?.('input,textarea,select');
1815
- if (input) {
1816
- if (input instanceof HTMLInputElement && input.type === 'checkbox') {
1817
- input.checked = !!newVal;
1818
- }
1819
- else {
1820
- input.value = String(newVal ?? '');
1821
- }
1822
- }
1823
- }
1814
+ viewRef.detectChanges();
1815
+ // Re-sync rootNodes in case Angular control flow changed them
1816
+ syncRootNodes(viewRef, container);
1824
1817
  });
1825
- return rootNode;
1818
+ return container;
1826
1819
  };
1827
1820
  }
1828
1821
  /**
@@ -2074,13 +2067,13 @@ class GridAdapter {
2074
2067
  column: ctx.column,
2075
2068
  }, true);
2076
2069
  wireEditorCallbacks(hostElement, componentRef.instance, (value) => ctx.commit(value), () => ctx.cancel());
2077
- // Auto-update editor when value changes externally (e.g., via updateRow cascade).
2078
- // This keeps Angular component editors in sync without manual DOM patching.
2070
+ // Auto-update editor when value changes externally (e.g., via updateRow cascade
2071
+ // or Escape-revert). Update the component input and run detectChanges()
2072
+ // the component's own template handles rendering regardless of editor type.
2079
2073
  ctx.onValueChange?.((newVal) => {
2080
2074
  try {
2081
2075
  // Notify the editor so it can clear stale internal state (e.g., searchText
2082
- // in autocomplete editors) before the value input updates. This ensures the
2083
- // template reads fresh state during the synchronous detectChanges() below.
2076
+ // in autocomplete editors) before the value input updates.
2084
2077
  const instance = componentRef.instance;
2085
2078
  if (typeof instance['onExternalValueChange'] === 'function') {
2086
2079
  instance.onExternalValueChange(newVal);
@@ -2089,16 +2082,7 @@ class GridAdapter {
2089
2082
  componentRef.changeDetectorRef.detectChanges();
2090
2083
  }
2091
2084
  catch {
2092
- // Input doesn't exist or component is destroyed — fall back to DOM patching
2093
- const input = hostElement.querySelector?.('input,textarea,select');
2094
- if (input) {
2095
- if (input instanceof HTMLInputElement && input.type === 'checkbox') {
2096
- input.checked = !!newVal;
2097
- }
2098
- else {
2099
- input.value = String(newVal ?? '');
2100
- }
2101
- }
2085
+ // Component is destroyed — nothing to update.
2102
2086
  }
2103
2087
  });
2104
2088
  return hostElement;
@@ -2190,13 +2174,26 @@ class GridAdapter {
2190
2174
  * @returns Processed GroupingColumnsConfig with actual renderer functions
2191
2175
  */
2192
2176
  processGroupingColumnsConfig(config) {
2177
+ const processed = { ...config };
2178
+ let changed = false;
2179
+ // Bridge top-level groupHeaderRenderer component class
2193
2180
  if (config.groupHeaderRenderer && isComponentClass(config.groupHeaderRenderer)) {
2194
- return {
2195
- ...config,
2196
- groupHeaderRenderer: this.createComponentGroupHeaderRenderer(config.groupHeaderRenderer),
2197
- };
2181
+ processed.groupHeaderRenderer = this.createComponentGroupHeaderRenderer(config.groupHeaderRenderer);
2182
+ changed = true;
2198
2183
  }
2199
- return config;
2184
+ // Bridge per-group renderer component classes inside columnGroups
2185
+ if (Array.isArray(config.columnGroups)) {
2186
+ const mappedGroups = config.columnGroups.map((def) => {
2187
+ if (def.renderer && isComponentClass(def.renderer)) {
2188
+ changed = true;
2189
+ return { ...def, renderer: this.createComponentGroupHeaderRenderer(def.renderer) };
2190
+ }
2191
+ return def;
2192
+ });
2193
+ if (changed)
2194
+ processed.columnGroups = mappedGroups;
2195
+ }
2196
+ return changed ? processed : config;
2200
2197
  }
2201
2198
  /**
2202
2199
  * Processes a GroupingRowsConfig, converting component class references
@@ -4588,7 +4585,14 @@ class Grid {
4588
4585
  if (columnsValue === undefined)
4589
4586
  return;
4590
4587
  const grid = this.elementRef.nativeElement;
4591
- grid.columns = columnsValue;
4588
+ // Process columns through the adapter to convert Angular component classes
4589
+ // (renderer/editor) to functions — the grid's columns setter does NOT call
4590
+ // processConfig, unlike gridConfig. Without this, raw component classes
4591
+ // would be invoked without `new`, causing runtime errors.
4592
+ const processed = this.adapter
4593
+ ? columnsValue.map((col) => this.adapter.processColumn(col))
4594
+ : columnsValue;
4595
+ grid.columns = processed;
4592
4596
  });
4593
4597
  // Effect to sync fitMode to the grid element
4594
4598
  effect(() => {