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,355 @@
|
|
|
1
|
+
import { CommonModule } from '@angular/common';
|
|
2
|
+
import { AfterViewInit, Component, Input, OnDestroy, ViewChild } from '@angular/core';
|
|
3
|
+
import { ArgentGridComponent, ArgentGridModule, ColDef, GridApi, themeQuartz } from '../public-api';
|
|
4
|
+
import {
|
|
5
|
+
departmentValueFormatter,
|
|
6
|
+
locationValueFormatter,
|
|
7
|
+
roleValueFormatter,
|
|
8
|
+
STORY_DEPARTMENTS,
|
|
9
|
+
STORY_LOCATIONS,
|
|
10
|
+
STORY_ROLES,
|
|
11
|
+
} from './story-utils';
|
|
12
|
+
|
|
13
|
+
interface Employee {
|
|
14
|
+
id: number;
|
|
15
|
+
name: string;
|
|
16
|
+
department: string;
|
|
17
|
+
role: string;
|
|
18
|
+
salary: number;
|
|
19
|
+
location: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@Component({
|
|
23
|
+
selector: 'app-benchmark-wrapper',
|
|
24
|
+
standalone: true,
|
|
25
|
+
imports: [CommonModule, ArgentGridModule],
|
|
26
|
+
template: `
|
|
27
|
+
<div class="benchmark-container">
|
|
28
|
+
<div class="controls">
|
|
29
|
+
<button (click)="runBenchmark()" [disabled]="isRunning">
|
|
30
|
+
{{ isRunning ? 'Running...' : 'Run Benchmark' }}
|
|
31
|
+
</button>
|
|
32
|
+
<button (click)="reloadData()">Reload Data</button>
|
|
33
|
+
<span class="row-count">{{ rowCount | number }} rows</span>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div class="benchmark-info">
|
|
37
|
+
<strong>What does this benchmark measure?</strong>
|
|
38
|
+
<ul>
|
|
39
|
+
<li><b>Initial Render</b> β time to paint the first visible frame of the grid</li>
|
|
40
|
+
<li><b>Selection Update</b> β time to select and deselect all {{ rowCount | number }} rows</li>
|
|
41
|
+
<li><b>Grouping Toggle</b> β time to enable then revert row grouping on the Department column</li>
|
|
42
|
+
<li><b>Avg Scroll Frame</b> β average canvas render time across 30 scroll frames (100 px each)</li>
|
|
43
|
+
<li><b>Total</b> β total wall-clock time for the entire benchmark run</li>
|
|
44
|
+
</ul>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<div class="results" *ngIf="results">
|
|
48
|
+
<div class="result-item" title="Time to paint the first visible frame of the grid">
|
|
49
|
+
<span class="label">Initial Render:</span>
|
|
50
|
+
<span class="value">{{ results.initialRender }}ms</span>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="result-item" title="Time to select and deselect all rows">
|
|
53
|
+
<span class="label">Selection Update:</span>
|
|
54
|
+
<span class="value">{{ results.selectionUpdateTime }}ms</span>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="result-item" title="Time to toggle row grouping on the Department column">
|
|
57
|
+
<span class="label">Grouping Toggle:</span>
|
|
58
|
+
<span class="value">{{ results.groupingUpdateTime }}ms</span>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="result-item" title="Average canvas render time per scroll frame (30 frames, 100px each)">
|
|
61
|
+
<span class="label">Avg Scroll Frame:</span>
|
|
62
|
+
<span class="value">{{ results.scrollFrameAverage }}ms</span>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="result-item total" title="Total wall-clock time for the full benchmark run">
|
|
65
|
+
<span class="label">Total:</span>
|
|
66
|
+
<span class="value">{{ results.totalTime }}ms</span>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<argent-grid
|
|
71
|
+
#grid
|
|
72
|
+
[columnDefs]="columnDefs"
|
|
73
|
+
[rowData]="rowData"
|
|
74
|
+
[height]="height"
|
|
75
|
+
[width]="width"
|
|
76
|
+
[theme]="theme"
|
|
77
|
+
[gridOptions]="gridOptions"
|
|
78
|
+
(gridReady)="onGridReady($event)"
|
|
79
|
+
/>
|
|
80
|
+
</div>
|
|
81
|
+
`,
|
|
82
|
+
styles: [
|
|
83
|
+
`
|
|
84
|
+
.benchmark-container {
|
|
85
|
+
display: flex;
|
|
86
|
+
flex-direction: column;
|
|
87
|
+
gap: 12px;
|
|
88
|
+
}
|
|
89
|
+
.controls {
|
|
90
|
+
display: flex;
|
|
91
|
+
gap: 8px;
|
|
92
|
+
align-items: center;
|
|
93
|
+
}
|
|
94
|
+
.controls button {
|
|
95
|
+
padding: 8px 16px;
|
|
96
|
+
background: #3b82f6;
|
|
97
|
+
color: white;
|
|
98
|
+
border: none;
|
|
99
|
+
border-radius: 4px;
|
|
100
|
+
cursor: pointer;
|
|
101
|
+
font-size: 14px;
|
|
102
|
+
}
|
|
103
|
+
.controls button:disabled {
|
|
104
|
+
background: #9ca3af;
|
|
105
|
+
cursor: not-allowed;
|
|
106
|
+
}
|
|
107
|
+
.controls button:hover:not(:disabled) {
|
|
108
|
+
background: #2563eb;
|
|
109
|
+
}
|
|
110
|
+
.row-count {
|
|
111
|
+
margin-left: auto;
|
|
112
|
+
font-size: 14px;
|
|
113
|
+
color: #6b7280;
|
|
114
|
+
}
|
|
115
|
+
.results {
|
|
116
|
+
display: flex;
|
|
117
|
+
gap: 16px;
|
|
118
|
+
padding: 12px;
|
|
119
|
+
background: #f9fafb;
|
|
120
|
+
border-radius: 4px;
|
|
121
|
+
flex-wrap: wrap;
|
|
122
|
+
}
|
|
123
|
+
.result-item {
|
|
124
|
+
display: flex;
|
|
125
|
+
flex-direction: column;
|
|
126
|
+
gap: 4px;
|
|
127
|
+
}
|
|
128
|
+
.result-item .label {
|
|
129
|
+
font-size: 12px;
|
|
130
|
+
color: #6b7280;
|
|
131
|
+
}
|
|
132
|
+
.result-item .value {
|
|
133
|
+
font-size: 18px;
|
|
134
|
+
font-weight: 600;
|
|
135
|
+
color: #111827;
|
|
136
|
+
}
|
|
137
|
+
.result-item.total .value {
|
|
138
|
+
color: #059669;
|
|
139
|
+
}
|
|
140
|
+
.benchmark-info {
|
|
141
|
+
padding: 10px 14px;
|
|
142
|
+
background: #eff6ff;
|
|
143
|
+
border: 1px solid #bfdbfe;
|
|
144
|
+
border-radius: 4px;
|
|
145
|
+
font-size: 13px;
|
|
146
|
+
color: #1e40af;
|
|
147
|
+
}
|
|
148
|
+
.benchmark-info strong {
|
|
149
|
+
display: block;
|
|
150
|
+
margin-bottom: 6px;
|
|
151
|
+
}
|
|
152
|
+
.benchmark-info ul {
|
|
153
|
+
margin: 0;
|
|
154
|
+
padding-left: 18px;
|
|
155
|
+
}
|
|
156
|
+
.benchmark-info li {
|
|
157
|
+
margin-bottom: 3px;
|
|
158
|
+
}
|
|
159
|
+
`,
|
|
160
|
+
],
|
|
161
|
+
})
|
|
162
|
+
export class BenchmarkWrapperComponent implements AfterViewInit, OnDestroy {
|
|
163
|
+
@ViewChild('grid') gridComponent!: ArgentGridComponent;
|
|
164
|
+
|
|
165
|
+
@Input() rowCount = 100000;
|
|
166
|
+
|
|
167
|
+
columnDefs: ColDef<Employee>[] = [
|
|
168
|
+
{ field: 'id', headerName: 'ID', width: 80, sortable: true },
|
|
169
|
+
{ field: 'name', headerName: 'Name', width: 200, sortable: true },
|
|
170
|
+
{
|
|
171
|
+
field: 'department',
|
|
172
|
+
headerName: 'Department',
|
|
173
|
+
width: 180,
|
|
174
|
+
sortable: true,
|
|
175
|
+
valueFormatter: departmentValueFormatter,
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
field: 'role',
|
|
179
|
+
headerName: 'Role',
|
|
180
|
+
width: 250,
|
|
181
|
+
filter: true,
|
|
182
|
+
valueFormatter: roleValueFormatter,
|
|
183
|
+
},
|
|
184
|
+
{ field: 'salary', headerName: 'Salary', width: 120, sortable: true, filter: 'number' },
|
|
185
|
+
{
|
|
186
|
+
field: 'location',
|
|
187
|
+
headerName: 'Location',
|
|
188
|
+
width: 150,
|
|
189
|
+
filter: true,
|
|
190
|
+
valueFormatter: locationValueFormatter,
|
|
191
|
+
},
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
rowData: Employee[] = [];
|
|
195
|
+
height = 'calc(100vh - 60px)';
|
|
196
|
+
width = '100%';
|
|
197
|
+
theme = themeQuartz;
|
|
198
|
+
|
|
199
|
+
gridOptions = {
|
|
200
|
+
floatingFilter: true,
|
|
201
|
+
enableRangeSelection: true,
|
|
202
|
+
defaultColDef: {
|
|
203
|
+
filter: true,
|
|
204
|
+
sortable: true,
|
|
205
|
+
resizable: true,
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
private gridApi?: GridApi<Employee>;
|
|
210
|
+
private fpsInterval?: number;
|
|
211
|
+
|
|
212
|
+
isRunning = false;
|
|
213
|
+
results: {
|
|
214
|
+
initialRender: number;
|
|
215
|
+
selectionUpdateTime: number;
|
|
216
|
+
groupingUpdateTime: number;
|
|
217
|
+
scrollFrameAverage: number;
|
|
218
|
+
totalTime: number;
|
|
219
|
+
} | null = null;
|
|
220
|
+
|
|
221
|
+
ngAfterViewInit(): void {
|
|
222
|
+
this.reloadData();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
generateData(count: number): Employee[] {
|
|
226
|
+
const departments = STORY_DEPARTMENTS;
|
|
227
|
+
const roles = STORY_ROLES;
|
|
228
|
+
const locations = STORY_LOCATIONS;
|
|
229
|
+
|
|
230
|
+
return Array.from({ length: count }, (_, i) => ({
|
|
231
|
+
id: i + 1,
|
|
232
|
+
name: `Employee ${i + 1}`,
|
|
233
|
+
department: departments[Math.floor(Math.random() * departments.length)],
|
|
234
|
+
role: roles[Math.floor(Math.random() * roles.length)],
|
|
235
|
+
salary: Math.floor(Math.random() * 150000) + 50000,
|
|
236
|
+
location: locations[Math.floor(Math.random() * locations.length)],
|
|
237
|
+
}));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
reloadData(): void {
|
|
241
|
+
this.rowData = this.generateData(this.rowCount);
|
|
242
|
+
this.results = null;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
onGridReady(api: GridApi<Employee>): void {
|
|
246
|
+
this.gridApi = api;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
runBenchmark(): void {
|
|
250
|
+
if (!this.gridApi || !this.gridComponent || this.isRunning) return;
|
|
251
|
+
|
|
252
|
+
this.isRunning = true;
|
|
253
|
+
this.results = null;
|
|
254
|
+
|
|
255
|
+
const results = {
|
|
256
|
+
initialRender: 0,
|
|
257
|
+
selectionUpdateTime: 0,
|
|
258
|
+
groupingUpdateTime: 0,
|
|
259
|
+
scrollFrameAverage: 0,
|
|
260
|
+
totalTime: 0,
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const startTime = performance.now();
|
|
264
|
+
|
|
265
|
+
// 1. Initial render time
|
|
266
|
+
results.initialRender = this.gridComponent.getLastFrameTime();
|
|
267
|
+
|
|
268
|
+
// 2. Selection Update Time
|
|
269
|
+
const selStart = performance.now();
|
|
270
|
+
this.gridApi.selectAll();
|
|
271
|
+
setTimeout(() => {
|
|
272
|
+
results.selectionUpdateTime = Number((performance.now() - selStart).toFixed(2));
|
|
273
|
+
this.gridApi?.deselectAll();
|
|
274
|
+
|
|
275
|
+
// 3. Grouping Toggle Time
|
|
276
|
+
const groupStart = performance.now();
|
|
277
|
+
const colDefs = this.gridApi.getColumnDefs() as ColDef<Employee>[];
|
|
278
|
+
const deptCol = colDefs.find((c) => c.field === 'department');
|
|
279
|
+
const wasGrouped = (deptCol as ColDef<Employee>)?.rowGroup;
|
|
280
|
+
|
|
281
|
+
const newColDefs = colDefs.map((col: ColDef<Employee>) => {
|
|
282
|
+
if (col.field === 'department') {
|
|
283
|
+
return { ...col, rowGroup: !wasGrouped };
|
|
284
|
+
}
|
|
285
|
+
return col;
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
this.gridApi.setColumnDefs(newColDefs);
|
|
289
|
+
|
|
290
|
+
setTimeout(() => {
|
|
291
|
+
// Toggle back
|
|
292
|
+
const revertColDefs = newColDefs.map((col: ColDef<Employee>) => {
|
|
293
|
+
if (col.field === 'department') {
|
|
294
|
+
return { ...col, rowGroup: wasGrouped ?? false };
|
|
295
|
+
}
|
|
296
|
+
return col;
|
|
297
|
+
});
|
|
298
|
+
this.gridApi.setColumnDefs(revertColDefs);
|
|
299
|
+
|
|
300
|
+
results.groupingUpdateTime = Number((performance.now() - groupStart).toFixed(2));
|
|
301
|
+
|
|
302
|
+
// 4. Scroll Test
|
|
303
|
+
this.runScrollTest(results, startTime);
|
|
304
|
+
}, 100);
|
|
305
|
+
}, 100);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
private runScrollTest(results: typeof this.results, startTime: number): void {
|
|
309
|
+
const frameTimes: number[] = [];
|
|
310
|
+
let scrollCount = 0;
|
|
311
|
+
const totalScrollFrames = 30;
|
|
312
|
+
const viewport = this.gridComponent.viewportRef?.nativeElement;
|
|
313
|
+
|
|
314
|
+
if (!viewport) {
|
|
315
|
+
this.finishBenchmark(results, startTime);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const runScroll = () => {
|
|
320
|
+
if (scrollCount < totalScrollFrames) {
|
|
321
|
+
viewport.scrollTop += 100;
|
|
322
|
+
frameTimes.push(this.gridComponent.getLastFrameTime());
|
|
323
|
+
scrollCount++;
|
|
324
|
+
requestAnimationFrame(runScroll);
|
|
325
|
+
} else {
|
|
326
|
+
results.scrollFrameAverage = Number(
|
|
327
|
+
(frameTimes.reduce((a, b) => a + b, 0) / frameTimes.length).toFixed(2)
|
|
328
|
+
);
|
|
329
|
+
this.finishBenchmark(results, startTime);
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
// Reset scroll position first
|
|
334
|
+
viewport.scrollTop = 0;
|
|
335
|
+
setTimeout(() => requestAnimationFrame(runScroll), 100);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
private finishBenchmark(results: typeof this.results, startTime: number): void {
|
|
339
|
+
results.totalTime = Number((performance.now() - startTime).toFixed(2));
|
|
340
|
+
this.results = results;
|
|
341
|
+
this.isRunning = false;
|
|
342
|
+
|
|
343
|
+
// Reset scroll
|
|
344
|
+
const viewport = this.gridComponent.viewportRef?.nativeElement;
|
|
345
|
+
if (viewport) {
|
|
346
|
+
viewport.scrollTop = 0;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
ngOnDestroy(): void {
|
|
351
|
+
if (this.fpsInterval) {
|
|
352
|
+
cancelAnimationFrame(this.fpsInterval);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities for Storybook stories
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const LOCATION_FLAGS: Record<string, string> = {
|
|
6
|
+
'New York': 'πΊπΈ New York',
|
|
7
|
+
'San Francisco': 'πΊπΈ San Francisco',
|
|
8
|
+
London: 'π¬π§ London',
|
|
9
|
+
Singapore: 'πΈπ¬ Singapore',
|
|
10
|
+
Remote: 'π Remote',
|
|
11
|
+
Berlin: 'π©πͺ Berlin',
|
|
12
|
+
Tokyo: 'π―π΅ Tokyo',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const DEPARTMENT_EMOJIS: Record<string, string> = {
|
|
16
|
+
Engineering: 'βοΈ Engineering',
|
|
17
|
+
Sales: 'π° Sales',
|
|
18
|
+
Marketing: 'π£ Marketing',
|
|
19
|
+
HR: 'π₯ HR',
|
|
20
|
+
Finance: 'π Finance',
|
|
21
|
+
Design: 'π¨ Design',
|
|
22
|
+
Operations: 'π’ Operations',
|
|
23
|
+
Support: 'π§ Support',
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const ROLE_EMOJIS: Record<string, string> = {
|
|
27
|
+
Engineer: 'π» Engineer',
|
|
28
|
+
'Software Engineer': 'π» Software Engineer',
|
|
29
|
+
Manager: 'π Manager',
|
|
30
|
+
Director: 'π’ Director',
|
|
31
|
+
VP: 'π VP',
|
|
32
|
+
Intern: 'π Intern',
|
|
33
|
+
Analyst: 'π Analyst',
|
|
34
|
+
Lead: 'π₯ Lead',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Common value formatter for location columns to add flags
|
|
39
|
+
*/
|
|
40
|
+
export const locationValueFormatter = (params: any) => {
|
|
41
|
+
return LOCATION_FLAGS[params.value] ?? params.value;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Common value formatter for department columns to add emojis
|
|
46
|
+
*/
|
|
47
|
+
export const departmentValueFormatter = (params: any) => {
|
|
48
|
+
return DEPARTMENT_EMOJIS[params.value] ?? params.value;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Common value formatter for role columns to add emojis
|
|
53
|
+
*/
|
|
54
|
+
export const roleValueFormatter = (params: any) => {
|
|
55
|
+
return ROLE_EMOJIS[params.value] ?? params.value;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Standard locations for mock data generation
|
|
60
|
+
*/
|
|
61
|
+
export const STORY_LOCATIONS = [
|
|
62
|
+
'New York',
|
|
63
|
+
'San Francisco',
|
|
64
|
+
'London',
|
|
65
|
+
'Singapore',
|
|
66
|
+
'Remote',
|
|
67
|
+
'Berlin',
|
|
68
|
+
'Tokyo',
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Standard departments for mock data generation
|
|
73
|
+
*/
|
|
74
|
+
export const STORY_DEPARTMENTS = [
|
|
75
|
+
'Engineering',
|
|
76
|
+
'Sales',
|
|
77
|
+
'Marketing',
|
|
78
|
+
'HR',
|
|
79
|
+
'Finance',
|
|
80
|
+
'Design',
|
|
81
|
+
'Operations',
|
|
82
|
+
'Support',
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Standard roles for mock data generation
|
|
87
|
+
*/
|
|
88
|
+
export const STORY_ROLES = ['Engineer', 'Manager', 'Director', 'VP', 'Intern', 'Analyst', 'Lead'];
|