@toolbox-web/grid-angular 0.12.0 → 0.13.0

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.
@@ -2429,6 +2429,94 @@ function clearFeatureRegistry() {
2429
2429
  warnedFeatures.clear();
2430
2430
  }
2431
2431
 
2432
+ /**
2433
+ * Base class for Angular filter panel components.
2434
+ *
2435
+ * Provides a ready-made `params` input and common lifecycle helpers
2436
+ * (`applyAndClose`, `clearAndClose`) so consumers only need to implement
2437
+ * their filter logic in `applyFilter()`.
2438
+ *
2439
+ * ## Usage
2440
+ *
2441
+ * ```typescript
2442
+ * import { Component } from '@angular/core';
2443
+ * import { BaseFilterPanel } from '@toolbox-web/grid-angular';
2444
+ *
2445
+ * @Component({
2446
+ * selector: 'app-text-filter',
2447
+ * template: `
2448
+ * <input #input (keydown.enter)="applyAndClose()" />
2449
+ * <button (click)="applyAndClose()">Apply</button>
2450
+ * <button (click)="clearAndClose()">Clear</button>
2451
+ * `
2452
+ * })
2453
+ * export class TextFilterComponent extends BaseFilterPanel {
2454
+ * @ViewChild('input') input!: ElementRef<HTMLInputElement>;
2455
+ *
2456
+ * applyFilter(): void {
2457
+ * this.params().applyTextFilter('contains', this.input.nativeElement.value);
2458
+ * }
2459
+ * }
2460
+ * ```
2461
+ *
2462
+ * ## Template Syntax
2463
+ *
2464
+ * The grid's filtering plugin will mount this component and provide `params`
2465
+ * automatically. No manual wiring is required:
2466
+ *
2467
+ * ```typescript
2468
+ * gridConfig = {
2469
+ * columns: [
2470
+ * { field: 'name', filterable: true, filterPanel: TextFilterComponent },
2471
+ * ],
2472
+ * };
2473
+ * ```
2474
+ *
2475
+ * @typeParam TRow - The row data type (available via `params().column`)
2476
+ */
2477
+ class BaseFilterPanel {
2478
+ /**
2479
+ * Filter panel parameters injected by the grid's filtering plugin.
2480
+ *
2481
+ * Provides access to:
2482
+ * - `field` — the column field name
2483
+ * - `column` — full column configuration
2484
+ * - `uniqueValues` — distinct values in the column
2485
+ * - `excludedValues` — currently excluded values (set filter)
2486
+ * - `searchText` — current search text
2487
+ * - `applySetFilter(excluded)` — apply a set-based (include/exclude) filter
2488
+ * - `applyTextFilter(operator, value, valueTo?)` — apply a text/number filter
2489
+ * - `clearFilter()` — clear the filter for this column
2490
+ * - `closePanel()` — close the filter panel
2491
+ */
2492
+ params = input.required(...(ngDevMode ? [{ debugName: "params" }] : []));
2493
+ /**
2494
+ * Apply the filter then close the panel.
2495
+ *
2496
+ * Calls {@link applyFilter} followed by `params().closePanel()`.
2497
+ * Bind this to your "Apply" button or Enter key handler.
2498
+ */
2499
+ applyAndClose() {
2500
+ this.applyFilter();
2501
+ this.params().closePanel();
2502
+ }
2503
+ /**
2504
+ * Clear the filter then close the panel.
2505
+ *
2506
+ * Calls `params().clearFilter()` followed by `params().closePanel()`.
2507
+ * Bind this to your "Clear" / "Reset" button.
2508
+ */
2509
+ clearAndClose() {
2510
+ this.params().clearFilter();
2511
+ this.params().closePanel();
2512
+ }
2513
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: BaseFilterPanel, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2514
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.1", type: BaseFilterPanel, isStandalone: true, inputs: { params: { classPropertyName: "params", publicName: "params", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
2515
+ }
2516
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: BaseFilterPanel, decorators: [{
2517
+ type: Directive
2518
+ }], propDecorators: { params: [{ type: i0.Input, args: [{ isSignal: true, alias: "params", required: true }] }] } });
2519
+
2432
2520
  /**
2433
2521
  * Base class for grid cell editors.
2434
2522
  *
@@ -2699,6 +2787,703 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
2699
2787
  type: Directive
2700
2788
  }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], row: [{ type: i0.Input, args: [{ isSignal: true, alias: "row", required: false }] }], column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: false }] }], control: [{ type: i0.Input, args: [{ isSignal: true, alias: "control", required: false }] }], commit: [{ type: i0.Output, args: ["commit"] }], cancel: [{ type: i0.Output, args: ["cancel"] }] } });
2701
2789
 
2790
+ /**
2791
+ * Base class for grid editors that also work as Angular form controls.
2792
+ *
2793
+ * Combines `BaseGridEditor` with `ControlValueAccessor` so the same component
2794
+ * can be used inside a `<tbw-grid>` **and** in a standalone `<form>`.
2795
+ *
2796
+ * ## What it provides
2797
+ *
2798
+ * | Member | Purpose |
2799
+ * |--------|---------|
2800
+ * | `cvaValue` | Signal holding the value written by the form control |
2801
+ * | `disabledState` | Signal tracking `setDisabledState` calls |
2802
+ * | `displayValue` | Computed that prefers grid value (`currentValue`) and falls back to `cvaValue` |
2803
+ * | `commitBoth(v)` | Commits via both CVA `onChange` and grid `commitValue` |
2804
+ * | `writeValue` / `registerOn*` / `setDisabledState` | Full CVA implementation |
2805
+ *
2806
+ * ## Usage
2807
+ *
2808
+ * ```typescript
2809
+ * import { Component, forwardRef } from '@angular/core';
2810
+ * import { NG_VALUE_ACCESSOR } from '@angular/forms';
2811
+ * import { BaseGridEditorCVA } from '@toolbox-web/grid-angular';
2812
+ *
2813
+ * @Component({
2814
+ * selector: 'app-date-picker',
2815
+ * providers: [{
2816
+ * provide: NG_VALUE_ACCESSOR,
2817
+ * useExisting: forwardRef(() => DatePickerComponent),
2818
+ * multi: true,
2819
+ * }],
2820
+ * template: `
2821
+ * <input
2822
+ * type="date"
2823
+ * [value]="displayValue()"
2824
+ * [disabled]="disabledState()"
2825
+ * (change)="commitBoth($event.target.value)"
2826
+ * (keydown.escape)="cancelEdit()"
2827
+ * />
2828
+ * `
2829
+ * })
2830
+ * export class DatePickerComponent extends BaseGridEditorCVA<MyRow, string> {}
2831
+ * ```
2832
+ *
2833
+ * > **Note:** Subclasses must still provide `NG_VALUE_ACCESSOR` themselves
2834
+ * > because `forwardRef(() => ConcreteClass)` must reference the concrete
2835
+ * > component — this is an Angular limitation.
2836
+ *
2837
+ * @typeParam TRow - The row data type
2838
+ * @typeParam TValue - The cell/control value type
2839
+ */
2840
+ class BaseGridEditorCVA extends BaseGridEditor {
2841
+ // ============================================================================
2842
+ // CVA State
2843
+ // ============================================================================
2844
+ /** Internal onChange callback registered by the form control. */
2845
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2846
+ _onChange = () => { };
2847
+ /** Internal onTouched callback registered by the form control. */
2848
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2849
+ _onTouched = () => { };
2850
+ /**
2851
+ * Signal holding the value written by the form control via `writeValue()`.
2852
+ * Updated when the form control pushes a new value (e.g. `patchValue`, `setValue`).
2853
+ */
2854
+ cvaValue = signal(null, ...(ngDevMode ? [{ debugName: "cvaValue" }] : []));
2855
+ /**
2856
+ * Signal tracking the disabled state set by the form control.
2857
+ * Updated when `setDisabledState()` is called by Angular's forms module.
2858
+ */
2859
+ disabledState = signal(false, ...(ngDevMode ? [{ debugName: "disabledState" }] : []));
2860
+ /**
2861
+ * Resolved display value.
2862
+ *
2863
+ * Prefers `currentValue()` (grid context — from `control.value` or `value` input)
2864
+ * and falls back to `cvaValue()` (standalone form context — from `writeValue`).
2865
+ *
2866
+ * Use this in your template instead of reading `currentValue()` directly
2867
+ * so the component works in both grid and standalone form contexts.
2868
+ */
2869
+ displayValue = computed(() => {
2870
+ return this.currentValue() ?? this.cvaValue();
2871
+ }, ...(ngDevMode ? [{ debugName: "displayValue" }] : []));
2872
+ // ============================================================================
2873
+ // ControlValueAccessor Implementation
2874
+ // ============================================================================
2875
+ /**
2876
+ * Called by Angular forms when the form control value changes programmatically.
2877
+ */
2878
+ writeValue(value) {
2879
+ this.cvaValue.set(value);
2880
+ }
2881
+ /**
2882
+ * Called by Angular forms to register a change callback.
2883
+ */
2884
+ registerOnChange(fn) {
2885
+ this._onChange = fn;
2886
+ }
2887
+ /**
2888
+ * Called by Angular forms to register a touched callback.
2889
+ */
2890
+ registerOnTouched(fn) {
2891
+ this._onTouched = fn;
2892
+ }
2893
+ /**
2894
+ * Called by Angular forms to set the disabled state.
2895
+ */
2896
+ setDisabledState(isDisabled) {
2897
+ this.disabledState.set(isDisabled);
2898
+ }
2899
+ // ============================================================================
2900
+ // Dual-Commit Helpers
2901
+ // ============================================================================
2902
+ /**
2903
+ * Commit a value through both the CVA (form control) and the grid.
2904
+ *
2905
+ * - Calls the CVA `onChange` callback (updates the form control)
2906
+ * - Marks the control as touched
2907
+ * - Calls `commitValue()` (emits grid commit event + DOM `CustomEvent`)
2908
+ *
2909
+ * Use this instead of `commitValue()` when your editor doubles as a form control.
2910
+ *
2911
+ * @param value - The new value to commit
2912
+ */
2913
+ commitBoth(value) {
2914
+ // Update CVA
2915
+ this.cvaValue.set(value);
2916
+ this._onChange(value);
2917
+ this._onTouched();
2918
+ // Update grid
2919
+ if (value != null) {
2920
+ this.commitValue(value);
2921
+ }
2922
+ }
2923
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: BaseGridEditorCVA, deps: null, target: i0.ɵɵFactoryTarget.Directive });
2924
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.1", type: BaseGridEditorCVA, isStandalone: true, usesInheritance: true, ngImport: i0 });
2925
+ }
2926
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: BaseGridEditorCVA, decorators: [{
2927
+ type: Directive
2928
+ }] });
2929
+
2930
+ // #endregion
2931
+ // #region Global Styles
2932
+ /** Tracks whether the global overlay stylesheet has been injected. */
2933
+ let overlayStylesInjected = false;
2934
+ /**
2935
+ * CSS for the overlay panel base layer.
2936
+ * Injected once into `<head>` on first `BaseOverlayEditor` use.
2937
+ *
2938
+ * Uses CSS Anchor Positioning as primary strategy with a JS fallback
2939
+ * for browsers that don't support it (Firefox, Safari as of late 2025).
2940
+ */
2941
+ const OVERLAY_STYLES = /* css */ `
2942
+ .tbw-overlay-panel {
2943
+ position: fixed;
2944
+ z-index: 10000;
2945
+ background: var(--tbw-overlay-bg, #fff);
2946
+ border: 1px solid var(--tbw-overlay-border, #ccc);
2947
+ border-radius: var(--tbw-overlay-radius, 4px);
2948
+ box-shadow: var(--tbw-overlay-shadow, 0 4px 12px rgba(0, 0, 0, 0.15));
2949
+ box-sizing: border-box;
2950
+ overflow: auto;
2951
+ }
2952
+
2953
+ .tbw-overlay-panel:popover-open {
2954
+ display: block;
2955
+ }
2956
+
2957
+ @supports (anchor-name: --a) {
2958
+ .tbw-overlay-panel[data-anchor-id] {
2959
+ position: fixed;
2960
+ position-anchor: var(--tbw-overlay-anchor);
2961
+ inset: unset;
2962
+ }
2963
+ .tbw-overlay-panel[data-pos="below"] {
2964
+ top: anchor(bottom);
2965
+ left: anchor(left);
2966
+ position-try-fallbacks: flip-block;
2967
+ }
2968
+ .tbw-overlay-panel[data-pos="above"] {
2969
+ bottom: anchor(top);
2970
+ left: anchor(left);
2971
+ position-try-fallbacks: flip-block;
2972
+ }
2973
+ .tbw-overlay-panel[data-pos="below-right"] {
2974
+ top: anchor(bottom);
2975
+ right: anchor(right);
2976
+ position-try-fallbacks: flip-block;
2977
+ }
2978
+ .tbw-overlay-panel[data-pos="over-left"] {
2979
+ top: anchor(top);
2980
+ left: anchor(left);
2981
+ }
2982
+ }
2983
+ `;
2984
+ function ensureOverlayStyles() {
2985
+ if (overlayStylesInjected)
2986
+ return;
2987
+ overlayStylesInjected = true;
2988
+ const style = document.createElement('style');
2989
+ style.setAttribute('data-tbw-overlay', '');
2990
+ style.textContent = OVERLAY_STYLES;
2991
+ document.head.appendChild(style);
2992
+ }
2993
+ // #endregion
2994
+ // #region Anchor ID Counter
2995
+ let anchorCounter = 0;
2996
+ // #endregion
2997
+ /**
2998
+ * Base class for grid editors that display a floating overlay panel.
2999
+ *
3000
+ * Provides infrastructure for:
3001
+ * - **Overlay positioning** — CSS Anchor Positioning with JS fallback
3002
+ * - **Focus gating** — in row editing mode, the panel only opens for the focused cell
3003
+ * - **Click-outside detection** — closes the panel when clicking outside
3004
+ * - **MutationObserver** — detects cell focus changes (row editing mode)
3005
+ * - **Escape handling** — closes the panel and returns focus to the inline input
3006
+ * - **Synthetic Tab dispatch** — advances grid focus after overlay close
3007
+ * - **Automatic teardown** — removes the panel from `<body>` and cleans up listeners
3008
+ *
3009
+ * ## Usage
3010
+ *
3011
+ * ```typescript
3012
+ * import { Component, ViewChild, ElementRef } from '@angular/core';
3013
+ * import { BaseOverlayEditor } from '@toolbox-web/grid-angular';
3014
+ *
3015
+ * @Component({
3016
+ * selector: 'app-date-editor',
3017
+ * template: `
3018
+ * <input
3019
+ * #inlineInput
3020
+ * readonly
3021
+ * [value]="currentValue()"
3022
+ * (click)="onInlineClick()"
3023
+ * (keydown)="onInlineKeydown($event)"
3024
+ * />
3025
+ * <div #panel class="tbw-overlay-panel" style="width: 280px;">
3026
+ * <!-- your date picker UI here -->
3027
+ * <div class="actions">
3028
+ * <button (click)="selectAndClose(selectedDate)">OK</button>
3029
+ * <button (click)="hideOverlay()">Cancel</button>
3030
+ * </div>
3031
+ * </div>
3032
+ * `
3033
+ * })
3034
+ * export class DateEditorComponent extends BaseOverlayEditor<MyRow, string> {
3035
+ * @ViewChild('panel') panelRef!: ElementRef<HTMLElement>;
3036
+ * @ViewChild('inlineInput') inputRef!: ElementRef<HTMLInputElement>;
3037
+ *
3038
+ * protected override overlayPosition = 'below' as const;
3039
+ *
3040
+ * ngAfterViewInit(): void {
3041
+ * this.initOverlay(this.panelRef.nativeElement);
3042
+ * if (this.isCellFocused()) this.showOverlay();
3043
+ * }
3044
+ *
3045
+ * protected getInlineInput(): HTMLInputElement | null {
3046
+ * return this.inputRef?.nativeElement ?? null;
3047
+ * }
3048
+ *
3049
+ * protected onOverlayOutsideClick(): void {
3050
+ * this.hideOverlay();
3051
+ * }
3052
+ *
3053
+ * selectAndClose(date: string): void {
3054
+ * this.commitValue(date);
3055
+ * this.hideOverlay();
3056
+ * }
3057
+ * }
3058
+ * ```
3059
+ *
3060
+ * @typeParam TRow - The row data type
3061
+ * @typeParam TValue - The cell value type
3062
+ */
3063
+ class BaseOverlayEditor extends BaseGridEditor {
3064
+ _elementRef = inject(ElementRef);
3065
+ _overlayDestroyRef = inject(DestroyRef);
3066
+ // ============================================================================
3067
+ // Configuration
3068
+ // ============================================================================
3069
+ /**
3070
+ * Position of the overlay panel relative to the anchor cell.
3071
+ * Override in subclasses to change the default position.
3072
+ *
3073
+ * @default 'below'
3074
+ */
3075
+ overlayPosition = 'below';
3076
+ // ============================================================================
3077
+ // Internal State
3078
+ // ============================================================================
3079
+ /** The overlay panel element (set via `initOverlay()`). */
3080
+ _panel = null;
3081
+ /** Whether the overlay is currently visible. */
3082
+ _isOpen = false;
3083
+ /** Unique anchor ID for CSS Anchor Positioning. */
3084
+ _anchorId = '';
3085
+ /** Whether the browser supports CSS Anchor Positioning. */
3086
+ _supportsAnchor = false;
3087
+ /** AbortController for all overlay-related listeners. */
3088
+ _abortCtrl = null;
3089
+ /** MutationObserver watching cell focus class changes. */
3090
+ _focusObserver = null;
3091
+ // ============================================================================
3092
+ // Lifecycle
3093
+ // ============================================================================
3094
+ constructor() {
3095
+ super();
3096
+ this._supportsAnchor = typeof CSS !== 'undefined' && CSS.supports('anchor-name', '--a');
3097
+ ensureOverlayStyles();
3098
+ afterNextRender(() => this._setupFocusObserver());
3099
+ this._overlayDestroyRef.onDestroy(() => this.teardownOverlay());
3100
+ }
3101
+ // ============================================================================
3102
+ // Public API — Subclass Interface
3103
+ // ============================================================================
3104
+ /**
3105
+ * Initialise the overlay with the panel element.
3106
+ *
3107
+ * Call this in `ngAfterViewInit` with your `@ViewChild` panel reference.
3108
+ * The panel is moved to `<body>` and hidden until {@link showOverlay} is called.
3109
+ *
3110
+ * @param panel - The overlay panel DOM element
3111
+ */
3112
+ initOverlay(panel) {
3113
+ this._panel = panel;
3114
+ // Assign a unique anchor ID
3115
+ this._anchorId = `tbw-anchor-${++anchorCounter}`;
3116
+ panel.classList.add('tbw-overlay-panel');
3117
+ panel.setAttribute('data-pos', this.overlayPosition);
3118
+ panel.setAttribute('data-anchor-id', this._anchorId);
3119
+ panel.style.display = 'none';
3120
+ // Set up CSS Anchor Positioning on the cell
3121
+ if (this._supportsAnchor) {
3122
+ const cell = this._getCell();
3123
+ if (cell) {
3124
+ cell.style.setProperty('anchor-name', `--${this._anchorId}`);
3125
+ panel.style.setProperty('--tbw-overlay-anchor', `--${this._anchorId}`);
3126
+ }
3127
+ }
3128
+ // Move panel to body so it escapes grid overflow clipping
3129
+ document.body.appendChild(panel);
3130
+ // Set up click-outside detection
3131
+ this._abortCtrl = new AbortController();
3132
+ document.addEventListener('pointerdown', (e) => this._onDocumentPointerDown(e), {
3133
+ signal: this._abortCtrl.signal,
3134
+ });
3135
+ }
3136
+ /**
3137
+ * Show the overlay panel.
3138
+ *
3139
+ * If CSS Anchor Positioning is not supported, falls back to JS-based
3140
+ * positioning using `getBoundingClientRect()`.
3141
+ */
3142
+ showOverlay() {
3143
+ if (!this._panel || this._isOpen)
3144
+ return;
3145
+ this._isOpen = true;
3146
+ this._panel.style.display = '';
3147
+ // JS fallback positioning for browsers without CSS Anchor Positioning
3148
+ if (!this._supportsAnchor) {
3149
+ this._positionWithJs();
3150
+ }
3151
+ }
3152
+ /**
3153
+ * Hide the overlay panel.
3154
+ *
3155
+ * @param suppressTabAdvance - When `true`, skip synthetic Tab dispatch
3156
+ * (useful when hiding is triggered by an external focus change).
3157
+ */
3158
+ hideOverlay(suppressTabAdvance) {
3159
+ if (!this._panel || !this._isOpen)
3160
+ return;
3161
+ this._isOpen = false;
3162
+ this._panel.style.display = 'none';
3163
+ if (!suppressTabAdvance) {
3164
+ this.getInlineInput()?.focus();
3165
+ }
3166
+ }
3167
+ /**
3168
+ * Close and immediately re-open the overlay.
3169
+ * Useful after the panel content changes size and needs repositioning.
3170
+ */
3171
+ reopenOverlay() {
3172
+ if (!this._panel)
3173
+ return;
3174
+ this._isOpen = false;
3175
+ this._panel.style.display = 'none';
3176
+ this.showOverlay();
3177
+ }
3178
+ /**
3179
+ * Remove the overlay from the DOM and clean up all listeners.
3180
+ *
3181
+ * Called automatically on `DestroyRef.onDestroy`. Can also be called
3182
+ * manually if the editor needs early cleanup.
3183
+ */
3184
+ teardownOverlay() {
3185
+ this._abortCtrl?.abort();
3186
+ this._abortCtrl = null;
3187
+ this._focusObserver?.disconnect();
3188
+ this._focusObserver = null;
3189
+ if (this._panel?.parentNode) {
3190
+ this._panel.parentNode.removeChild(this._panel);
3191
+ }
3192
+ this._panel = null;
3193
+ this._isOpen = false;
3194
+ // Clean up anchor-name on the cell
3195
+ if (this._supportsAnchor) {
3196
+ const cell = this._getCell();
3197
+ if (cell) {
3198
+ cell.style.removeProperty('anchor-name');
3199
+ }
3200
+ }
3201
+ }
3202
+ /**
3203
+ * Override in `edit-close` handler to also hide the overlay.
3204
+ * This is called automatically by `BaseGridEditor` when the grid
3205
+ * ends the editing session.
3206
+ */
3207
+ onEditClose() {
3208
+ this.hideOverlay(true);
3209
+ }
3210
+ // ============================================================================
3211
+ // Keyboard & Click Helpers
3212
+ // ============================================================================
3213
+ /**
3214
+ * Keydown handler for the inline readonly input.
3215
+ *
3216
+ * - **Enter / Space / ArrowDown / F2** → open overlay
3217
+ * - **Escape** → calls {@link handleEscape}
3218
+ *
3219
+ * Bind this to `(keydown)` on your inline input element.
3220
+ */
3221
+ onInlineKeydown(event) {
3222
+ switch (event.key) {
3223
+ case 'Enter':
3224
+ case ' ':
3225
+ case 'ArrowDown':
3226
+ case 'F2':
3227
+ event.preventDefault();
3228
+ this.showOverlay();
3229
+ this.onOverlayOpened();
3230
+ break;
3231
+ case 'Escape':
3232
+ this.handleEscape(event);
3233
+ break;
3234
+ }
3235
+ }
3236
+ /**
3237
+ * Click handler for the inline input.
3238
+ * Opens the overlay and calls {@link onOverlayOpened}.
3239
+ *
3240
+ * Bind this to `(click)` on your inline input element.
3241
+ */
3242
+ onInlineClick() {
3243
+ if (this._isOpen) {
3244
+ this.hideOverlay();
3245
+ }
3246
+ else {
3247
+ this.showOverlay();
3248
+ this.onOverlayOpened();
3249
+ }
3250
+ }
3251
+ /**
3252
+ * Handle Escape key press.
3253
+ *
3254
+ * If the overlay is open, closes it and returns focus to the inline input.
3255
+ * If the overlay is already closed, cancels the edit entirely.
3256
+ */
3257
+ handleEscape(event) {
3258
+ if (this._isOpen) {
3259
+ event.stopPropagation();
3260
+ this.hideOverlay();
3261
+ }
3262
+ else {
3263
+ this.cancelEdit();
3264
+ }
3265
+ }
3266
+ /**
3267
+ * Dispatch a synthetic Tab key event to advance grid focus.
3268
+ *
3269
+ * Call this after committing a value and closing the overlay so the
3270
+ * grid moves focus to the next cell.
3271
+ *
3272
+ * @param backward - When `true`, dispatch Shift+Tab to move backwards.
3273
+ */
3274
+ advanceGridFocus(backward = false) {
3275
+ const cell = this._getCell();
3276
+ if (!cell)
3277
+ return;
3278
+ cell.dispatchEvent(new KeyboardEvent('keydown', {
3279
+ key: 'Tab',
3280
+ shiftKey: backward,
3281
+ bubbles: true,
3282
+ cancelable: true,
3283
+ }));
3284
+ }
3285
+ /**
3286
+ * Called after the overlay is shown.
3287
+ *
3288
+ * Override to focus an element inside the panel, start animations, etc.
3289
+ * Default implementation is a no-op.
3290
+ */
3291
+ onOverlayOpened() {
3292
+ // Default: no-op. Subclasses override.
3293
+ }
3294
+ // ============================================================================
3295
+ // Private Helpers
3296
+ // ============================================================================
3297
+ /** Find the parent cell element for this editor. */
3298
+ _getCell() {
3299
+ return this._elementRef.nativeElement.closest('[part="cell"]') ?? null;
3300
+ }
3301
+ /**
3302
+ * JS fallback positioning for browsers without CSS Anchor Positioning.
3303
+ * Uses `getBoundingClientRect()` with viewport overflow detection.
3304
+ */
3305
+ _positionWithJs() {
3306
+ const cell = this._getCell();
3307
+ const panel = this._panel;
3308
+ if (!cell || !panel)
3309
+ return;
3310
+ const cellRect = cell.getBoundingClientRect();
3311
+ // Temporarily make visible to measure
3312
+ panel.style.visibility = 'hidden';
3313
+ panel.style.display = '';
3314
+ const panelRect = panel.getBoundingClientRect();
3315
+ panel.style.visibility = '';
3316
+ const viewportH = window.innerHeight;
3317
+ const viewportW = window.innerWidth;
3318
+ let top;
3319
+ let left;
3320
+ switch (this.overlayPosition) {
3321
+ case 'above': {
3322
+ top = cellRect.top - panelRect.height;
3323
+ left = cellRect.left;
3324
+ // Flip to below if off-screen
3325
+ if (top < 0)
3326
+ top = cellRect.bottom;
3327
+ break;
3328
+ }
3329
+ case 'below-right': {
3330
+ top = cellRect.bottom;
3331
+ left = cellRect.right - panelRect.width;
3332
+ // Flip to above if off-screen
3333
+ if (top + panelRect.height > viewportH)
3334
+ top = cellRect.top - panelRect.height;
3335
+ break;
3336
+ }
3337
+ case 'over-left': {
3338
+ top = cellRect.top;
3339
+ left = cellRect.left;
3340
+ break;
3341
+ }
3342
+ case 'below':
3343
+ default: {
3344
+ top = cellRect.bottom;
3345
+ left = cellRect.left;
3346
+ // Flip to above if off-screen
3347
+ if (top + panelRect.height > viewportH)
3348
+ top = cellRect.top - panelRect.height;
3349
+ break;
3350
+ }
3351
+ }
3352
+ // Clamp to viewport
3353
+ if (left + panelRect.width > viewportW)
3354
+ left = viewportW - panelRect.width - 4;
3355
+ if (left < 0)
3356
+ left = 4;
3357
+ if (top < 0)
3358
+ top = 4;
3359
+ panel.style.top = `${top}px`;
3360
+ panel.style.left = `${left}px`;
3361
+ }
3362
+ /**
3363
+ * Document pointerdown handler for click-outside detection.
3364
+ * Fires `onOverlayOutsideClick()` if the click is outside the panel
3365
+ * and outside the editor's host element.
3366
+ */
3367
+ _onDocumentPointerDown(event) {
3368
+ if (!this._isOpen || !this._panel)
3369
+ return;
3370
+ const target = event.target;
3371
+ const hostEl = this._elementRef.nativeElement;
3372
+ // Click inside panel or host — ignore
3373
+ if (this._panel.contains(target) || hostEl.contains(target))
3374
+ return;
3375
+ this.onOverlayOutsideClick();
3376
+ }
3377
+ /**
3378
+ * Set up a MutationObserver on the parent cell to watch for
3379
+ * `cell-focus` class changes. This handles row-editing mode where
3380
+ * all editors exist simultaneously but only the focused cell's
3381
+ * editor should have its overlay visible.
3382
+ */
3383
+ _setupFocusObserver() {
3384
+ const cell = this._getCell();
3385
+ if (!cell)
3386
+ return;
3387
+ this._focusObserver = new MutationObserver((mutations) => {
3388
+ for (const mutation of mutations) {
3389
+ if (mutation.type !== 'attributes' || mutation.attributeName !== 'class')
3390
+ continue;
3391
+ const isFocused = cell.classList.contains('cell-focus');
3392
+ if (isFocused && !this._isOpen) {
3393
+ // Cell just gained focus — open overlay if appropriate
3394
+ this.showOverlay();
3395
+ this.onOverlayOpened();
3396
+ }
3397
+ else if (!isFocused && this._isOpen) {
3398
+ // Cell lost focus — hide overlay silently
3399
+ this.hideOverlay(true);
3400
+ }
3401
+ }
3402
+ });
3403
+ this._focusObserver.observe(cell, { attributes: true, attributeFilter: ['class'] });
3404
+ }
3405
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: BaseOverlayEditor, deps: [], target: i0.ɵɵFactoryTarget.Directive });
3406
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.1", type: BaseOverlayEditor, isStandalone: true, usesInheritance: true, ngImport: i0 });
3407
+ }
3408
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: BaseOverlayEditor, decorators: [{
3409
+ type: Directive
3410
+ }], ctorParameters: () => [] });
3411
+
3412
+ /**
3413
+ * Directive that registers `<tbw-grid-column>` as a known Angular element.
3414
+ *
3415
+ * This directive exists so that Angular's template compiler recognises
3416
+ * `<tbw-grid-column>` without requiring `CUSTOM_ELEMENTS_SCHEMA`.
3417
+ * The underlying web component reads its attributes (`field`, `header`,
3418
+ * `type`, `width`, etc.) directly from the DOM, so no `@Input()` forwarding
3419
+ * is needed — Angular's standard property/attribute binding handles it.
3420
+ *
3421
+ * ## Usage
3422
+ *
3423
+ * ```typescript
3424
+ * import { Component } from '@angular/core';
3425
+ * import { Grid, TbwGridColumn, TbwRenderer } from '@toolbox-web/grid-angular';
3426
+ *
3427
+ * @Component({
3428
+ * imports: [Grid, TbwGridColumn, TbwRenderer],
3429
+ * template: `
3430
+ * <tbw-grid [rows]="rows" [gridConfig]="config">
3431
+ * <tbw-grid-column field="status">
3432
+ * <app-status-badge *tbwRenderer="let value" [value]="value" />
3433
+ * </tbw-grid-column>
3434
+ * </tbw-grid>
3435
+ * `
3436
+ * })
3437
+ * export class MyComponent { }
3438
+ * ```
3439
+ */
3440
+ class TbwGridColumn {
3441
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: TbwGridColumn, deps: [], target: i0.ɵɵFactoryTarget.Directive });
3442
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.1", type: TbwGridColumn, isStandalone: true, selector: "tbw-grid-column", ngImport: i0 });
3443
+ }
3444
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: TbwGridColumn, decorators: [{
3445
+ type: Directive,
3446
+ args: [{
3447
+ selector: 'tbw-grid-column',
3448
+ }]
3449
+ }] });
3450
+
3451
+ /**
3452
+ * Directive that registers `<tbw-grid-header>` as a known Angular element.
3453
+ *
3454
+ * This directive exists so that Angular's template compiler recognises
3455
+ * `<tbw-grid-header>` without requiring `CUSTOM_ELEMENTS_SCHEMA`.
3456
+ * The grid's `config-manager` reads attributes like `title` directly
3457
+ * from the DOM element.
3458
+ *
3459
+ * ## Usage
3460
+ *
3461
+ * ```typescript
3462
+ * import { Component } from '@angular/core';
3463
+ * import { Grid, TbwGridHeader } from '@toolbox-web/grid-angular';
3464
+ *
3465
+ * @Component({
3466
+ * imports: [Grid, TbwGridHeader],
3467
+ * template: `
3468
+ * <tbw-grid [rows]="rows" [gridConfig]="config">
3469
+ * <tbw-grid-header title="My Grid Title"></tbw-grid-header>
3470
+ * </tbw-grid>
3471
+ * `
3472
+ * })
3473
+ * export class MyComponent { }
3474
+ * ```
3475
+ */
3476
+ class TbwGridHeader {
3477
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: TbwGridHeader, deps: [], target: i0.ɵɵFactoryTarget.Directive });
3478
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.1", type: TbwGridHeader, isStandalone: true, selector: "tbw-grid-header", ngImport: i0 });
3479
+ }
3480
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: TbwGridHeader, decorators: [{
3481
+ type: Directive,
3482
+ args: [{
3483
+ selector: 'tbw-grid-header',
3484
+ }]
3485
+ }] });
3486
+
2702
3487
  // Symbol for storing form context on the grid element (shared with GridFormArray)
