@toolbox-web/grid-angular 1.9.1 → 2.0.0-beta

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.
Files changed (33) hide show
  1. package/fesm2022/toolbox-web-grid-angular-features-editing.mjs +2317 -4
  2. package/fesm2022/toolbox-web-grid-angular-features-editing.mjs.map +1 -1
  3. package/fesm2022/toolbox-web-grid-angular-features-filtering.mjs +91 -2
  4. package/fesm2022/toolbox-web-grid-angular-features-filtering.mjs.map +1 -1
  5. package/fesm2022/toolbox-web-grid-angular-features-master-detail.mjs +97 -4
  6. package/fesm2022/toolbox-web-grid-angular-features-master-detail.mjs.map +1 -1
  7. package/fesm2022/toolbox-web-grid-angular-features-pinned-rows.mjs +1 -15
  8. package/fesm2022/toolbox-web-grid-angular-features-pinned-rows.mjs.map +1 -1
  9. package/fesm2022/toolbox-web-grid-angular-features-responsive.mjs +109 -3
  10. package/fesm2022/toolbox-web-grid-angular-features-responsive.mjs.map +1 -1
  11. package/fesm2022/toolbox-web-grid-angular-features-shell.mjs +452 -0
  12. package/fesm2022/toolbox-web-grid-angular-features-shell.mjs.map +1 -1
  13. package/fesm2022/toolbox-web-grid-angular.mjs +740 -4649
  14. package/fesm2022/toolbox-web-grid-angular.mjs.map +1 -1
  15. package/package.json +2 -6
  16. package/types/toolbox-web-grid-angular-features-editing.d.ts +1181 -4
  17. package/types/toolbox-web-grid-angular-features-editing.d.ts.map +1 -1
  18. package/types/toolbox-web-grid-angular-features-filtering.d.ts +102 -7
  19. package/types/toolbox-web-grid-angular-features-filtering.d.ts.map +1 -1
  20. package/types/toolbox-web-grid-angular-features-master-detail.d.ts +88 -4
  21. package/types/toolbox-web-grid-angular-features-master-detail.d.ts.map +1 -1
  22. package/types/toolbox-web-grid-angular-features-pinned-rows.d.ts +2 -7
  23. package/types/toolbox-web-grid-angular-features-pinned-rows.d.ts.map +1 -1
  24. package/types/toolbox-web-grid-angular-features-responsive.d.ts +109 -3
  25. package/types/toolbox-web-grid-angular-features-responsive.d.ts.map +1 -1
  26. package/types/toolbox-web-grid-angular-features-shell.d.ts +229 -0
  27. package/types/toolbox-web-grid-angular-features-shell.d.ts.map +1 -1
  28. package/types/toolbox-web-grid-angular.d.ts +483 -2929
  29. package/types/toolbox-web-grid-angular.d.ts.map +1 -1
  30. package/fesm2022/toolbox-web-grid-angular-features-reorder-rows.mjs +0 -70
  31. package/fesm2022/toolbox-web-grid-angular-features-reorder-rows.mjs.map +0 -1
  32. package/types/toolbox-web-grid-angular-features-reorder-rows.d.ts +0 -25
  33. package/types/toolbox-web-grid-angular-features-reorder-rows.d.ts.map +0 -1
@@ -1,9 +1,10 @@
1
1
  export { _Augmentation as _EditingAugmentation } from '@toolbox-web/grid/features/editing';
2
2
  import * as _angular_core from '@angular/core';
3
- import { OnInit, OnDestroy } from '@angular/core';
3
+ import { OnInit, OnDestroy, ElementRef, TemplateRef } from '@angular/core';
4
4
  import { CellCommitEvent, RowCommitEvent } from '@toolbox-web/grid-angular';
5
- export { BaseGridEditor, BaseGridEditorCVA, BaseOverlayEditor, FormArrayContext, GridColumnEditor, GridEditorContext, GridFormArray, GridLazyForm, LazyFormFactory, OverlayPosition, RowFormChangeEvent, StructuralEditorContext, TbwEditor, getFormArrayContext, getLazyFormContext } from '@toolbox-web/grid-angular';
6
5
  import { EditingConfig, CellCancelDetail, ChangedRowsResetDetail, EditOpenDetail, BeforeEditCloseDetail, EditCloseDetail, DirtyChangeDetail } from '@toolbox-web/grid/plugins/editing';
6
+ import { AbstractControl, ControlValueAccessor, FormGroup, FormArray } from '@angular/forms';
7
+ import { ColumnConfig } from '@toolbox-web/grid';
7
8
 
8
9
  /**
9
10
  * Owns the binding(s) `[editing], [cellCommit], [cellCancel], [rowCommit], [changedRowsReset], [editOpen], [beforeEditClose], [editClose], [dirtyChange]` on `<tbw-grid>` for the matching feature plugin. See `GridFilteringDirective` for the full rationale.
@@ -12,7 +13,7 @@ import { EditingConfig, CellCancelDetail, ChangedRowsResetDetail, EditOpenDetail
12
13
  */
