argent-grid 0.1.0 → 0.3.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.
- package/.github/workflows/ci.yml +69 -0
- package/.github/workflows/pages.yml +6 -12
- package/.storybook/main.ts +20 -0
- package/.storybook/preview.ts +18 -0
- package/.storybook/tsconfig.json +24 -0
- package/AGENTS.md +70 -27
- package/README.md +51 -34
- package/angular.json +66 -0
- package/biome.json +66 -0
- package/demo-app/e2e/selection-screenshot.spec.ts +20 -0
- package/docs/AG-GRID-COMPARISON.md +725 -0
- package/docs/CELL-RENDERER-GUIDE.md +241 -0
- package/docs/CONTEXT-MENU-GUIDE.md +371 -0
- package/docs/LIVE-DATA-OPTIMIZATIONS.md +497 -0
- package/docs/PERFORMANCE-OPTIMIZATIONS-PHASE1.md +162 -0
- package/docs/PERFORMANCE-REVIEW.md +571 -0
- package/docs/RESEARCH-STATUS.md +234 -0
- package/docs/STATE-PERSISTENCE-GUIDE.md +370 -0
- package/docs/STORYBOOK-REFACTOR.md +215 -0
- package/docs/STORYBOOK-STATUS.md +156 -0
- package/docs/TEST-COVERAGE-REPORT.md +276 -0
- package/docs/THEME-API-GUIDE.md +445 -0
- package/docs/THEME-API-PLAN.md +364 -0
- package/e2e/advanced.spec.ts +109 -0
- package/e2e/argentgrid.spec.ts +65 -0
- package/e2e/benchmark.spec.ts +52 -0
- package/e2e/cell-renderers.spec.ts +152 -0
- package/e2e/debug-streaming.spec.ts +31 -0
- package/e2e/dnd.spec.ts +73 -0
- package/e2e/screenshots.spec.ts +52 -0
- package/e2e/theming.spec.ts +35 -0
- package/e2e/visual.spec.ts +112 -0
- package/e2e/visual.spec.ts-snapshots/checkbox-renderer-mixed.png +0 -0
- package/e2e/visual.spec.ts-snapshots/debug.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-column-group-headers.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-default.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-empty-state.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-filter-popup.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-scroll-borders.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-sidebar-buttons.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-text-filter.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-with-selection.png +0 -0
- package/e2e/visual.spec.ts-snapshots/rating-renderer-varied.png +0 -0
- package/package.json +21 -7
- package/plan.md +56 -28
- package/playwright.config.ts +38 -0
- package/setup-vitest.ts +10 -13
- package/src/lib/argent-grid.module.ts +10 -12
- package/src/lib/components/argent-grid.component.css +281 -321
- package/src/lib/components/argent-grid.component.html +295 -207
- package/src/lib/components/argent-grid.component.spec.ts +120 -160
- package/src/lib/components/argent-grid.component.ts +1193 -290
- package/src/lib/components/argent-grid.regressions.spec.ts +301 -0
- package/src/lib/components/argent-grid.selection.spec.ts +132 -0
- package/src/lib/components/set-filter/set-filter.component.spec.ts +191 -0
- package/src/lib/components/set-filter/set-filter.component.ts +307 -0
- package/src/lib/directives/ag-grid-compatibility.directive.ts +16 -26
- package/src/lib/directives/click-outside.directive.ts +19 -0
- package/src/lib/rendering/canvas-renderer.spec.ts +513 -0
- package/src/lib/rendering/canvas-renderer.ts +456 -452
- package/src/lib/rendering/live-data-handler.ts +110 -0
- package/src/lib/rendering/live-data-optimizations.ts +133 -0
- package/src/lib/rendering/render/blit.spec.ts +16 -27
- package/src/lib/rendering/render/blit.ts +48 -36
- package/src/lib/rendering/render/cells.spec.ts +132 -0
- package/src/lib/rendering/render/cells.ts +167 -28
- package/src/lib/rendering/render/column-utils.ts +95 -0
- package/src/lib/rendering/render/hit-test.ts +50 -0
- package/src/lib/rendering/render/index.ts +88 -76
- package/src/lib/rendering/render/lines.ts +53 -47
- package/src/lib/rendering/render/primitives.ts +423 -0
- package/src/lib/rendering/render/theme.spec.ts +8 -12
- package/src/lib/rendering/render/theme.ts +7 -10
- package/src/lib/rendering/render/types.ts +3 -2
- package/src/lib/rendering/render/walk.spec.ts +35 -38
- package/src/lib/rendering/render/walk.ts +94 -64
- package/src/lib/rendering/utils/damage-tracker.spec.ts +8 -7
- package/src/lib/rendering/utils/damage-tracker.ts +6 -18
- package/src/lib/rendering/utils/index.ts +1 -1
- package/src/lib/services/grid.service.set-filter.spec.ts +219 -0
- package/src/lib/services/grid.service.spec.ts +1241 -201
- package/src/lib/services/grid.service.ts +1204 -235
- package/src/lib/themes/parts/color-schemes.ts +132 -0
- package/src/lib/themes/parts/icon-sets.ts +258 -0
- package/src/lib/themes/theme-builder.ts +347 -0
- package/src/lib/themes/theme-quartz.ts +72 -0
- package/src/lib/themes/types.ts +238 -0
- package/src/lib/types/ag-grid-types.ts +573 -14
- package/src/public-api.ts +39 -9
- package/src/stories/Advanced.stories.ts +249 -0
- package/src/stories/ArgentGrid.stories.ts +301 -0
- package/src/stories/Benchmark.stories.ts +76 -0
- package/src/stories/CellRenderers.stories.ts +395 -0
- package/src/stories/Filtering.stories.ts +292 -0
- package/src/stories/Grouping.stories.ts +290 -0
- package/src/stories/Streaming.stories.ts +57 -0
- package/src/stories/Theming.stories.ts +137 -0
- package/src/stories/Tooltips.stories.ts +381 -0
- package/src/stories/benchmark-wrapper.component.ts +355 -0
- package/src/stories/story-utils.ts +88 -0
- package/src/stories/streaming-wrapper.component.ts +441 -0
- package/tsconfig.json +1 -0
- package/tsconfig.storybook.json +10 -0
- package/vitest.config.ts +9 -9
- package/demo-app/README.md +0 -70
- package/demo-app/angular.json +0 -78
- package/demo-app/e2e/benchmark.spec.ts +0 -53
- package/demo-app/e2e/demo-page.spec.ts +0 -77
- package/demo-app/e2e/grid-features.spec.ts +0 -269
- package/demo-app/package-lock.json +0 -14023
- package/demo-app/package.json +0 -36
- package/demo-app/playwright-test-menu.js +0 -19
- package/demo-app/playwright.config.ts +0 -23
- package/demo-app/src/app/app.component.ts +0 -10
- package/demo-app/src/app/app.config.ts +0 -13
- package/demo-app/src/app/app.routes.ts +0 -7
- package/demo-app/src/app/demo-page/demo-page.component.css +0 -313
- package/demo-app/src/app/demo-page/demo-page.component.html +0 -124
- package/demo-app/src/app/demo-page/demo-page.component.ts +0 -366
- package/demo-app/src/index.html +0 -19
- package/demo-app/src/main.ts +0 -6
- package/demo-app/tsconfig.json +0 -31
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChangeDetectionStrategy,
|
|
3
|
+
Component,
|
|
4
|
+
EventEmitter,
|
|
5
|
+
Input,
|
|
6
|
+
OnInit,
|
|
7
|
+
Output,
|
|
8
|
+
} from '@angular/core';
|
|
9
|
+
|
|
10
|
+
@Component({
|
|
11
|
+
selector: 'argent-set-filter',
|
|
12
|
+
standalone: true,
|
|
13
|
+
imports: [],
|
|
14
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
15
|
+
template: `
|
|
16
|
+
<div class="set-filter">
|
|
17
|
+
<!-- Search Box -->
|
|
18
|
+
<div class="set-filter-search">
|
|
19
|
+
<input
|
|
20
|
+
type="text"
|
|
21
|
+
[value]="searchText"
|
|
22
|
+
(input)="onSearchInput($event)"
|
|
23
|
+
placeholder="Search..."
|
|
24
|
+
class="set-filter-input"
|
|
25
|
+
/>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<!-- Select All / Clear All -->
|
|
29
|
+
<div class="set-filter-actions">
|
|
30
|
+
<button
|
|
31
|
+
type="button"
|
|
32
|
+
(click)="selectAll()"
|
|
33
|
+
class="set-filter-btn"
|
|
34
|
+
>
|
|
35
|
+
Select All
|
|
36
|
+
</button>
|
|
37
|
+
<button
|
|
38
|
+
type="button"
|
|
39
|
+
(click)="clearAll()"
|
|
40
|
+
class="set-filter-btn"
|
|
41
|
+
>
|
|
42
|
+
Clear All
|
|
43
|
+
</button>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<!-- Checkbox List -->
|
|
47
|
+
<div class="set-filter-list">
|
|
48
|
+
@for (item of filteredValues; track item.value) {
|
|
49
|
+
<label class="set-filter-item">
|
|
50
|
+
<input
|
|
51
|
+
type="checkbox"
|
|
52
|
+
[checked]="item.selected"
|
|
53
|
+
(change)="onValueToggled(item.value, $event)"
|
|
54
|
+
/>
|
|
55
|
+
<span class="set-filter-label">{{ item.displayValue }}</span>
|
|
56
|
+
<span class="set-filter-count">({{ item.count }})</span>
|
|
57
|
+
</label>
|
|
58
|
+
} @empty {
|
|
59
|
+
<div class="set-filter-empty">
|
|
60
|
+
No values found
|
|
61
|
+
</div>
|
|
62
|
+
}
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<!-- Action Buttons -->
|
|
66
|
+
<div class="set-filter-footer">
|
|
67
|
+
<button
|
|
68
|
+
type="button"
|
|
69
|
+
(click)="applyFilter()"
|
|
70
|
+
class="set-filter-apply"
|
|
71
|
+
>
|
|
72
|
+
Apply
|
|
73
|
+
</button>
|
|
74
|
+
<button
|
|
75
|
+
type="button"
|
|
76
|
+
(click)="resetFilter()"
|
|
77
|
+
class="set-filter-reset"
|
|
78
|
+
>
|
|
79
|
+
Reset
|
|
80
|
+
</button>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
`,
|
|
84
|
+
styles: [
|
|
85
|
+
`
|
|
86
|
+
.set-filter {
|
|
87
|
+
display: flex;
|
|
88
|
+
flex-direction: column;
|
|
89
|
+
width: 250px;
|
|
90
|
+
max-height: 400px;
|
|
91
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
92
|
+
font-size: 13px;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.set-filter-search {
|
|
96
|
+
padding: 8px;
|
|
97
|
+
border-bottom: 1px solid #e0e0e0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.set-filter-input {
|
|
101
|
+
width: 100%;
|
|
102
|
+
padding: 6px 10px;
|
|
103
|
+
border: 1px solid #d0d0d0;
|
|
104
|
+
border-radius: 4px;
|
|
105
|
+
font-size: 13px;
|
|
106
|
+
box-sizing: border-box;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.set-filter-input:focus {
|
|
110
|
+
outline: none;
|
|
111
|
+
border-color: #4f46e5;
|
|
112
|
+
box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.2);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.set-filter-actions {
|
|
116
|
+
display: flex;
|
|
117
|
+
gap: 8px;
|
|
118
|
+
padding: 8px;
|
|
119
|
+
border-bottom: 1px solid #e0e0e0;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.set-filter-btn {
|
|
123
|
+
flex: 1;
|
|
124
|
+
padding: 4px 8px;
|
|
125
|
+
border: 1px solid #d0d0d0;
|
|
126
|
+
border-radius: 4px;
|
|
127
|
+
background: #fff;
|
|
128
|
+
cursor: pointer;
|
|
129
|
+
font-size: 12px;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.set-filter-btn:hover {
|
|
133
|
+
background: #f5f5f5;
|
|
134
|
+
border-color: #4f46e5;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.set-filter-list {
|
|
138
|
+
flex: 1;
|
|
139
|
+
overflow-y: auto;
|
|
140
|
+
padding: 8px 0;
|
|
141
|
+
max-height: 250px;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.set-filter-item {
|
|
145
|
+
display: flex;
|
|
146
|
+
align-items: center;
|
|
147
|
+
padding: 4px 12px;
|
|
148
|
+
cursor: pointer;
|
|
149
|
+
gap: 8px;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.set-filter-item:hover {
|
|
153
|
+
background: #f5f5f5;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.set-filter-item input[type="checkbox"] {
|
|
157
|
+
margin: 0;
|
|
158
|
+
cursor: pointer;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.set-filter-label {
|
|
162
|
+
flex: 1;
|
|
163
|
+
overflow: hidden;
|
|
164
|
+
text-overflow: ellipsis;
|
|
165
|
+
white-space: nowrap;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.set-filter-count {
|
|
169
|
+
color: #999;
|
|
170
|
+
font-size: 11px;
|
|
171
|
+
margin-left: 4px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.set-filter-empty {
|
|
175
|
+
padding: 16px;
|
|
176
|
+
text-align: center;
|
|
177
|
+
color: #999;
|
|
178
|
+
font-style: italic;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.set-filter-footer {
|
|
182
|
+
display: flex;
|
|
183
|
+
gap: 8px;
|
|
184
|
+
padding: 8px;
|
|
185
|
+
border-top: 1px solid #e0e0e0;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.set-filter-apply,
|
|
189
|
+
.set-filter-reset {
|
|
190
|
+
flex: 1;
|
|
191
|
+
padding: 6px 12px;
|
|
192
|
+
border: none;
|
|
193
|
+
border-radius: 4px;
|
|
194
|
+
cursor: pointer;
|
|
195
|
+
font-size: 13px;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.set-filter-apply {
|
|
199
|
+
background: #4f46e5;
|
|
200
|
+
color: #fff;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.set-filter-apply:hover {
|
|
204
|
+
background: #4338ca;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.set-filter-reset {
|
|
208
|
+
background: #f5f5f5;
|
|
209
|
+
color: #333;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.set-filter-reset:hover {
|
|
213
|
+
background: #e5e5e5;
|
|
214
|
+
}
|
|
215
|
+
`,
|
|
216
|
+
],
|
|
217
|
+
})
|
|
218
|
+
export class SetFilterComponent<T = any> implements OnInit {
|
|
219
|
+
@Input() values: T[] = [];
|
|
220
|
+
@Input() valueFormatter?: (value: T) => string;
|
|
221
|
+
/** When provided, only these values will be pre-checked (restores existing filter state) */
|
|
222
|
+
@Input() initialSelectedValues: T[] | null = null;
|
|
223
|
+
@Output() filterChanged = new EventEmitter<T[]>();
|
|
224
|
+
|
|
225
|
+
searchText = '';
|
|
226
|
+
selectedValues: T[] = [];
|
|
227
|
+
allValues: Array<{ value: T; displayValue: string; count: number; selected: boolean }> = [];
|
|
228
|
+
|
|
229
|
+
get filteredValues() {
|
|
230
|
+
if (!this.searchText) {
|
|
231
|
+
return this.allValues;
|
|
232
|
+
}
|
|
233
|
+
const search = this.searchText.toLowerCase();
|
|
234
|
+
return this.allValues.filter((item) => item.displayValue.toLowerCase().includes(search));
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
ngOnInit(): void {
|
|
238
|
+
this.initializeValues();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
onSearchInput(event: Event): void {
|
|
242
|
+
this.searchText = (event.target as HTMLInputElement).value;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private initializeValues(): void {
|
|
246
|
+
// Count occurrences of each value
|
|
247
|
+
const valueCounts = new Map<T, number>();
|
|
248
|
+
this.values.forEach((value) => {
|
|
249
|
+
valueCounts.set(value, (valueCounts.get(value) || 0) + 1);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Build value list with counts
|
|
253
|
+
// If initialSelectedValues provided, pre-check only those; otherwise check all
|
|
254
|
+
const preSelected = this.initialSelectedValues;
|
|
255
|
+
this.allValues = Array.from(valueCounts.entries()).map(([value, count]) => ({
|
|
256
|
+
value,
|
|
257
|
+
displayValue: this.valueFormatter ? this.valueFormatter(value) : String(value),
|
|
258
|
+
count,
|
|
259
|
+
selected: preSelected ? preSelected.includes(value) : true,
|
|
260
|
+
}));
|
|
261
|
+
|
|
262
|
+
this.selectedValues = this.allValues.filter((v) => v.selected).map((v) => v.value);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
onSearchChanged(): void {
|
|
266
|
+
// Trigger change detection through filteredValues getter
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
onValueToggled(value: T, event: Event): void {
|
|
270
|
+
const checkbox = event.target as HTMLInputElement;
|
|
271
|
+
const item = this.allValues.find((v) => v.value === value);
|
|
272
|
+
if (item) {
|
|
273
|
+
item.selected = checkbox.checked;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
this.updateSelectedValues();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
selectAll(): void {
|
|
280
|
+
this.allValues.forEach((item) => {
|
|
281
|
+
item.selected = true;
|
|
282
|
+
});
|
|
283
|
+
this.updateSelectedValues();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
clearAll(): void {
|
|
287
|
+
this.allValues.forEach((item) => {
|
|
288
|
+
item.selected = false;
|
|
289
|
+
});
|
|
290
|
+
this.updateSelectedValues();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
private updateSelectedValues(): void {
|
|
294
|
+
this.selectedValues = this.allValues.filter((item) => item.selected).map((item) => item.value);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
applyFilter(): void {
|
|
298
|
+
this.filterChanged.emit(this.selectedValues);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
resetFilter(): void {
|
|
302
|
+
this.searchText = '';
|
|
303
|
+
this.initialSelectedValues = null; // Reset to all-selected state
|
|
304
|
+
this.initializeValues();
|
|
305
|
+
this.filterChanged.emit(this.selectedValues);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
@@ -1,24 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Directive,
|
|
3
|
-
Input,
|
|
4
|
-
OnInit,
|
|
5
|
-
OnDestroy,
|
|
6
|
-
ComponentRef,
|
|
7
|
-
ViewContainerRef,
|
|
8
|
-
ComponentFactoryResolver
|
|
9
|
-
} from '@angular/core';
|
|
10
|
-
import { GridOptions, ColDef, ColGroupDef } from '../types/ag-grid-types';
|
|
1
|
+
import { ComponentRef, Directive, Input, OnDestroy, OnInit, ViewContainerRef } from '@angular/core';
|
|
11
2
|
import { ArgentGridComponent } from '../components/argent-grid.component';
|
|
3
|
+
import { ColDef, ColGroupDef, GridOptions } from '../types/ag-grid-types';
|
|
12
4
|
|
|
13
5
|
/**
|
|
14
6
|
* AgGridCompatibilityDirective - Drop-in replacement for AG Grid
|
|
15
|
-
*
|
|
7
|
+
*
|
|
16
8
|
* This directive allows users to migrate from AG Grid to ArgentGrid
|
|
17
9
|
* by simply changing their import statement. It maps AG Grid's
|
|
18
10
|
* [columnDefs] and [rowData] inputs to ArgentGrid's internal format.
|
|
19
11
|
*/
|
|
20
12
|
@Directive({
|
|
21
|
-
selector: '[argentGrid], ag-grid-angular'
|
|
13
|
+
selector: '[argentGrid], ag-grid-angular',
|
|
22
14
|
})
|
|
23
15
|
export class AgGridCompatibilityDirective<TData = any> implements OnInit, OnDestroy {
|
|
24
16
|
@Input('columnDefs') set columnDefs(value: (ColDef<TData> | ColGroupDef<TData>)[] | null) {
|
|
@@ -29,7 +21,7 @@ export class AgGridCompatibilityDirective<TData = any> implements OnInit, OnDest
|
|
|
29
21
|
return this._columnDefs;
|
|
30
22
|
}
|
|
31
23
|
private _columnDefs: (ColDef<TData> | ColGroupDef<TData>)[] | null = null;
|
|
32
|
-
|
|
24
|
+
|
|
33
25
|
@Input('rowData') set rowData(value: TData[] | null) {
|
|
34
26
|
this._rowData = value;
|
|
35
27
|
this.updateGrid();
|
|
@@ -38,7 +30,7 @@ export class AgGridCompatibilityDirective<TData = any> implements OnInit, OnDest
|
|
|
38
30
|
return this._rowData;
|
|
39
31
|
}
|
|
40
32
|
private _rowData: TData[] | null = null;
|
|
41
|
-
|
|
33
|
+
|
|
42
34
|
@Input('gridOptions') set gridOptions(value: GridOptions<TData>) {
|
|
43
35
|
this._gridOptions = value;
|
|
44
36
|
this.updateGrid();
|
|
@@ -47,42 +39,40 @@ export class AgGridCompatibilityDirective<TData = any> implements OnInit, OnDest
|
|
|
47
39
|
return this._gridOptions;
|
|
48
40
|
}
|
|
49
41
|
private _gridOptions: GridOptions<TData> | null = null;
|
|
50
|
-
|
|
42
|
+
|
|
51
43
|
private gridComponentRef: ComponentRef<ArgentGridComponent<TData>> | null = null;
|
|
52
|
-
|
|
53
|
-
constructor(
|
|
54
|
-
|
|
55
|
-
) {}
|
|
56
|
-
|
|
44
|
+
|
|
45
|
+
constructor(private viewContainerRef: ViewContainerRef) {}
|
|
46
|
+
|
|
57
47
|
ngOnInit(): void {
|
|
58
48
|
this.createGridComponent();
|
|
59
49
|
}
|
|
60
|
-
|
|
50
|
+
|
|
61
51
|
ngOnDestroy(): void {
|
|
62
52
|
if (this.gridComponentRef) {
|
|
63
53
|
this.gridComponentRef.destroy();
|
|
64
54
|
}
|
|
65
55
|
}
|
|
66
|
-
|
|
56
|
+
|
|
67
57
|
private createGridComponent(): void {
|
|
68
58
|
// Create ArgentGrid component
|
|
69
59
|
this.gridComponentRef = this.viewContainerRef.createComponent(ArgentGridComponent as any);
|
|
70
|
-
|
|
60
|
+
|
|
71
61
|
this.updateGrid();
|
|
72
62
|
}
|
|
73
|
-
|
|
63
|
+
|
|
74
64
|
private updateGrid(): void {
|
|
75
65
|
if (!this.gridComponentRef) {
|
|
76
66
|
return;
|
|
77
67
|
}
|
|
78
|
-
|
|
68
|
+
|
|
79
69
|
// Map AG Grid inputs to ArgentGrid component
|
|
80
70
|
this.gridComponentRef.instance.columnDefs = this.columnDefs;
|
|
81
71
|
this.gridComponentRef.instance.rowData = this.rowData;
|
|
82
72
|
this.gridComponentRef.instance.gridOptions = this.gridOptions || undefined;
|
|
83
73
|
this.gridComponentRef.instance.refresh();
|
|
84
74
|
}
|
|
85
|
-
|
|
75
|
+
|
|
86
76
|
/**
|
|
87
77
|
* Get the underlying GridApi for programmatic access
|
|
88
78
|
*/
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Directive, ElementRef, EventEmitter, HostListener, Output } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
@Directive({
|
|
4
|
+
selector: '[clickOutside]',
|
|
5
|
+
standalone: true,
|
|
6
|
+
})
|
|
7
|
+
export class ClickOutsideDirective {
|
|
8
|
+
@Output() clickOutside = new EventEmitter<void>();
|
|
9
|
+
|
|
10
|
+
constructor(private elementRef: ElementRef) {}
|
|
11
|
+
|
|
12
|
+
@HostListener('document:click', ['$event.target'])
|
|
13
|
+
onClick(target: HTMLElement): void {
|
|
14
|
+
const clickedInside = this.elementRef.nativeElement.contains(target);
|
|
15
|
+
if (!clickedInside) {
|
|
16
|
+
this.clickOutside.emit();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|