2703
3488
  const FORM_ARRAY_CONTEXT = Symbol('formArrayContext');
2704
3489
  /**
@@ -3183,6 +3968,43 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
3183
3968
  }]
3184
3969
  }], propDecorators: { lazyForm: [{ type: i0.Input, args: [{ isSignal: true, alias: "lazyForm", required: true }] }], syncValidation: [{ type: i0.Input, args: [{ isSignal: true, alias: "syncValidation", required: false }] }], keepFormGroups: [{ type: i0.Input, args: [{ isSignal: true, alias: "keepFormGroups", required: false }] }], rowFormChange: [{ type: i0.Output, args: ["rowFormChange"] }] } });
3185
3970
 
3971
+ /**
3972
+ * Directive that registers `<tbw-grid-tool-buttons>` as a known Angular element.
3973
+ *
3974
+ * This directive exists so that Angular's template compiler recognises
3975
+ * `<tbw-grid-tool-buttons>` without requiring `CUSTOM_ELEMENTS_SCHEMA`.
3976
+ * The grid's shell reads toolbar buttons directly from the DOM.
3977
+ *
3978
+ * ## Usage
3979
+ *
3980
+ * ```typescript
3981
+ * import { Component } from '@angular/core';
3982
+ * import { Grid, TbwGridToolButtons } from '@toolbox-web/grid-angular';
3983
+ *
3984
+ * @Component({
3985
+ * imports: [Grid, TbwGridToolButtons],
3986
+ * template: `
3987
+ * <tbw-grid [rows]="rows" [gridConfig]="config">
3988
+ * <tbw-grid-tool-buttons>
3989
+ * <button (click)="doSomething()">Action</button>
3990
+ * </tbw-grid-tool-buttons>
3991
+ * </tbw-grid>
3992
+ * `
3993
+ * })
3994
+ * export class MyComponent { }
3995
+ * ```
3996
+ */
3997
+ class TbwGridToolButtons {
3998
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: TbwGridToolButtons, deps: [], target: i0.ɵɵFactoryTarget.Directive });
3999
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.1", type: TbwGridToolButtons, isStandalone: true, selector: "tbw-grid-tool-buttons", ngImport: i0 });
4000
+ }
4001
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: TbwGridToolButtons, decorators: [{
4002
+ type: Directive,
4003
+ args: [{
4004
+ selector: 'tbw-grid-tool-buttons',
4005
+ }]
4006
+ }] });
4007
+
3186
4008
  /**
3187
4009
  * Directive that automatically registers the Angular adapter with tbw-grid elements.
3188
4010
  *
@@ -3192,13 +4014,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
3192
4014
  * ## Usage
3193
4015
  *
3194
4016
  * ```typescript
3195
- * import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
4017
+ * import { Component } from '@angular/core';
3196
4018
  * import { Grid } from '@toolbox-web/grid-angular';
3197
4019
  *
3198
4020
  * @Component({
3199
4021
  * selector: 'app-root',
3200
4022
  * imports: [Grid],
3201
- * schemas: [CUSTOM_ELEMENTS_SCHEMA],
3202
4023
  * template: `
3203
4024
  * <tbw-grid [rows]="rows" [gridConfig]="config" [customStyles]="myStyles">
3204
4025
  * <!-- column templates -->
@@ -3292,6 +4113,30 @@ class Grid {
3292
4113
  const grid = this.elementRef.nativeElement;
3293
4114
  grid.loading = loadingValue;
3294
4115
  });
4116
+ // Effect to sync rows to the grid element
4117
+ effect(() => {
4118
+ const rowsValue = this.rows();
4119
+ if (rowsValue === undefined)
4120
+ return;
4121
+ const grid = this.elementRef.nativeElement;
4122
+ grid.rows = rowsValue;
4123
+ });
4124
+ // Effect to sync columns to the grid element
4125
+ effect(() => {
4126
+ const columnsValue = this.columns();
4127
+ if (columnsValue === undefined)
4128
+ return;
4129
+ const grid = this.elementRef.nativeElement;
4130
+ grid.columns = columnsValue;
4131
+ });
4132
+ // Effect to sync fitMode to the grid element
4133
+ effect(() => {
4134
+ const fitModeValue = this.fitMode();
4135
+ if (fitModeValue === undefined)
4136
+ return;
4137
+ const grid = this.elementRef.nativeElement;
4138
+ grid.fitMode = fitModeValue;
4139
+ });
3295
4140
  }
3296
4141
  /**
3297
4142
  * Custom CSS styles to inject into the grid.
@@ -3400,6 +4245,53 @@ class Grid {
3400
4245
  * ```
3401
4246
  */