13
14
  declare class GridEditingDirective implements OnInit, OnDestroy {
14
15
  private readonly elementRef;
15
- readonly editing: _angular_core.InputSignal<boolean | EditingConfig | "click" | "dblclick" | "manual" | undefined>;
16
+ readonly editing: _angular_core.InputSignal<boolean | "click" | EditingConfig | "dblclick" | "manual" | undefined>;
16
17
  readonly cellCommit: _angular_core.OutputEmitterRef<CellCommitEvent<unknown, unknown>>;
17
18
  readonly cellCancel: _angular_core.OutputEmitterRef<CellCancelDetail>;
18
19
  readonly rowCommit: _angular_core.OutputEmitterRef<RowCommitEvent<unknown>>;
@@ -30,5 +31,1181 @@ declare class GridEditingDirective implements OnInit, OnDestroy {
30
31
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<GridEditingDirective, "tbw-grid[editing], tbw-grid[cellCommit], tbw-grid[cellCancel], tbw-grid[rowCommit], tbw-grid[changedRowsReset], tbw-grid[editOpen], tbw-grid[beforeEditClose], tbw-grid[editClose], tbw-grid[dirtyChange]", never, { "editing": { "alias": "editing"; "required": false; "isSignal": true; }; }, { "cellCommit": "cellCommit"; "cellCancel": "cellCancel"; "rowCommit": "rowCommit"; "changedRowsReset": "changedRowsReset"; "editOpen": "editOpen"; "beforeEditClose": "beforeEditClose"; "editClose": "editClose"; "dirtyChange": "dirtyChange"; }, never, never, true, never>;
31
32
  }
32
33
 
33
- export { GridEditingDirective };
34
+ /**
35
+ * Base class for grid cell editors.
36
+ *
37
+ * Provides common functionality for Angular cell editors:
38
+ * - Automatic value resolution from FormControl or value input
39
+ * - Common inputs (value, row, column, control)
40
+ * - Common outputs (commit, cancel)
41
+ * - Validation state helpers
42
+ *
43
+ * ## Usage
44
+ *
45
+ * ```typescript
46
+ * import { Component } from '@angular/core';
47
+ * import { BaseGridEditor } from '@toolbox-web/grid-angular/features/editing';
48
+ *
49
+ * @Component({
50
+ * selector: 'app-my-editor',
51
+ * template: \`
52
+ * <input
53
+ * [value]="currentValue()"
54
+ * [class.is-invalid]="isInvalid()"
55
+ * (input)="commitValue($event.target.value)"
56
+ * (keydown.escape)="cancelEdit()"
57
+ * />
58
+ * @if (hasErrors()) {
59
+ * <div class="error">{{ firstErrorMessage() }}</div>
60
+ * }
61
+ * \`
62
+ * })
63
+ * export class MyEditorComponent extends BaseGridEditor<MyRow, string> {
64
+ * // Override to customize error messages
65
+ * protected override getErrorMessage(errorKey: string): string {
66
+ * if (errorKey === 'required') return 'This field is required';
67
+ * if (errorKey === 'minlength') return 'Too short';
68
+ * return super.getErrorMessage(errorKey);
69
+ * }
70
+ * }
71
+ * ```
72
+ *
73
+ * ## Template Syntax
74
+ *
75
+ * When using the base class, you only need to pass the control:
76
+ *
77
+ * ```html
78
+ * <tbw-grid-column field="name">
79
+ * <app-my-editor *tbwEditor="let _; control as control" [control]="control" />
80
+ * </tbw-grid-column>
81
+ * ```
82
+ *
83
+ * Or without FormArray binding (fallback to value):
84
+ *
85
+ * ```html
86
+ * <tbw-grid-column field="name">
87
+ * <app-my-editor *tbwEditor="let value" [value]="value" />
88
+ * </tbw-grid-column>
89
+ * ```
90
+ *
91
+ * @typeParam TRow - The row data type
92
+ * @typeParam TValue - The cell value type
93
+ *
94
+ * @since 0.5.0
95
+ */
96
+ declare abstract class BaseGridEditor<TRow = unknown, TValue = unknown> {
97
+ protected readonly elementRef: ElementRef<any>;
98
+ private readonly _destroyRef;
99
+ /** Cleanup function for the edit-close listener */
100
+ private _editCloseCleanup;
101
+ /**
102
+ * The cell value. Used when FormControl is not available.
103
+ * When a FormControl is provided, value is derived from control.value instead.
104
+ */
105
+ readonly value: _angular_core.InputSignal<TValue | undefined>;
106
+ /**
107
+ * The full row data object.
108
+ */
109
+ readonly row: _angular_core.InputSignal<TRow | undefined>;
110
+ /**
111
+ * The column configuration.
112
+ */
113
+ readonly column: _angular_core.InputSignal<ColumnConfig<TRow> | undefined>;
114
+ /**
115
+ * The FormControl for this cell, if the grid is bound to a FormArray.
116
+ * When provided, the editor uses control.value instead of the value input.
117
+ */
118
+ readonly control: _angular_core.InputSignal<AbstractControl<any, any, any> | undefined>;
119
+ /**
120
+ * Emits when the user commits a new value.
121
+ * Emits `null` when a nullable field is cleared.
122
+ */
123
+ readonly commit: _angular_core.OutputEmitterRef<TValue | null>;
124
+ /**
125
+ * Emits when the user cancels editing.
126
+ */
127
+ readonly cancel: _angular_core.OutputEmitterRef<void>;
128
+ /**
129
+ * The current value, derived from FormControl if available, otherwise from value input.
130
+ * This is the recommended way to get the current value in your editor template.
131
+ */
132
+ readonly currentValue: _angular_core.Signal<TValue | undefined>;
133
+ /**
134
+ * Whether the control is invalid (has validation errors).
135
+ * Returns false if no FormControl is available.
136
+ */
137
+ readonly isInvalid: _angular_core.Signal<boolean>;
138
+ /**
139
+ * Whether the control is dirty (has been modified).
140
+ * Returns false if no FormControl is available.
141
+ */
142
+ readonly isDirty: _angular_core.Signal<boolean>;
143
+ /**
144
+ * Whether the control has been touched.
145
+ * Returns false if no FormControl is available.
146
+ */
147
+ readonly isTouched: _angular_core.Signal<boolean>;
148
+ /**
149
+ * Whether the control has any validation errors.
150
+ */
151
+ readonly hasErrors: _angular_core.Signal<boolean>;
152
+ /**
153
+ * The first error message from the control's validation errors.
154
+ * Returns an empty string if no errors.
155
+ */
156
+ readonly firstErrorMessage: _angular_core.Signal<string>;
157
+ /**
158
+ * All error messages from the control's validation errors.
159
+ */
160
+ readonly allErrorMessages: _angular_core.Signal<string[]>;
161
+ constructor();
162
+ private _initEditCloseListener;
163
+ /**
164
+ * Whether this editor's cell is the currently focused cell.
165
+ *
166
+ * In row editing mode the grid creates editors for every editable cell
167
+ * in the row simultaneously. Use this to conditionally auto-focus inputs
168
+ * or open panels only in the active cell.
169
+ *
170
+ * Performs a synchronous DOM check — safe to call from `ngAfterViewInit`.
171
+ */
172
+ protected isCellFocused(): boolean;
173
+ /**
174
+ * Called **before** the grid clears editing state and destroys editor DOM.
175
+ *
176
+ * At this point the commit callback is still active, so subclasses can
177
+ * call {@link commitValue} to flush any pending/deferred values.
178
+ *
179
+ * This fires only on the **commit** path (not on revert/cancel).
180
+ * Use {@link onEditClose} for cleanup that should happen on both paths.
181
+ */
182
+ protected onBeforeEditClose(): void;
183
+ /**
184
+ * Called when the grid ends the editing session for this cell.
185
+ *
186
+ * Override to perform cleanup such as closing overlay panels, autocomplete
187
+ * dropdowns, or other floating UI that lives at `<body>` level and would
188
+ * otherwise persist after the editor DOM is removed.
189
+ *
190
+ * The listener is set up automatically via `afterNextRender` — no manual
191
+ * wiring required.
192
+ */
193
+ protected onEditClose(): void;
194
+ /**
195
+ * Called by the grid adapter when the cell value changes externally
196
+ * (e.g., via `updateRow()` cascade or undo/redo).
197
+ *
198
+ * Override in subclasses to reset internal state (search text, selection
199
+ * flags, etc.) so the editor displays the updated value.
200
+ *
201
+ * This runs **synchronously** before the value input is updated, giving
202
+ * the editor a chance to clear stale state before the next change-detection
203
+ * pass re-reads the template.
204
+ *
205
+ * @param _newVal The new cell value being pushed from the grid
206
+ */
207
+ onExternalValueChange(_newVal: TValue): void;
208
+ /**
209
+ * Commit a new value. Emits the commit output AND dispatches a DOM event.
210
+ * The DOM event enables the grid's auto-wiring to catch the commit.
211
+ * Call this when the user confirms their edit.
212
+ */
213
+ commitValue(newValue: TValue | null): void;
214
+ /**
215
+ * Cancel editing. Emits the cancel output AND dispatches a DOM event.
216
+ * Call this when the user cancels (e.g., presses Escape).
217
+ */
218
+ cancelEdit(): void;
219
+ /**
220
+ * Get a human-readable error message for a validation error.
221
+ * Override this method to customize error messages for your editor.
222
+ *
223
+ * @param errorKey - The validation error key (e.g., 'required', 'minlength')
224
+ * @param errorValue - The error value (e.g., { requiredLength: 5, actualLength: 3 })
225
+ * @returns A human-readable error message
226
+ */
227
+ protected getErrorMessage(errorKey: string, errorValue?: unknown): string;
228
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<BaseGridEditor<any, any>, never>;
229
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<BaseGridEditor<any, any>, never, never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "row": { "alias": "row"; "required": false; "isSignal": true; }; "column": { "alias": "column"; "required": false; "isSignal": true; }; "control": { "alias": "control"; "required": false; "isSignal": true; }; }, { "commit": "commit"; "cancel": "cancel"; }, never, never, true, never>;
230
+ }
231
+
232
+ /**
233
+ * Base class for grid editors that also work as Angular form controls.
234
+ *
235
+ * Combines `BaseGridEditor` with `ControlValueAccessor` so the same component
236
+ * can be used inside a `<tbw-grid>` **and** in a standalone `<form>`.
237
+ *
238
+ * ## What it provides
239
+ *
240
+ * | Member | Purpose |
241
+ * |--------|---------|
242
+ * | `cvaValue` | Signal holding the value written by the form control |
243
+ * | `disabledState` | Signal tracking `setDisabledState` calls |
244
+ * | `displayValue` | Computed that prefers grid value (`currentValue`) and falls back to `cvaValue` |
245
+ * | `commitBoth(v)` | Commits via both CVA `onChange` and grid `commitValue` |
246
+ * | `writeValue` / `registerOn*` / `setDisabledState` | Full CVA implementation |
247
+ *
248
+ * ## Usage
249
+ *
250
+ * ```typescript
251
+ * import { Component, forwardRef } from '@angular/core';
252
+ * import { NG_VALUE_ACCESSOR } from '@angular/forms';
253
+ * import { BaseGridEditorCVA } from '@toolbox-web/grid-angular/features/editing';
254
+ *
255
+ * @Component({
256
+ * selector: 'app-date-picker',
257
+ * providers: [{
258
+ * provide: NG_VALUE_ACCESSOR,
259
+ * useExisting: forwardRef(() => DatePickerComponent),
260
+ * multi: true,
261
+ * }],
262
+ * template: `
263
+ * <input
264
+ * type="date"
265
+ * [value]="displayValue()"
266
+ * [disabled]="disabledState()"
267
+ * (change)="commitBoth($event.target.value)"
268
+ * (keydown.escape)="cancelEdit()"
269
+ * />
270
+ * `
271
+ * })
272
+ * export class DatePickerComponent extends BaseGridEditorCVA<MyRow, string> {}
273
+ * ```
274
+ *
275
+ * > **Note:** Subclasses must still provide `NG_VALUE_ACCESSOR` themselves
276
+ * > because `forwardRef(() => ConcreteClass)` must reference the concrete
277
+ * > component — this is an Angular limitation.
278
+ *
279
+ * @typeParam TRow - The row data type
280
+ * @typeParam TValue - The cell/control value type
281
+ *
282
+ * @since 0.13.0
283
+ */
284
+ declare abstract class BaseGridEditorCVA<TRow = unknown, TValue = unknown> extends BaseGridEditor<TRow, TValue> implements ControlValueAccessor {
285
+ /** Internal onChange callback registered by the form control. */
286
+ private _onChange;
287
+ /** Internal onTouched callback registered by the form control. */
288
+ private _onTouched;
289
+ /**
290
+ * Signal holding the value written by the form control via `writeValue()`.
291
+ * Updated when the form control pushes a new value (e.g. `patchValue`, `setValue`).
292
+ */
293
+ protected readonly cvaValue: _angular_core.WritableSignal<TValue | null>;
294
+ /**
295
+ * Signal tracking the disabled state set by the form control.
296
+ * Updated when `setDisabledState()` is called by Angular's forms module.
297
+ */
298
+ readonly disabledState: _angular_core.WritableSignal<boolean>;
299
+ /**
300
+ * Resolved display value.
301
+ *
302
+ * Prefers `currentValue()` (grid context — from `control.value` or `value` input)
303
+ * and falls back to `cvaValue()` (standalone form context — from `writeValue`).
304
+ *
305
+ * Use this in your template instead of reading `currentValue()` directly
306
+ * so the component works in both grid and standalone form contexts.
307
+ */
308
+ readonly displayValue: _angular_core.Signal<TValue | null>;
309
+ /**
310
+ * Called by Angular forms when the form control value changes programmatically.
311
+ */
312
+ writeValue(value: TValue | null): void;
313
+ /**
314
+ * Called by Angular forms to register a change callback.
315
+ */
316
+ registerOnChange(fn: (value: TValue | null) => void): void;
317
+ /**
318
+ * Called by Angular forms to register a touched callback.
319
+ */
320
+ registerOnTouched(fn: () => void): void;
321
+ /**
322
+ * Called by Angular forms to set the disabled state.
323
+ */
324
+ setDisabledState(isDisabled: boolean): void;
325
+ /**
326
+ * Commit a value through both the CVA (form control) and the grid.
327
+ *
328
+ * - Calls the CVA `onChange` callback (updates the form control)
329
+ * - Marks the control as touched
330
+ * - Calls `commitValue()` (emits grid commit event + DOM `CustomEvent`)
331
+ *
332
+ * Use this instead of `commitValue()` when your editor doubles as a form control.
333
+ *
334
+ * @param value - The new value to commit
335
+ */
336
+ protected commitBoth(value: TValue | null): void;
337
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<BaseGridEditorCVA<any, any>, never>;
338
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<BaseGridEditorCVA<any, any>, never, never, {}, {}, never, never, true, never>;
339
+ }
340
+
341
+ /**
342
+ * Position of the overlay panel relative to its anchor cell.
343
+ *
344
+ * - `'below'` — panel appears below the cell, left-aligned (default)
345
+ * - `'above'` — panel appears above the cell, left-aligned
346
+ * - `'below-right'` — panel appears below the cell, right-aligned
347
+ * - `'over-top-left'` — panel top-left corner aligns with cell top-left corner (opens downward)
348
+ * - `'over-bottom-left'` — panel bottom-left corner aligns with cell bottom-left corner (opens upward)
349
+ * @since 0.13.0
350
+ */
351
+ type OverlayPosition = 'below' | 'above' | 'below-right' | 'over-top-left' | 'over-bottom-left';
352
+ /**
353
+ * Base class for grid editors that display a floating overlay panel.
354
+ *
355
+ * Provides infrastructure for:
356
+ * - **Overlay positioning** — CSS Anchor Positioning with JS fallback
357
+ * - **Focus gating** — in row editing mode, the panel only opens for the focused cell
358
+ * - **Click-outside detection** — closes the panel when clicking outside
359
+ * - **MutationObserver** — detects cell focus changes (row editing mode)
360
+ * - **Escape handling** — closes the panel and returns focus to the inline input
361
+ * - **Synthetic Tab dispatch** — advances grid focus after overlay close
362
+ * - **Automatic teardown** — removes the panel from `<body>` and cleans up listeners
363
+ * - **External focus registration** — auto-registers the panel via `grid.registerExternalFocusContainer()` so the grid keeps `data-has-focus` and editors stay open while the overlay has focus
364
+ *
365
+ * ## Usage
366
+ *
367
+ * ```typescript
368
+ * import { Component, viewChild, ElementRef, effect } from '@angular/core';
369
+ * import { BaseOverlayEditor } from '@toolbox-web/grid-angular/features/editing';
370
+ *
371
+ * @Component({
372
+ * selector: 'app-date-editor',
373
+ * template: `
374
+ * <input
375
+ * #inlineInput
376
+ * readonly
377
+ * [value]="currentValue()"
378
+ * (click)="onInlineClick()"
379
+ * (keydown)="onInlineKeydown($event)"
380
+ * />
381
+ * <div #panel class="tbw-overlay-panel" style="width: 280px;">
382
+ * <!-- your date picker UI here -->
383
+ * <div class="actions">
384
+ * <button (click)="selectAndClose(selectedDate)">OK</button>
385
+ * <button (click)="hideOverlay()">Cancel</button>
386
+ * </div>
387
+ * </div>
388
+ * `
389
+ * })
390
+ * export class DateEditorComponent extends BaseOverlayEditor<MyRow, string> {
391
+ * panelRef = viewChild.required<ElementRef<HTMLElement>>('panel');
392
+ * inputRef = viewChild.required<ElementRef<HTMLInputElement>>('inlineInput');
393
+ *
394
+ * protected override overlayPosition = 'below' as const;
395
+ *
396
+ * constructor() {
397
+ * super();
398
+ * effect(() => {
399
+ * const panel = this.panelRef().nativeElement;
400
+ * this.initOverlay(panel);
401
+ * if (this.isCellFocused()) this.showOverlay();
402
+ * });
403
+ * }
404
+ *
405
+ * protected getInlineInput(): HTMLInputElement | null {
406
+ * return this.inputRef()?.nativeElement ?? null;
407
+ * }
408
+ *
409
+ * protected onOverlayOutsideClick(): void {
410
+ * this.hideOverlay();
411
+ * }
412
+ *
413
+ * selectAndClose(date: string): void {
414
+ * this.commitValue(date);
415
+ * this.hideOverlay();
416
+ * }
417
+ * }
418
+ * ```
419
+ *
420
+ * @example
421
+ * ```typescript
422
+ * @Component({
423
+ * template: `
424
+ * <input #inlineInput readonly [value]="currentValue()"
425
+ * (click)="onInlineClick()" (keydown)="onInlineKeydown($event)" />
426
+ * <div #panel style="width: 280px">
427
+ * <!-- overlay content -->
428
+ * </div>
429
+ * `
430
+ * })
431
+ * export class MyEditor extends BaseOverlayEditor<MyRow, string> {
432
+ * panelRef = viewChild.required<ElementRef<HTMLElement>>('panel');
433
+ * inputRef = viewChild.required<ElementRef<HTMLInputElement>>('inlineInput');
434
+ *
435
+ * constructor() {
436
+ * super();
437
+ * effect(() => this.initOverlay(this.panelRef().nativeElement));
438
+ * }
439
+ *
440
+ * protected getInlineInput() { return this.inputRef()?.nativeElement ?? null; }
441
+ * protected onOverlayOutsideClick() { this.hideOverlay(); }
442
+ * }
443
+ * ```
444
+ *
445
+ * @typeParam TRow - The row data type
446
+ * @typeParam TValue - The cell value type
447
+ *
448
+ * @since 0.13.0
449
+ */
450
+ declare abstract class BaseOverlayEditor<TRow = unknown, TValue = unknown> extends BaseGridEditor<TRow, TValue> {
451
+ private readonly _elementRef;
452
+ private readonly _overlayDestroyRef;
453
+ /**
454
+ * Position of the overlay panel relative to the anchor cell.
455
+ * Override in subclasses to change the default position.
456
+ *
457
+ * @default 'below'
458
+ */
459
+ protected overlayPosition: OverlayPosition;
460
+ /** The overlay panel element (set via `initOverlay()`). */
461
+ private _panel;
462
+ /** Whether the overlay is currently visible. */
463
+ protected _isOpen: boolean;
464
+ /** Unique anchor ID for CSS Anchor Positioning. */
465
+ private _anchorId;
466
+ /** Whether the browser supports CSS Anchor Positioning. */
467
+ private _supportsAnchor;
468
+ /** AbortController for all overlay-related listeners. */
469
+ private _abortCtrl;
470
+ /** MutationObserver watching cell focus class changes. */
471
+ protected _focusObserver: MutationObserver | null;
472
+ constructor();
473
+ /**
474
+ * Initialise the overlay with the panel element.
475
+ *
476
+ * Call this in an `effect()` or `afterNextRender()` with your `viewChild` panel reference.
477
+ * The panel is moved to `<body>` and hidden until {@link showOverlay} is called.
478
+ *
479
+ * @param panel - The overlay panel DOM element
480
+ */
481
+ protected initOverlay(panel: HTMLElement): void;
482
+ /**
483
+ * Show the overlay panel.
484
+ *
485
+ * If CSS Anchor Positioning is not supported, falls back to JS-based
486
+ * positioning using `getBoundingClientRect()`.
487
+ */
488
+ protected showOverlay(): void;
489
+ /**
490
+ * Hide the overlay panel.
491
+ *
492
+ * @param suppressTabAdvance - When `true`, skip synthetic Tab dispatch
493
+ * (useful when hiding is triggered by an external focus change).
494
+ */
495
+ protected hideOverlay(suppressTabAdvance?: boolean): void;
496
+ /**
497
+ * Close and immediately re-open the overlay.
498
+ * Useful after the panel content changes size and needs repositioning.
499
+ */
500
+ protected reopenOverlay(): void;
501
+ /**
502
+ * Remove the overlay from the DOM and clean up all listeners.
503
+ *
504
+ * Called automatically on `DestroyRef.onDestroy`. Can also be called
505
+ * manually if the editor needs early cleanup.
506
+ */
507
+ protected teardownOverlay(): void;
508
+ /**
509
+ * Override in `edit-close` handler to also hide the overlay.
510
+ * This is called automatically by `BaseGridEditor` when the grid
511
+ * ends the editing session.
512
+ */
513
+ protected onEditClose(): void;
514
+ /**
515
+ * Keydown handler for the inline readonly input.
516
+ *
517
+ * - **Enter / Space / ArrowDown / F2** → open overlay
518
+ * - **Escape** → calls {@link handleEscape}
519
+ *
520
+ * Bind this to `(keydown)` on your inline input element.
521
+ */
522
+ onInlineKeydown(event: KeyboardEvent): void;
523
+ /**
524
+ * Click handler for the inline input.
525
+ * Opens the overlay and calls {@link onOverlayOpened}.
526
+ *
527
+ * Bind this to `(click)` on your inline input element.
528
+ */
529
+ onInlineClick(): void;
530
+ /**
531
+ * Handle Escape key press.
532
+ *
533
+ * If the overlay is open, closes it and returns focus to the inline input.
534
+ * If the overlay is already closed, cancels the edit entirely.
535
+ */
536
+ protected handleEscape(event: Event): void;
537
+ /**
538
+ * Dispatch a synthetic Tab key event to advance grid focus.
539
+ *
540
+ * Call this after committing a value and closing the overlay so the
541
+ * grid moves focus to the next cell.
542
+ *
543
+ * @param backward - When `true`, dispatch Shift+Tab to move backwards.
544
+ */
545
+ protected advanceGridFocus(backward?: boolean): void;
546
+ /**
547
+ * Return the inline input element, if any.
548
+ *
549
+ * Used by overlay infrastructure to return focus after hiding.
550
+ * Return `null` if there is no inline input.
551
+ */
552
+ protected abstract getInlineInput(): HTMLInputElement | null;
553
+ /**
554
+ * Called when a pointerdown event occurs outside the overlay panel
555
+ * and outside the editor's host element.
556
+ *
557
+ * Typically, subclasses call `hideOverlay()` here.
558
+ */
559
+ protected abstract onOverlayOutsideClick(): void;
560
+ /**
561
+ * Called after the overlay is shown.
562
+ *
563
+ * Override to focus an element inside the panel, start animations, etc.
564
+ * Default implementation is a no-op.
565
+ */
566
+ protected onOverlayOpened(): void;
567
+ /** Find the parent cell element for this editor. */
568
+ private _getCell;
569
+ /** Find the parent `<tbw-grid>` element for this editor. */
570
+ private _getGridElement;
571
+ /**
572
+ * JS fallback positioning for browsers without CSS Anchor Positioning.
573
+ * Uses `getBoundingClientRect()` with viewport overflow detection.
574
+ */
575
+ private _positionWithJs;
576
+ /**
577
+ * Document pointerdown handler for click-outside detection.
578
+ * Fires `onOverlayOutsideClick()` if the click is outside the panel
579
+ * and outside the editor's host element.
580
+ */
581
+ private _onDocumentPointerDown;
582
+ /**
583
+ * Handle the grid's `tbw-scroll` CustomEvent while the overlay is open.
584
+ *
585
+ * When the viewport scrolls, the anchored overlay would otherwise float
586
+ * over content that no longer corresponds to the edited cell (or the
587
+ * cell may be virtualized out entirely). Treat scroll as a dismissal
588
+ * signal equivalent to clicking outside so subclasses' overlay-outside
589
+ * handler drives the appropriate commit/cancel path.
590
+ */
591
+ private _onGridScroll;
592
+ /**
593
+ * Set up a MutationObserver on the parent cell to watch for
594
+ * `cell-focus` class changes. This handles row-editing mode where
595
+ * all editors exist simultaneously but only the focused cell's
596
+ * editor should have its overlay visible.
597
+ *
598
+ * A `justOpened` flash guard suppresses the observer from
599
+ * immediately closing the overlay when `beginBulkEdit()` moves
600
+ * focus to the first editable column. Without this guard,
601
+ * double-click triggers a "flash open then close" effect.
602
+ */
603
+ protected _setupFocusObserver(): void;
604
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<BaseOverlayEditor<any, any>, never>;
605
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<BaseOverlayEditor<any, any>, never, never, {}, {}, never, never, true, never>;
606
+ }
607
+
608
+ /**
609
+ * Context object passed to the cell editor template.
610
+ * Contains the cell value, row data, column configuration, and commit/cancel functions.
611
+ * @since 0.1.0
612
+ */
613
+ interface GridEditorContext<TValue = unknown, TRow = unknown> {
614
+ /** The cell value for this column */
615
+ $implicit: TValue;
616
+ /** The cell value (explicit binding) */
617
+ value: TValue;
618
+ /** The full row data object */
619
+ row: TRow;
620
+ /** Field name being edited */
621
+ field: string;
622
+ /** The column configuration */
623
+ column: unknown;
624
+ /** Stable row identifier (from `getRowId`). Empty string if no `getRowId` is configured. */
625
+ rowId: string;
626
+ /**
627
+ * Callback function to commit the edited value.
628
+ * Use with Angular event binding: `(commit)="onCommit($event)"`
629
+ */
630
+ onCommit: (value: TValue) => void;
631
+ /**
632
+ * Callback function to cancel editing.
633
+ * Use with Angular event binding: `(cancel)="onCancel()"`
634
+ */
635
+ onCancel: () => void;
636
+ /**
637
+ * Update other fields in this row while the editor is open.
638
+ * Changes trigger `cell-change` events with source `'cascade'`.
639
+ */
640
+ updateRow: (changes: Partial<TRow>) => void;
641
+ /**
642
+ * Register a callback to receive value updates when the cell is modified
643
+ * externally (e.g., via `updateRow()` from another cell's commit).
644
+ *
645
+ * The framework adapter auto-patches `value`/`$implicit` for template editors,
646
+ * but custom components may use this for additional reactivity.
647
+ */
648
+ onValueChange?: (callback: (newValue: TValue) => void) => void;
649
+ /**
650
+ * The FormControl for this cell, if the grid is bound to a FormArray with FormGroups.
651
+ *
652
+ * This allows custom editors to bind directly to the control for validation display:
653
+ * ```html
654
+ * <input *tbwEditor="let value; control as ctrl"
655
+ * [formControl]="ctrl"
656
+ * [class.is-invalid]="ctrl?.invalid && ctrl?.touched" />
657
+ * ```
658
+ *
659
+ * Returns `undefined` if:
660
+ * - The grid is not bound to a FormArray
661
+ * - The FormArray doesn't contain FormGroups
662
+ * - The field doesn't exist in the FormGroup
663
+ */
664
+ control?: AbstractControl;
665
+ }
666
+ /**
667
+ * Gets the editor template registered for a given element.
668
+ * Used by AngularGridAdapter to retrieve templates at render time.
669
+ */
670
+ declare function getEditorTemplate(element: HTMLElement): TemplateRef<GridEditorContext> | undefined;
671
+ /**
672
+ * Directive that captures an `<ng-template>` for use as a cell editor.
673
+ *
674
+ * This enables declarative Angular component usage with proper input bindings
675
+ * that satisfy Angular's AOT compiler.
676
+ *
677
+ * ## Usage
678
+ *
679
+ * ```html
680
+ * <tbw-grid-column field="status" editable>
681
+ * <tbw-grid-column-editor>
682
+ * <ng-template let-value let-row="row" let-onCommit="onCommit" let-onCancel="onCancel">
683
+ * <app-status-select
684
+ * [value]="value"
685
+ * [row]="row"
686
+ * (commit)="onCommit($event)"
687
+ * (cancel)="onCancel()"
688
+ * />
689
+ * </ng-template>
690
+ * </tbw-grid-column-editor>
691
+ * </tbw-grid-column>
692
+ * ```
693
+ *
694
+ * The template context provides:
695
+ * - `$implicit` / `value`: The cell value
696
+ * - `row`: The full row data object
697
+ * - `column`: The column configuration
698
+ * - `onCommit`: Callback function to commit the new value
699
+ * - `onCancel`: Callback function to cancel editing
700
+ *
701
+ * Import the directive in your component:
702
+ *
703
+ * ```typescript
704
+ * import { GridColumnEditor } from '@toolbox-web/grid-angular/features/editing';
705
+ *
706
+ * @Component({
707
+ * imports: [GridColumnEditor],
708
+ * // ...
709
+ * })
710
+ * ```
711
+ *
712
+ * @category Directive
713
+ *
714
+ * @since 0.1.0
715
+ */
716
+ declare class GridColumnEditor {
717
+ private elementRef;
718
+ /**
719
+ * Query for the ng-template content child.
720
+ */
721
+ template: _angular_core.Signal<TemplateRef<any> | undefined>;
722
+ /** Effect that triggers when the template is available */
723
+ private onTemplateReceived;
724
+ /**
725
+ * Static type guard for template context.
726
+ * Enables type inference in templates.
727
+ */
728
+ static ngTemplateContextGuard(dir: GridColumnEditor, ctx: unknown): ctx is GridEditorContext;
729
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<GridColumnEditor, never>;
730
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<GridColumnEditor, "tbw-grid-column-editor", never, {}, {}, ["template"], never, true, never>;
731
+ }
732
+
733
+ /**
734
+ * Context provided to the grid containing form-related information.
735
+ * This can be accessed by other directives to get form controls.
736
+ * @since 0.5.0
737
+ */
738
+ interface FormArrayContext {
739
+ /** Get the row data at a specific index */
740
+ getRow<T = unknown>(rowIndex: number): T | null;
741
+ /** Update a field value at a specific row */
742
+ updateField(rowIndex: number, field: string, value: unknown): void;
743
+ /** Get the current form value (all rows) */
744
+ getValue<T = unknown>(): T[];
745
+ /**
746
+ * Get the FormControl for a specific cell.
747
+ * Only available when using FormArray with FormGroup rows.
748
+ *
749
+ * @param rowIndex - The row index
750
+ * @param field - The field name
751
+ * @returns The AbstractControl for the cell, or undefined if not available
752
+ */
753
+ getControl(rowIndex: number, field: string): AbstractControl | undefined;
754
+ /**
755
+ * Whether the grid is backed by a FormArray of FormGroups.
756
+ * When true, `getControl()` will return cell-level controls.
757
+ */
758
+ hasFormGroups: boolean;
759
+ /**
760
+ * Get the FormGroup for a specific row.
761
+ * Only available when using FormArray with FormGroup rows.
762
+ *
763
+ * @param rowIndex - The row index
764
+ * @returns The FormGroup for the row, or undefined if not available
765
+ */
766
+ getRowFormGroup(rowIndex: number): FormGroup | undefined;
767
+ /**
768
+ * Check if a row is valid (all controls in the FormGroup are valid).
769
+ * Returns true if not using FormArray or if the row doesn't exist.
770
+ *
771
+ * @param rowIndex - The row index
772
+ * @returns true if the row is valid, false if any control is invalid
773
+ */
774
+ isRowValid(rowIndex: number): boolean;
775
+ /**
776
+ * Check if a row has been touched (any control in the FormGroup is touched).
777
+ * Returns false if not using FormArray or if the row doesn't exist.
778
+ *
779
+ * @param rowIndex - The row index
780
+ * @returns true if any control in the row is touched
781
+ */
782
+ isRowTouched(rowIndex: number): boolean;
783
+ /**
784
+ * Check if a row is dirty (any control in the FormGroup is dirty).
785
+ * Returns false if not using FormArray or if the row doesn't exist.
786
+ *
787
+ * @param rowIndex - The row index
788
+ * @returns true if any control in the row is dirty
789
+ */
790
+ isRowDirty(rowIndex: number): boolean;
791
+ /**
792
+ * Get validation errors for a specific row.
793
+ * Aggregates errors from all controls in the FormGroup.
794
+ *
795
+ * @param rowIndex - The row index
796
+ * @returns Object with field names as keys and their errors, or null if no errors
797
+ */
798
+ getRowErrors(rowIndex: number): Record<string, unknown> | null;
799
+ }
800
+ /**
801
+ * Gets the FormArrayContext from a grid element, if present.
802
+ * @internal
803
+ * @since 0.5.0
804
+ */
805
+ declare function getFormArrayContext(gridElement: HTMLElement): FormArrayContext | undefined;
806
+ /**
807
+ * Directive that binds a FormArray directly to the grid.
808
+ *
809
+ * This is the recommended way to integrate tbw-grid with Angular Reactive Forms.
810
+ * Use a FormArray of FormGroups for row-level validation and cell-level control access.
811
+ *
812
+ * ## Usage
813
+ *
814
+ * ```typescript
815
+ * import { Component, inject } from '@angular/core';
816
+ * import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
817
+ * import { Grid, GridFormArray } from '@toolbox-web/grid-angular/features/editing';
818
+ *
819
+ * @Component({
820
+ * imports: [Grid, GridFormArray, ReactiveFormsModule],
821
+ * template: \`
822
+ * <form [formGroup]="form">
823
+ * <tbw-grid [formArray]="form.controls.rows" [columns]="columns" />
824
+ * </form>
825
+ * \`
826
+ * })
827
+ * export class MyComponent {
828
+ * private fb = inject(FormBuilder);
829
+ *
830
+ * form = this.fb.group({
831
+ * rows: this.fb.array([
832
+ * this.fb.group({ name: 'Alice', age: 30 }),
833
+ * this.fb.group({ name: 'Bob', age: 25 }),
834
+ * ])
835
+ * });
836
+ *
837
+ * columns = [
838
+ * { field: 'name', header: 'Name', editable: true },
839
+ * { field: 'age', header: 'Age', editable: true }
840
+ * ];
841
+ * }
842
+ * ```
843
+ *
844
+ * ## How It Works
845
+ *
846
+ * - **FormArray → Grid**: The grid displays the FormArray's value as rows
847
+ * - **Grid → FormArray**: When a cell is edited, the corresponding FormControl is updated
848
+ * - FormArrayContext is available for accessing cell-level controls
849
+ *
850
+ * ## Features
851
+ *
852
+ * - Works naturally with FormArray inside a FormGroup
853
+ * - Provides cell-level FormControl access for validation
854
+ * - Supports row-level validation state aggregation
855
+ * - Automatically syncs FormArray changes to the grid
856
+ *
857
+ * @example
858
+ * ```html
859
+ * <form [formGroup]="form">
860
+ * <tbw-grid [formArray]="form.controls.rows" [columns]="columns" />
861
+ * </form>
862
+ * ```
863
+ *
864
+ * @category Directive
865
+ *
866
+ * @since 0.5.0
867
+ */
868
+ declare class GridFormArray implements OnInit, OnDestroy {
869
+ #private;
870
+ private readonly destroyRef;
871
+ private elementRef;
872
+ private cellCommitUnsub;
873
+ private cellCancelUnsub;
874
+ private rowCommitUnsub;
875
+ private touchListener;
876
+ private valueChangesSubscription;
877
+ private statusChangesSubscriptions;
878
+ /**
879
+ * The FormArray to bind to the grid.
880
+ */
881
+ readonly formArray: _angular_core.InputSignal<FormArray<any>>;
882
+ /**
883
+ * Whether to automatically sync Angular validation state to grid's visual invalid styling.
884
+ *
885
+ * When enabled:
886
+ * - After a cell commit, if the FormControl is invalid, the cell is marked with `setInvalid()`
887
+ * - When a FormControl becomes valid, `clearInvalid()` is called
888
+ * - On `row-commit`, if the row's FormGroup has invalid controls, the commit is prevented
889
+ * - In grid mode: validation state is synced on initial render and updated reactively
890
+ *
891
+ * @default true
892
+ */
893
+ readonly syncValidation: _angular_core.InputSignal<boolean>;
894
+ /**
895
+ * Effect that sets up valueChanges subscription when FormArray changes.
896
+ * This handles both initial binding and when the FormArray reference changes.
897
+ */
898
+ private syncFormArrayToGrid;
899
+ ngOnInit(): void;
900
+ ngOnDestroy(): void;
901
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<GridFormArray, never>;
902
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<GridFormArray, "tbw-grid[formArray]", never, { "formArray": { "alias": "formArray"; "required": true; "isSignal": true; }; "syncValidation": { "alias": "syncValidation"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
903
+ }
904
+
905
+ /**
906
+ * Gets the FormArrayContext from a grid element, if present.
907
+ * @internal
908
+ * @since 0.11.0
909
+ */
910
+ declare function getLazyFormContext(gridElement: HTMLElement): FormArrayContext | undefined;
911
+ /**
912
+ * Factory function type for creating FormGroups lazily.
913
+ * Called when a row enters edit mode for the first time.
914
+ *
915
+ * @template TRow The row data type
916
+ * @param row The row data object
917
+ * @param rowIndex The row index in the grid
918
+ * @returns A FormGroup for the row (only include editable fields)
919
+ * @since 0.11.0
920
+ */
921
+ type LazyFormFactory<TRow = unknown> = (row: TRow, rowIndex: number) => FormGroup;
922
+ /**
923
+ * Event emitted when a row's form values have changed.
924
+ * @since 0.11.0
925
+ */
926
+ interface RowFormChangeEvent<TRow = unknown> {
927
+ /** The row index */
928
+ rowIndex: number;
929
+ /** The row ID (if available) */
930
+ rowId?: string;
931
+ /** The original row data */
932
+ row: TRow;
933
+ /** The FormGroup for this row */
934
+ formGroup: FormGroup;
935
+ /** The current form values */
936
+ values: Partial<TRow>;
937
+ /** Whether the form is valid */
938
+ valid: boolean;
939
+ /** Whether the form is dirty */
940
+ dirty: boolean;
941
+ }
942
+ /**
943
+ * Directive that provides lazy FormGroup creation for grid editing.
944
+ *
945
+ * Unlike `GridFormArray` which creates all FormGroups upfront, this directive
946
+ * creates FormGroups on-demand only when a row enters edit mode. This provides
947
+ * much better performance for large datasets while still enabling full
948
+ * Angular Reactive Forms integration.
949
+ *
950
+ * ## Key Benefits
951
+ *
952
+ * - **Performance**: Only creates FormGroups for rows being edited (20-100x fewer controls)
953
+ * - **Same DX**: Editors still receive `control` in their context for validation
954
+ * - **Memory efficient**: FormGroups are cleaned up when rows exit edit mode
955
+ *
956
+ * ## Usage
957
+ *
958
+ * ```typescript
959
+ * import { Component, inject, signal } from '@angular/core';
960
+ * import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
961
+ * import { Grid, GridLazyForm, TbwEditor } from '@toolbox-web/grid-angular/features/editing';
962
+ *
963
+ * @Component({
964
+ * imports: [Grid, GridLazyForm, TbwEditor, ReactiveFormsModule],
965
+ * template: \`
966
+ * <tbw-grid
967
+ * [rows]="employees()"
968
+ * [lazyForm]="createRowForm"
969
+ * [gridConfig]="config">
970
+ *
971
+ * <tbw-grid-column field="firstName">
972
+ * <input *tbwEditor="let _; control as ctrl"
973
+ * [formControl]="ctrl"
974
+ * [class.is-invalid]="ctrl?.invalid && ctrl?.touched" />
975
+ * </tbw-grid-column>
976
+ * </tbw-grid>
977
+ * \`
978
+ * })
979
+ * export class MyComponent {
980
+ * private fb = inject(FormBuilder);
981
+ * employees = signal(generateEmployees(1000));
982
+ *
983
+ * // Factory called when editing starts - only include editable fields!
984
+ * createRowForm = (row: Employee): FormGroup => this.fb.group({
985
+ * firstName: [row.firstName, Validators.required],
986
+ * lastName: [row.lastName, Validators.minLength(2)],
987
+ * salary: [row.salary, [Validators.required, Validators.min(0)]],
988
+ * });
989
+ *
990
+ * gridConfig = { columns: [...] };
991
+ * }
992
+ * ```
993
+ *
994
+ * ## How It Works
995
+ *
996
+ * 1. Rows come from `[rows]` input (plain data array)
997
+ * 2. When a cell enters edit mode, the FormGroup is created lazily
998
+ * 3. Editors receive the FormControl in their template context
999
+ * 4. On commit, FormGroup values are synced back to the row
1000
+ * 5. FormGroup is cleaned up when the row exits edit mode (configurable)
1001
+ *
1002
+ * ## Performance Comparison
1003
+ *
1004
+ * | Rows | GridFormArray (20 fields) | GridLazyForm |
1005
+ * |------|---------------------------|--------------|
1006
+ * | 100 | 2,000 controls | ~20 controls |
1007
+ * | 500 | 10,000 controls | ~20 controls |
1008
+ * | 1000 | 20,000 controls | ~20 controls |
1009
+ *
1010
+ * @example
1011
+ * ```html
1012
+ * <tbw-grid [rows]="employees()" [lazyForm]="createRowForm" [gridConfig]="gridConfig" />
1013
+ * ```
1014
+ *
1015
+ * @see GridFormArray For small datasets with full upfront validation
1016
+ * @category Directive
1017
+ *
1018
+ * @since 0.11.0
1019
+ */
1020
+ declare class GridLazyForm<TRow = unknown> implements OnInit, OnDestroy {
1021
+ #private;
1022
+ private elementRef;
1023
+ private formGroupCache;
1024
+ private rowIndexMap;
1025
+ private editingRowIndex;
1026
+ private cellCommitUnsub;
1027
+ private rowCommitUnsub;
1028
+ private rowsChangeUnsub;
1029
+ /**
1030
+ * Factory function to create a FormGroup for a row.
1031
+ * Called lazily when the row first enters edit mode.
1032
+ *
1033
+ * @example
1034
+ * ```typescript
1035
+ * createRowForm = (row: Employee): FormGroup => this.fb.group({
1036
+ * firstName: [row.firstName, Validators.required],
1037
+ * lastName: [row.lastName],
1038
+ * salary: [row.salary, [Validators.min(0)]],
1039
+ * });
1040
+ * ```
1041
+ */
1042
+ readonly lazyForm: _angular_core.InputSignal<LazyFormFactory<TRow>>;
1043
+ /**
1044
+ * Whether to automatically sync Angular validation state to grid's visual invalid styling.
1045
+ *
1046
+ * When enabled:
1047
+ * - After a cell commit, if the FormControl is invalid, the cell is marked with `setInvalid()`
1048
+ * - When a FormControl becomes valid, `clearInvalid()` is called
1049
+ * - On `row-commit`, if the row's FormGroup has invalid controls, the commit is prevented
1050
+ *
1051
+ * @default true
1052
+ */
1053
+ readonly syncValidation: _angular_core.InputSignal<boolean>;
1054
+ /**
1055
+ * Whether to keep FormGroups cached after a row exits edit mode.
1056
+ *
1057
+ * - `true`: FormGroups are kept, preserving dirty/touched state across edit sessions
1058
+ * - `false`: FormGroups are disposed when the row exits edit mode (default)
1059
+ *
1060
+ * @default false
1061
+ */
1062
+ readonly keepFormGroups: _angular_core.InputSignal<boolean>;
1063
+ /**
1064
+ * Emitted when a row's form values change.
1065
+ * Useful for auto-save, validation display, or syncing to external state.
1066
+ */
1067
+ readonly rowFormChange: _angular_core.OutputEmitterRef<RowFormChangeEvent<TRow>>;
1068
+ ngOnInit(): void;
1069
+ ngOnDestroy(): void;
1070
+ /**
1071
+ * Gets the FormGroup for a row, if it exists.
1072
+ * Unlike the context methods, this does NOT create a FormGroup lazily.
1073
+ *
1074
+ * @param rowIndex The row index
1075
+ * @returns The FormGroup or undefined
1076
+ */
1077
+ getFormGroup(rowIndex: number): FormGroup | undefined;
1078
+ /**
1079
+ * Gets all cached FormGroups.
1080
+ * Useful for bulk validation or inspection.
1081
+ *
1082
+ * @returns Map of row objects to their FormGroups
1083
+ */
1084
+ getAllFormGroups(): ReadonlyMap<TRow, FormGroup>;
1085
+ /**
1086
+ * Clears all cached FormGroups.
1087
+ * Useful when the underlying data changes significantly.
1088
+ */
1089
+ clearAllFormGroups(): void;
1090
+ /**
1091
+ * Validates all currently cached FormGroups.
1092
+ *
1093
+ * @returns true if all FormGroups are valid, false otherwise
1094
+ */
1095
+ validateAll(): boolean;
1096
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<GridLazyForm<any>, never>;
1097
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<GridLazyForm<any>, "tbw-grid[lazyForm]", never, { "lazyForm": { "alias": "lazyForm"; "required": true; "isSignal": true; }; "syncValidation": { "alias": "syncValidation"; "required": false; "isSignal": true; }; "keepFormGroups": { "alias": "keepFormGroups"; "required": false; "isSignal": true; }; }, { "rowFormChange": "rowFormChange"; }, never, never, true, never>;
1098
+ }
1099
+
1100
+ /**
1101
+ * Context type for structural editor directives with `any` defaults.
1102
+ * This provides better ergonomics in templates without requiring explicit type annotations.
1103
+ *
1104
+ * @internal Use `GridEditorContext` in application code for stricter typing.
1105
+ * @since 0.1.1
1106
+ */
1107
+ interface StructuralEditorContext<TValue = any, TRow = any> {
1108
+ /** The cell value for this column */
1109
+ $implicit: TValue;
1110
+ /** The cell value (explicit binding) */
1111
+ value: TValue;
1112
+ /** The full row data object */
1113
+ row: TRow;
1114
+ /** The column configuration */
1115
+ column: any;
1116
+ /**
1117
+ * Callback function to commit the edited value.
1118
+ */
1119
+ onCommit: (value: TValue) => void;
1120
+ /**
1121
+ * Callback function to cancel editing.
1122
+ */
1123
+ onCancel: () => void;
1124
+ /**
1125
+ * The FormControl for this cell, if the grid is bound to a FormArray with FormGroups.
1126
+ *
1127
+ * Returns `undefined` if:
1128
+ * - The grid is not bound to a FormArray
1129
+ * - The FormArray doesn't contain FormGroups
1130
+ * - The field doesn't exist in the FormGroup
1131
+ */
1132
+ control?: AbstractControl;
1133
+ }
1134
+ /**
1135
+ * Gets the editor template registered by the structural directive for a given column element.
1136
+ * Falls back to the non-structural directive registry.
1137
+ */
1138
+ declare function getStructuralEditorTemplate(columnElement: HTMLElement): TemplateRef<StructuralEditorContext> | undefined;
1139
+ /**
1140
+ * Structural directive for cell editor rendering.
1141
+ *
1142
+ * This provides a cleaner syntax for defining custom cell editors without
1143
+ * the nested `<tbw-grid-column-editor>` and `<ng-template>` boilerplate.
1144
+ *
1145
+ * ## Usage
1146
+ *
1147
+ * ```html
1148
+ * <!-- Instead of this verbose syntax: -->
1149
+ * <tbw-grid-column field="status">
1150
+ * <tbw-grid-column-editor>
1151
+ * <ng-template let-value let-onCommit="onCommit" let-onCancel="onCancel">
1152
+ * <app-status-editor [value]="value" (commit)="onCommit($event)" (cancel)="onCancel()" />
1153
+ * </ng-template>
1154
+ * </tbw-grid-column-editor>
1155
+ * </tbw-grid-column>
1156
+ *
1157
+ * <!-- Use this cleaner syntax (with auto-wiring - no explicit bindings needed!): -->
1158
+ * <tbw-grid-column field="status">
1159
+ * <app-status-editor *tbwEditor="let value" [value]="value" />
1160
+ * </tbw-grid-column>
1161
+ * ```
1162
+ *
1163
+ * ## Template Context
1164
+ *
1165
+ * The structural directive provides the same context as `GridColumnEditor`:
1166
+ * - `$implicit` / `value`: The cell value
1167
+ * - `row`: The full row data object
1168
+ * - `column`: The column configuration
1169
+ * - `onCommit`: Callback function to commit the new value (optional - auto-wired if component emits `commit` event)
1170
+ * - `onCancel`: Callback function to cancel editing (optional - auto-wired if component emits `cancel` event)
1171
+ *
1172
+ * ## Import
1173
+ *
1174
+ * ```typescript
1175
+ * import { TbwEditor } from '@toolbox-web/grid-angular/features/editing';
1176
+ *
1177
+ * @Component({
1178
+ * imports: [TbwEditor],
1179
+ * // ...
1180
+ * })
1181
+ * ```
1182
+ *
1183
+ * @example
1184
+ * ```html
1185
+ * <tbw-grid-column field="status" editable>
1186
+ * <app-status-editor *tbwEditor="let value" [value]="value" />
1187
+ * </tbw-grid-column>
1188
+ * ```
1189
+ *
1190
+ * @category Directive
1191
+ * @since 0.1.1
1192
+ */
1193
+ declare class TbwEditor implements OnDestroy {
1194
+ private template;
1195
+ private elementRef;
1196
+ private columnElement;
1197
+ constructor();
1198
+ private registerTemplate;
1199
+ ngOnDestroy(): void;
1200
+ /**
1201
+ * Static type guard for template context.
1202
+ * Uses `any` defaults for ergonomic template usage.
1203
+ */
1204
+ static ngTemplateContextGuard(dir: TbwEditor, ctx: unknown): ctx is StructuralEditorContext;
1205
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<TbwEditor, never>;
1206
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<TbwEditor, "[tbwEditor]", never, {}, {}, never, never, true, never>;
1207
+ }
1208
+
1209
+ export { BaseGridEditor, BaseGridEditorCVA, BaseOverlayEditor, GridColumnEditor, GridEditingDirective, GridFormArray, GridLazyForm, TbwEditor, getEditorTemplate, getFormArrayContext, getLazyFormContext, getStructuralEditorTemplate };
1210
+ export type { FormArrayContext, GridEditorContext, LazyFormFactory, OverlayPosition, RowFormChangeEvent, StructuralEditorContext };
34
1211
  //# sourceMappingURL=toolbox-web-grid-angular-features-editing.d.ts.map