3402
4247
  loading = input(...(ngDevMode ? [undefined, { debugName: "loading" }] : []));
4248
+ /**
4249
+ * The data rows to display in the grid.
4250
+ *
4251
+ * Accepts an array of data objects. Each object represents one row.
4252
+ * The grid reads property values for each column's `field` from these objects.
4253
+ *
4254
+ * @example
4255
+ * ```html
4256
+ * <tbw-grid [rows]="employees()" [gridConfig]="config" />
4257
+ * ```
4258
+ */
4259
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
4260
+ rows = input(...(ngDevMode ? [undefined, { debugName: "rows" }] : []));
4261
+ /**
4262
+ * Column configuration array.
4263
+ *
4264
+ * Shorthand for setting columns without wrapping them in a full `gridConfig`.
4265
+ * If both `columns` and `gridConfig.columns` are set, `columns` takes precedence
4266
+ * (see configuration precedence system).
4267
+ *
4268
+ * @example
4269
+ * ```html
4270
+ * <tbw-grid [rows]="data" [columns]="[
4271
+ * { field: 'id', header: 'ID', pinned: 'left', width: 80 },
4272
+ * { field: 'name', header: 'Name' },
4273
+ * { field: 'email', header: 'Email' }
4274
+ * ]" />
4275
+ * ```
4276
+ */
4277
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
4278
+ columns = input(...(ngDevMode ? [undefined, { debugName: "columns" }] : []));
4279
+ /**
4280
+ * Column sizing strategy.
4281
+ *
4282
+ * - `'stretch'` (default) — columns stretch to fill available width
4283
+ * - `'fixed'` — columns use their declared widths; enables horizontal scrolling
4284
+ * - `'auto-fit'` — columns auto-size to content, then stretch to fill
4285
+ *
4286
+ * @default 'stretch'
4287
+ *
4288
+ * @example
4289
+ * ```html
4290
+ * <tbw-grid [rows]="data" fitMode="fixed" />
4291
+ * <tbw-grid [rows]="data" [fitMode]="dynamicMode()" />
4292
+ * ```
4293
+ */
4294
+ fitMode = input(...(ngDevMode ? [undefined, { debugName: "fitMode" }] : []));
3403
4295
  /**
3404
4296
  * Grid configuration object with optional Angular-specific extensions.
3405
4297
  *
@@ -3618,9 +4510,9 @@ class Grid {
3618
4510
  * @example
3619
4511
  * ```html
3620
4512
  * <tbw-grid [pinnedColumns]="true" [columns]="[
3621
- * { field: 'id', sticky: 'left' },
4513
+ * { field: 'id', pinned: 'left' },
3622
4514
  * { field: 'name' },
3623
- * { field: 'actions', sticky: 'right' }
4515
+ * { field: 'actions', pinned: 'right' }
3624
4516
  * ]" />
3625
4517
  * ```
3626
4518
  */
@@ -4330,12 +5222,12 @@ class Grid {
4330
5222
  }
4331
5223
  }
4332
5224
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: Grid, deps: [], target: i0.ɵɵFactoryTarget.Directive });
4333
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.1", type: Grid, isStandalone: true, selector: "tbw-grid", inputs: { customStyles: { classPropertyName: "customStyles", publicName: "customStyles", isSignal: true, isRequired: false, transformFunction: null }, sortable: { classPropertyName: "sortable", publicName: "sortable", isSignal: true, isRequired: false, transformFunction: null }, filterable: { classPropertyName: "filterable", publicName: "filterable", isSignal: true, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, gridConfig: { classPropertyName: "gridConfig", publicName: "gridConfig", isSignal: true, isRequired: false, transformFunction: null }, angularConfig: { classPropertyName: "angularConfig", publicName: "angularConfig", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, editing: { classPropertyName: "editing", publicName: "editing", isSignal: true, isRequired: false, transformFunction: null }, clipboard: { classPropertyName: "clipboard", publicName: "clipboard", isSignal: true, isRequired: false, transformFunction: null }, contextMenu: { classPropertyName: "contextMenu", publicName: "contextMenu", isSignal: true, isRequired: false, transformFunction: null }, multiSort: { classPropertyName: "multiSort", publicName: "multiSort", isSignal: true, isRequired: false, transformFunction: null }, sorting: { classPropertyName: "sorting", publicName: "sorting", isSignal: true, isRequired: false, transformFunction: null }, filtering: { classPropertyName: "filtering", publicName: "filtering", isSignal: true, isRequired: false, transformFunction: null }, reorder: { classPropertyName: "reorder", publicName: "reorder", isSignal: true, isRequired: false, transformFunction: null }, visibility: { classPropertyName: "visibility", publicName: "visibility", isSignal: true, isRequired: false, transformFunction: null }, pinnedColumns: { classPropertyName: "pinnedColumns", publicName: "pinnedColumns", isSignal: true, isRequired: false, transformFunction: null }, groupingColumns: { classPropertyName: "groupingColumns", publicName: "groupingColumns", isSignal: true, isRequired: false, transformFunction: null }, columnVirtualization: { classPropertyName: "columnVirtualization", publicName: "columnVirtualization", isSignal: true, isRequired: false, transformFunction: null }, rowReorder: { classPropertyName: "rowReorder", publicName: "rowReorder", isSignal: true, isRequired: false, transformFunction: null }, groupingRows: { classPropertyName: "groupingRows", publicName: "groupingRows", isSignal: true, isRequired: false, transformFunction: null }, pinnedRows: { classPropertyName: "pinnedRows", publicName: "pinnedRows", isSignal: true, isRequired: false, transformFunction: null }, tree: { classPropertyName: "tree", publicName: "tree", isSignal: true, isRequired: false, transformFunction: null }, masterDetail: { classPropertyName: "masterDetail", publicName: "masterDetail", isSignal: true, isRequired: false, transformFunction: null }, responsive: { classPropertyName: "responsive", publicName: "responsive", isSignal: true, isRequired: false, transformFunction: null }, undoRedo: { classPropertyName: "undoRedo", publicName: "undoRedo", isSignal: true, isRequired: false, transformFunction: null }, exportFeature: { classPropertyName: "exportFeature", publicName: "export", isSignal: true, isRequired: false, transformFunction: null }, print: { classPropertyName: "print", publicName: "print", isSignal: true, isRequired: false, transformFunction: null }, pivot: { classPropertyName: "pivot", publicName: "pivot", isSignal: true, isRequired: false, transformFunction: null }, serverSide: { classPropertyName: "serverSide", publicName: "serverSide", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cellClick: "cellClick", rowClick: "rowClick", cellActivate: "cellActivate", cellChange: "cellChange", cellCommit: "cellCommit", rowCommit: "rowCommit", changedRowsReset: "changedRowsReset", sortChange: "sortChange", filterChange: "filterChange", columnResize: "columnResize", columnMove: "columnMove", columnVisibility: "columnVisibility", columnStateChange: "columnStateChange", selectionChange: "selectionChange", rowMove: "rowMove", groupToggle: "groupToggle", treeExpand: "treeExpand", detailExpand: "detailExpand", responsiveChange: "responsiveChange", copy: "copy", paste: "paste", undoRedoAction: "undoRedoAction", exportComplete: "exportComplete", printStart: "printStart", printComplete: "printComplete" }, ngImport: i0 });
5225
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.1.1", type: Grid, isStandalone: true, selector: "tbw-grid", inputs: { customStyles: { classPropertyName: "customStyles", publicName: "customStyles", isSignal: true, isRequired: false, transformFunction: null }, sortable: { classPropertyName: "sortable", publicName: "sortable", isSignal: true, isRequired: false, transformFunction: null }, filterable: { classPropertyName: "filterable", publicName: "filterable", isSignal: true, isRequired: false, transformFunction: null }, selectable: { classPropertyName: "selectable", publicName: "selectable", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, fitMode: { classPropertyName: "fitMode", publicName: "fitMode", isSignal: true, isRequired: false, transformFunction: null }, gridConfig: { classPropertyName: "gridConfig", publicName: "gridConfig", isSignal: true, isRequired: false, transformFunction: null }, angularConfig: { classPropertyName: "angularConfig", publicName: "angularConfig", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, editing: { classPropertyName: "editing", publicName: "editing", isSignal: true, isRequired: false, transformFunction: null }, clipboard: { classPropertyName: "clipboard", publicName: "clipboard", isSignal: true, isRequired: false, transformFunction: null }, contextMenu: { classPropertyName: "contextMenu", publicName: "contextMenu", isSignal: true, isRequired: false, transformFunction: null }, multiSort: { classPropertyName: "multiSort", publicName: "multiSort", isSignal: true, isRequired: false, transformFunction: null }, sorting: { classPropertyName: "sorting", publicName: "sorting", isSignal: true, isRequired: false, transformFunction: null }, filtering: { classPropertyName: "filtering", publicName: "filtering", isSignal: true, isRequired: false, transformFunction: null }, reorder: { classPropertyName: "reorder", publicName: "reorder", isSignal: true, isRequired: false, transformFunction: null }, visibility: { classPropertyName: "visibility", publicName: "visibility", isSignal: true, isRequired: false, transformFunction: null }, pinnedColumns: { classPropertyName: "pinnedColumns", publicName: "pinnedColumns", isSignal: true, isRequired: false, transformFunction: null }, groupingColumns: { classPropertyName: "groupingColumns", publicName: "groupingColumns", isSignal: true, isRequired: false, transformFunction: null }, columnVirtualization: { classPropertyName: "columnVirtualization", publicName: "columnVirtualization", isSignal: true, isRequired: false, transformFunction: null }, rowReorder: { classPropertyName: "rowReorder", publicName: "rowReorder", isSignal: true, isRequired: false, transformFunction: null }, groupingRows: { classPropertyName: "groupingRows", publicName: "groupingRows", isSignal: true, isRequired: false, transformFunction: null }, pinnedRows: { classPropertyName: "pinnedRows", publicName: "pinnedRows", isSignal: true, isRequired: false, transformFunction: null }, tree: { classPropertyName: "tree", publicName: "tree", isSignal: true, isRequired: false, transformFunction: null }, masterDetail: { classPropertyName: "masterDetail", publicName: "masterDetail", isSignal: true, isRequired: false, transformFunction: null }, responsive: { classPropertyName: "responsive", publicName: "responsive", isSignal: true, isRequired: false, transformFunction: null }, undoRedo: { classPropertyName: "undoRedo", publicName: "undoRedo", isSignal: true, isRequired: false, transformFunction: null }, exportFeature: { classPropertyName: "exportFeature", publicName: "export", isSignal: true, isRequired: false, transformFunction: null }, print: { classPropertyName: "print", publicName: "print", isSignal: true, isRequired: false, transformFunction: null }, pivot: { classPropertyName: "pivot", publicName: "pivot", isSignal: true, isRequired: false, transformFunction: null }, serverSide: { classPropertyName: "serverSide", publicName: "serverSide", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cellClick: "cellClick", rowClick: "rowClick", cellActivate: "cellActivate", cellChange: "cellChange", cellCommit: "cellCommit", rowCommit: "rowCommit", changedRowsReset: "changedRowsReset", sortChange: "sortChange", filterChange: "filterChange", columnResize: "columnResize", columnMove: "columnMove", columnVisibility: "columnVisibility", columnStateChange: "columnStateChange", selectionChange: "selectionChange", rowMove: "rowMove", groupToggle: "groupToggle", treeExpand: "treeExpand", detailExpand: "detailExpand", responsiveChange: "responsiveChange", copy: "copy", paste: "paste", undoRedoAction: "undoRedoAction", exportComplete: "exportComplete", printStart: "printStart", printComplete: "printComplete" }, ngImport: i0 });
4334
5226
  }
4335
5227
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImport: i0, type: Grid, decorators: [{
4336
5228
  type: Directive,
4337
5229
  args: [{ selector: 'tbw-grid' }]
4338
- }], ctorParameters: () => [], propDecorators: { customStyles: [{ type: i0.Input, args: [{ isSignal: true, alias: "customStyles", required: false }] }], sortable: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortable", required: false }] }], filterable: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterable", required: false }] }], selectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectable", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], gridConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "gridConfig", required: false }] }], angularConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "angularConfig", required: false }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }], editing: [{ type: i0.Input, args: [{ isSignal: true, alias: "editing", required: false }] }], clipboard: [{ type: i0.Input, args: [{ isSignal: true, alias: "clipboard", required: false }] }], contextMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "contextMenu", required: false }] }], multiSort: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiSort", required: false }] }], sorting: [{ type: i0.Input, args: [{ isSignal: true, alias: "sorting", required: false }] }], filtering: [{ type: i0.Input, args: [{ isSignal: true, alias: "filtering", required: false }] }], reorder: [{ type: i0.Input, args: [{ isSignal: true, alias: "reorder", required: false }] }], visibility: [{ type: i0.Input, args: [{ isSignal: true, alias: "visibility", required: false }] }], pinnedColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "pinnedColumns", required: false }] }], groupingColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupingColumns", required: false }] }], columnVirtualization: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnVirtualization", required: false }] }], rowReorder: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowReorder", required: false }] }], groupingRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupingRows", required: false }] }], pinnedRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "pinnedRows", required: false }] }], tree: [{ type: i0.Input, args: [{ isSignal: true, alias: "tree", required: false }] }], masterDetail: [{ type: i0.Input, args: [{ isSignal: true, alias: "masterDetail", required: false }] }], responsive: [{ type: i0.Input, args: [{ isSignal: true, alias: "responsive", required: false }] }], undoRedo: [{ type: i0.Input, args: [{ isSignal: true, alias: "undoRedo", required: false }] }], exportFeature: [{ type: i0.Input, args: [{ isSignal: true, alias: "export", required: false }] }], print: [{ type: i0.Input, args: [{ isSignal: true, alias: "print", required: false }] }], pivot: [{ type: i0.Input, args: [{ isSignal: true, alias: "pivot", required: false }] }], serverSide: [{ type: i0.Input, args: [{ isSignal: true, alias: "serverSide", required: false }] }], cellClick: [{ type: i0.Output, args: ["cellClick"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], cellActivate: [{ type: i0.Output, args: ["cellActivate"] }], cellChange: [{ type: i0.Output, args: ["cellChange"] }], cellCommit: [{ type: i0.Output, args: ["cellCommit"] }], rowCommit: [{ type: i0.Output, args: ["rowCommit"] }], changedRowsReset: [{ type: i0.Output, args: ["changedRowsReset"] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], filterChange: [{ type: i0.Output, args: ["filterChange"] }], columnResize: [{ type: i0.Output, args: ["columnResize"] }], columnMove: [{ type: i0.Output, args: ["columnMove"] }], columnVisibility: [{ type: i0.Output, args: ["columnVisibility"] }], columnStateChange: [{ type: i0.Output, args: ["columnStateChange"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], rowMove: [{ type: i0.Output, args: ["rowMove"] }], groupToggle: [{ type: i0.Output, args: ["groupToggle"] }], treeExpand: [{ type: i0.Output, args: ["treeExpand"] }], detailExpand: [{ type: i0.Output, args: ["detailExpand"] }], responsiveChange: [{ type: i0.Output, args: ["responsiveChange"] }], copy: [{ type: i0.Output, args: ["copy"] }], paste: [{ type: i0.Output, args: ["paste"] }], undoRedoAction: [{ type: i0.Output, args: ["undoRedoAction"] }], exportComplete: [{ type: i0.Output, args: ["exportComplete"] }], printStart: [{ type: i0.Output, args: ["printStart"] }], printComplete: [{ type: i0.Output, args: ["printComplete"] }] } });
5230
+ }], ctorParameters: () => [], propDecorators: { customStyles: [{ type: i0.Input, args: [{ isSignal: true, alias: "customStyles", required: false }] }], sortable: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortable", required: false }] }], filterable: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterable", required: false }] }], selectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectable", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], fitMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "fitMode", required: false }] }], gridConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "gridConfig", required: false }] }], angularConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "angularConfig", required: false }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }], editing: [{ type: i0.Input, args: [{ isSignal: true, alias: "editing", required: false }] }], clipboard: [{ type: i0.Input, args: [{ isSignal: true, alias: "clipboard", required: false }] }], contextMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "contextMenu", required: false }] }], multiSort: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiSort", required: false }] }], sorting: [{ type: i0.Input, args: [{ isSignal: true, alias: "sorting", required: false }] }], filtering: [{ type: i0.Input, args: [{ isSignal: true, alias: "filtering", required: false }] }], reorder: [{ type: i0.Input, args: [{ isSignal: true, alias: "reorder", required: false }] }], visibility: [{ type: i0.Input, args: [{ isSignal: true, alias: "visibility", required: false }] }], pinnedColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "pinnedColumns", required: false }] }], groupingColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupingColumns", required: false }] }], columnVirtualization: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnVirtualization", required: false }] }], rowReorder: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowReorder", required: false }] }], groupingRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupingRows", required: false }] }], pinnedRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "pinnedRows", required: false }] }], tree: [{ type: i0.Input, args: [{ isSignal: true, alias: "tree", required: false }] }], masterDetail: [{ type: i0.Input, args: [{ isSignal: true, alias: "masterDetail", required: false }] }], responsive: [{ type: i0.Input, args: [{ isSignal: true, alias: "responsive", required: false }] }], undoRedo: [{ type: i0.Input, args: [{ isSignal: true, alias: "undoRedo", required: false }] }], exportFeature: [{ type: i0.Input, args: [{ isSignal: true, alias: "export", required: false }] }], print: [{ type: i0.Input, args: [{ isSignal: true, alias: "print", required: false }] }], pivot: [{ type: i0.Input, args: [{ isSignal: true, alias: "pivot", required: false }] }], serverSide: [{ type: i0.Input, args: [{ isSignal: true, alias: "serverSide", required: false }] }], cellClick: [{ type: i0.Output, args: ["cellClick"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], cellActivate: [{ type: i0.Output, args: ["cellActivate"] }], cellChange: [{ type: i0.Output, args: ["cellChange"] }], cellCommit: [{ type: i0.Output, args: ["cellCommit"] }], rowCommit: [{ type: i0.Output, args: ["rowCommit"] }], changedRowsReset: [{ type: i0.Output, args: ["changedRowsReset"] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], filterChange: [{ type: i0.Output, args: ["filterChange"] }], columnResize: [{ type: i0.Output, args: ["columnResize"] }], columnMove: [{ type: i0.Output, args: ["columnMove"] }], columnVisibility: [{ type: i0.Output, args: ["columnVisibility"] }], columnStateChange: [{ type: i0.Output, args: ["columnStateChange"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], rowMove: [{ type: i0.Output, args: ["rowMove"] }], groupToggle: [{ type: i0.Output, args: ["groupToggle"] }], treeExpand: [{ type: i0.Output, args: ["treeExpand"] }], detailExpand: [{ type: i0.Output, args: ["detailExpand"] }], responsiveChange: [{ type: i0.Output, args: ["responsiveChange"] }], copy: [{ type: i0.Output, args: ["copy"] }], paste: [{ type: i0.Output, args: ["paste"] }], undoRedoAction: [{ type: i0.Output, args: ["undoRedoAction"] }], exportComplete: [{ type: i0.Output, args: ["exportComplete"] }], printStart: [{ type: i0.Output, args: ["printStart"] }], printComplete: [{ type: i0.Output, args: ["printComplete"] }] } });
4339
5231
 
4340
5232
  /**
4341
5233
  * @packageDocumentation
@@ -4349,5 +5241,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.1", ngImpor
4349
5241
  * Generated bundle index. Do not edit.
4350
5242
  */
4351
5243
 
4352
- export { AngularGridAdapter, BaseGridEditor, GRID_ICONS, GRID_TYPE_DEFAULTS, Grid, GridAdapter, GridColumnEditor, GridColumnView, GridDetailView, GridFormArray, GridIconRegistry, GridLazyForm, GridResponsiveCard, GridToolPanel, GridTypeRegistry, TbwEditor as TbwCellEditor, TbwRenderer as TbwCellView, TbwEditor, TbwRenderer, clearFeatureRegistry, createPluginFromFeature, getFeatureFactory, getFormArrayContext, getLazyFormContext, getRegisteredFeatures, injectGrid, isComponentClass, isFeatureRegistered, provideGridIcons, provideGridTypeDefaults, registerFeature };
5244
+ export { AngularGridAdapter, BaseFilterPanel, BaseGridEditor, BaseGridEditorCVA, BaseOverlayEditor, GRID_ICONS, GRID_TYPE_DEFAULTS, Grid, GridAdapter, GridColumnEditor, GridColumnView, GridDetailView, GridFormArray, GridIconRegistry, GridLazyForm, GridResponsiveCard, GridToolPanel, GridTypeRegistry, TbwEditor as TbwCellEditor, TbwRenderer as TbwCellView, TbwEditor, TbwGridColumn, TbwGridHeader, TbwGridToolButtons, TbwRenderer, clearFeatureRegistry, createPluginFromFeature, getFeatureFactory, getFormArrayContext, getLazyFormContext, getRegisteredFeatures, injectGrid, isComponentClass, isFeatureRegistered, provideGridIcons, provideGridTypeDefaults, registerFeature };
4353
5245
  //# sourceMappingURL=toolbox-web-grid-angular.mjs.map