@algoux/standard-ranklist-renderer-component-angular 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/README.md +148 -0
  2. package/dist/esm2022/algoux-standard-ranklist-renderer-component-angular.mjs +5 -0
  3. package/dist/esm2022/lib/index.mjs +8 -0
  4. package/dist/esm2022/lib/modal/default-solution-modal.component.mjs +146 -0
  5. package/dist/esm2022/lib/modal/default-user-modal.component.mjs +172 -0
  6. package/dist/esm2022/lib/modal/modal-interactions.mjs +2 -0
  7. package/dist/esm2022/lib/modal/modal.component.mjs +263 -0
  8. package/dist/esm2022/lib/progress/progress-bar.component.mjs +247 -0
  9. package/dist/esm2022/lib/progress/progress-utils.mjs +45 -0
  10. package/dist/esm2022/lib/ranklist/ranklist-utils.mjs +110 -0
  11. package/dist/esm2022/lib/ranklist/ranklist.component.mjs +1013 -0
  12. package/dist/esm2022/lib/ranklist/status-cell-template.directive.mjs +18 -0
  13. package/dist/esm2022/lib/ranklist/user-cell-template.directive.mjs +18 -0
  14. package/dist/esm2022/lib/types.mjs +2 -0
  15. package/dist/esm2022/public-api.mjs +2 -0
  16. package/dist/fesm2022/algoux-standard-ranklist-renderer-component-angular.mjs +2005 -0
  17. package/dist/fesm2022/algoux-standard-ranklist-renderer-component-angular.mjs.map +1 -0
  18. package/dist/index.d.ts +5 -0
  19. package/dist/lib/index.d.ts +8 -0
  20. package/dist/lib/modal/default-solution-modal.component.d.ts +30 -0
  21. package/dist/lib/modal/default-user-modal.component.d.ts +34 -0
  22. package/dist/lib/modal/modal-interactions.d.ts +1 -0
  23. package/dist/lib/modal/modal.component.d.ts +49 -0
  24. package/dist/lib/progress/progress-bar.component.d.ts +31 -0
  25. package/dist/lib/progress/progress-utils.d.ts +15 -0
  26. package/dist/lib/ranklist/ranklist-utils.d.ts +22 -0
  27. package/dist/lib/ranklist/ranklist.component.d.ts +88 -0
  28. package/dist/lib/ranklist/status-cell-template.directive.d.ts +26 -0
  29. package/dist/lib/ranklist/user-cell-template.directive.d.ts +22 -0
  30. package/dist/lib/types.d.ts +29 -0
  31. package/dist/public-api.d.ts +1 -0
  32. package/package.json +3 -3
@@ -0,0 +1,2005 @@
1
+ import * as i1 from '@angular/common';
2
+ import { CommonModule } from '@angular/common';
3
+ import * as i0 from '@angular/core';
4
+ import { EventEmitter, ViewChild, Output, Input, Component, inject, TemplateRef, Directive, ContentChild, ChangeDetectionStrategy } from '@angular/core';
5
+ import { EnumTheme, formatTimeDuration, numberToAlphabet, resolveText, secToTimeStr, resolveStyle, resolveUserMarkers, canRegenerateRanklist } from '@algoux/standard-ranklist-utils';
6
+ import { ensureModalInteractionTracker, unlockModalBodyScroll, SRK_ANIMATED_MODAL_ROOT_CLASS, resolveModalTransformOrigin, MODAL_ANIMATION_DURATION_MS, lockModalBodyScroll, registerModalFocusScope, unregisterModalFocusScope, srkSupportedVersions, calculateProblemStatisticsFooter, caniuse, shouldShowTimeColumn as shouldShowTimeColumn$1, getProblemHeaderBackgroundImage as getProblemHeaderBackgroundImage$1, getMarkerPresentation as getMarkerPresentation$1, resolveSrkAssetUrl as resolveSrkAssetUrl$1, calculateDirtPercentage, calculateSEValue, getRankProblemStatusCellPresentation, captureModalTriggerPointFromMouseEvent, formatProblemStatisticsAcceptedMinute, formatProblemStatisticsAverageHardness, formatProblemStatisticsPercent } from '@algoux/standard-ranklist-renderer-component-core';
7
+
8
+ const defaultBackgroundColor = {
9
+ [EnumTheme.light]: '#ffffff',
10
+ [EnumTheme.dark]: '#191919',
11
+ };
12
+ function resolveSrkAssetUrl(url, field, formatter) {
13
+ if (typeof formatter === 'function') {
14
+ return formatter(url, field);
15
+ }
16
+ if (url.startsWith('//')) {
17
+ return url;
18
+ }
19
+ const protocolMatch = url.match(/^([a-zA-Z][a-zA-Z0-9+.-]*):/);
20
+ if (protocolMatch) {
21
+ const protocol = protocolMatch[1].toLowerCase();
22
+ if (protocol === 'http' || protocol === 'https' || protocol === 'data') {
23
+ return url;
24
+ }
25
+ console.warn(`unsupported protocol "${protocol}" in srk asset url:`, url);
26
+ return '';
27
+ }
28
+ console.warn('unsupported srk asset url:', url);
29
+ return '';
30
+ }
31
+ function getAcceptedStatusDetails(status) {
32
+ return `${status.tries}/${status.time ? formatTimeDuration(status.time, 'min', Math.floor) : '-'}`;
33
+ }
34
+ function getSolutionResultMeta(result) {
35
+ switch (result) {
36
+ case 'FB':
37
+ return { label: 'First Blood', className: 'srk-preset-result-fb' };
38
+ case 'AC':
39
+ return { label: 'Accepted', className: 'srk-preset-result-ac' };
40
+ case 'RJ':
41
+ return { label: 'Rejected', className: 'srk-preset-result-rj' };
42
+ case '?':
43
+ return { label: 'Frozen', className: 'srk-preset-result-fz' };
44
+ case 'WA':
45
+ return { label: 'Wrong Answer', className: 'srk-preset-result-rj' };
46
+ case 'PE':
47
+ return { label: 'Presentation Error', className: 'srk-preset-result-rj' };
48
+ case 'TLE':
49
+ return { label: 'Time Limit Exceeded', className: 'srk-preset-result-rj' };
50
+ case 'MLE':
51
+ return { label: 'Memory Limit Exceeded', className: 'srk-preset-result-rj' };
52
+ case 'OLE':
53
+ return { label: 'Output Limit Exceeded', className: 'srk-preset-result-rj' };
54
+ case 'RTE':
55
+ return { label: 'Runtime Error', className: 'srk-preset-result-rj' };
56
+ case 'NOUT':
57
+ return { label: 'No Output' };
58
+ case 'CE':
59
+ return { label: 'Compile Error' };
60
+ case 'UKE':
61
+ return { label: 'Unknown Error' };
62
+ case null:
63
+ return { label: '--' };
64
+ default:
65
+ return { label: result };
66
+ }
67
+ }
68
+ function getSolutionModalTitle(problemIndex, user) {
69
+ return `Solutions of ${numberToAlphabet(problemIndex)} (${resolveText(user.name)})`;
70
+ }
71
+ function formatSolutionTimestamp(solution) {
72
+ return secToTimeStr(formatTimeDuration(solution.time, 's'));
73
+ }
74
+ function getProblemHeaderBackgroundImage(style, theme) {
75
+ const { backgroundColor } = resolveStyle(style || {});
76
+ const resolvedColor = backgroundColor[theme] || defaultBackgroundColor[theme];
77
+ const bgColorAlphaStr = withAlpha(resolvedColor, 0.27);
78
+ return `linear-gradient(180deg, ${resolvedColor} 0%, ${resolvedColor} 10%, ${bgColorAlphaStr} 10%, transparent 100%)`;
79
+ }
80
+ function getMarkerPresentation(marker, theme) {
81
+ if (typeof marker.style === 'string') {
82
+ return { className: `srk-preset-marker-${marker.style}` };
83
+ }
84
+ if (marker.style) {
85
+ return {
86
+ style: {
87
+ backgroundColor: resolveStyle(marker.style).backgroundColor[theme],
88
+ },
89
+ };
90
+ }
91
+ return {};
92
+ }
93
+ function shouldShowTimeColumn(rows) {
94
+ return rows.some((row) => Boolean(row.score?.time));
95
+ }
96
+ function withAlpha(color, alpha) {
97
+ const hexMatch = color.match(/^#([0-9a-f]{6})$/i);
98
+ if (hexMatch) {
99
+ const value = hexMatch[1];
100
+ const red = parseInt(value.slice(0, 2), 16);
101
+ const green = parseInt(value.slice(2, 4), 16);
102
+ const blue = parseInt(value.slice(4, 6), 16);
103
+ return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
104
+ }
105
+ const rgbMatch = color.match(/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i);
106
+ if (rgbMatch) {
107
+ return `rgba(${rgbMatch[1]}, ${rgbMatch[2]}, ${rgbMatch[3]}, ${alpha})`;
108
+ }
109
+ const rgbaMatch = color.match(/^rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*[\d.]+\s*\)$/i);
110
+ if (rgbaMatch) {
111
+ return `rgba(${rgbaMatch[1]}, ${rgbaMatch[2]}, ${rgbaMatch[3]}, ${alpha})`;
112
+ }
113
+ return color;
114
+ }
115
+
116
+ class ModalComponent {
117
+ open = false;
118
+ title;
119
+ width;
120
+ rootClassName;
121
+ wrapClassName;
122
+ destroyOnClose = true;
123
+ closeOnEsc = true;
124
+ closeOnMaskClick = true;
125
+ panelStyle = {};
126
+ openChange = new EventEmitter();
127
+ close = new EventEmitter();
128
+ dialogRef;
129
+ isMounted = this.open || !this.destroyOnClose;
130
+ animationState = this.open ? 'pre-open' : 'closing';
131
+ transformOrigin = { x: 0, y: 0 };
132
+ closeTimer = null;
133
+ openTimer = null;
134
+ bodyScrollLocked = false;
135
+ focusScopeId = null;
136
+ constructor() {
137
+ if (typeof document !== 'undefined') {
138
+ ensureModalInteractionTracker();
139
+ }
140
+ }
141
+ ngOnChanges(changes) {
142
+ if (changes['open'] || changes['destroyOnClose']) {
143
+ this.syncOpenState();
144
+ }
145
+ }
146
+ ngAfterViewChecked() {
147
+ if (this.open) {
148
+ this.registerFocusScope();
149
+ }
150
+ }
151
+ ngOnDestroy() {
152
+ this.clearTimers();
153
+ this.releaseFocusScope();
154
+ if (this.bodyScrollLocked) {
155
+ unlockModalBodyScroll();
156
+ this.bodyScrollLocked = false;
157
+ }
158
+ }
159
+ handleEscape(event) {
160
+ if (this.open && this.closeOnEsc) {
161
+ this.requestClose('escape');
162
+ }
163
+ }
164
+ handleMaskMouseDown(event) {
165
+ if (this.closeOnMaskClick && event.target === event.currentTarget) {
166
+ this.requestClose('mask');
167
+ }
168
+ }
169
+ requestClose(reason) {
170
+ this.openChange.emit(false);
171
+ this.close.emit(reason);
172
+ }
173
+ shouldRender() {
174
+ return this.isMounted || !this.destroyOnClose;
175
+ }
176
+ rootClasses() {
177
+ return [SRK_ANIMATED_MODAL_ROOT_CLASS, this.rootClassName].filter(Boolean);
178
+ }
179
+ dialogStyle() {
180
+ return {
181
+ ...this.panelStyle,
182
+ width: this.width ? `${this.width}px` : this.panelStyle?.['width'],
183
+ '--srk-modal-origin-x': `${this.transformOrigin.x}px`,
184
+ '--srk-modal-origin-y': `${this.transformOrigin.y}px`,
185
+ '--srk-modal-max-width': this.width
186
+ ? `${this.width}px`
187
+ : typeof this.panelStyle?.['width'] === 'string'
188
+ ? this.panelStyle['width']
189
+ : undefined,
190
+ };
191
+ }
192
+ clearTimers() {
193
+ if (this.closeTimer !== null) {
194
+ window.clearTimeout(this.closeTimer);
195
+ this.closeTimer = null;
196
+ }
197
+ if (this.openTimer !== null) {
198
+ window.clearTimeout(this.openTimer);
199
+ this.openTimer = null;
200
+ }
201
+ }
202
+ syncOpenState() {
203
+ this.clearTimers();
204
+ if (this.open) {
205
+ this.isMounted = true;
206
+ this.transformOrigin = { x: 0, y: 0 };
207
+ this.animationState = 'pre-open';
208
+ if (typeof window !== 'undefined') {
209
+ this.openTimer = window.setTimeout(() => {
210
+ this.registerFocusScope();
211
+ const resolution = resolveModalTransformOrigin(this.dialogRef?.nativeElement || null);
212
+ this.transformOrigin = resolution.origin;
213
+ this.animationState = 'opening';
214
+ this.openTimer = null;
215
+ }, 0);
216
+ }
217
+ }
218
+ else {
219
+ this.releaseFocusScope();
220
+ this.animationState = 'closing';
221
+ if (!this.destroyOnClose) {
222
+ this.isMounted = true;
223
+ }
224
+ else if (this.isMounted && typeof window !== 'undefined') {
225
+ this.closeTimer = window.setTimeout(() => {
226
+ this.isMounted = false;
227
+ this.updateBodyScrollLock();
228
+ this.closeTimer = null;
229
+ }, MODAL_ANIMATION_DURATION_MS);
230
+ }
231
+ }
232
+ this.updateBodyScrollLock();
233
+ }
234
+ updateBodyScrollLock() {
235
+ const shouldLock = this.open || (this.destroyOnClose && this.isMounted);
236
+ if (shouldLock && !this.bodyScrollLocked) {
237
+ lockModalBodyScroll();
238
+ this.bodyScrollLocked = true;
239
+ return;
240
+ }
241
+ if (!shouldLock && this.bodyScrollLocked) {
242
+ unlockModalBodyScroll();
243
+ this.bodyScrollLocked = false;
244
+ }
245
+ }
246
+ registerFocusScope() {
247
+ const nextFocusScopeId = registerModalFocusScope(this.dialogRef?.nativeElement || null, {
248
+ onEscape: this.closeOnEsc ? (event) => this.handleEscape(event) : undefined,
249
+ });
250
+ if (this.focusScopeId === null) {
251
+ this.focusScopeId = nextFocusScopeId;
252
+ }
253
+ }
254
+ releaseFocusScope() {
255
+ if (this.focusScopeId !== null) {
256
+ unregisterModalFocusScope(this.focusScopeId);
257
+ this.focusScopeId = null;
258
+ }
259
+ }
260
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
261
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: ModalComponent, isStandalone: true, selector: "srk-modal", inputs: { open: "open", title: "title", width: "width", rootClassName: "rootClassName", wrapClassName: "wrapClassName", destroyOnClose: "destroyOnClose", closeOnEsc: "closeOnEsc", closeOnMaskClick: "closeOnMaskClick", panelStyle: "panelStyle" }, outputs: { openChange: "openChange", close: "close" }, viewQueries: [{ propertyName: "dialogRef", first: true, predicate: ["dialog"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
262
+ <div *ngIf="shouldRender()" class="srk-modal-root" [ngClass]="rootClasses()" [attr.data-srk-modal-state]="animationState">
263
+ <div class="srk-modal-mask"></div>
264
+ <div
265
+ class="srk-modal-wrap"
266
+ [ngClass]="wrapClassName"
267
+ tabindex="-1"
268
+ (mousedown)="handleMaskMouseDown($event)"
269
+ >
270
+ <div
271
+ #dialog
272
+ class="srk-modal"
273
+ role="dialog"
274
+ aria-modal="true"
275
+ [attr.aria-label]="title || undefined"
276
+ data-srk-modal-panel="true"
277
+ tabindex="-1"
278
+ [ngStyle]="dialogStyle()"
279
+ >
280
+ <div class="srk-modal-content">
281
+ <button
282
+ aria-label="Close"
283
+ class="srk-modal-close"
284
+ type="button"
285
+ (click)="requestClose('close-button')"
286
+ >
287
+ <span class="srk-modal-close-x"></span>
288
+ </button>
289
+ <div *ngIf="title !== undefined" class="srk-modal-header">
290
+ <div class="srk-modal-title">{{ title }}</div>
291
+ </div>
292
+ <div class="srk-modal-body">
293
+ <ng-content />
294
+ </div>
295
+ </div>
296
+ </div>
297
+ </div>
298
+ </div>
299
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] });
300
+ }
301
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ModalComponent, decorators: [{
302
+ type: Component,
303
+ args: [{
304
+ selector: 'srk-modal',
305
+ standalone: true,
306
+ imports: [CommonModule],
307
+ template: `
308
+ <div *ngIf="shouldRender()" class="srk-modal-root" [ngClass]="rootClasses()" [attr.data-srk-modal-state]="animationState">
309
+ <div class="srk-modal-mask"></div>
310
+ <div
311
+ class="srk-modal-wrap"
312
+ [ngClass]="wrapClassName"
313
+ tabindex="-1"
314
+ (mousedown)="handleMaskMouseDown($event)"
315
+ >
316
+ <div
317
+ #dialog
318
+ class="srk-modal"
319
+ role="dialog"
320
+ aria-modal="true"
321
+ [attr.aria-label]="title || undefined"
322
+ data-srk-modal-panel="true"
323
+ tabindex="-1"
324
+ [ngStyle]="dialogStyle()"
325
+ >
326
+ <div class="srk-modal-content">
327
+ <button
328
+ aria-label="Close"
329
+ class="srk-modal-close"
330
+ type="button"
331
+ (click)="requestClose('close-button')"
332
+ >
333
+ <span class="srk-modal-close-x"></span>
334
+ </button>
335
+ <div *ngIf="title !== undefined" class="srk-modal-header">
336
+ <div class="srk-modal-title">{{ title }}</div>
337
+ </div>
338
+ <div class="srk-modal-body">
339
+ <ng-content />
340
+ </div>
341
+ </div>
342
+ </div>
343
+ </div>
344
+ </div>
345
+ `,
346
+ }]
347
+ }], ctorParameters: () => [], propDecorators: { open: [{
348
+ type: Input
349
+ }], title: [{
350
+ type: Input
351
+ }], width: [{
352
+ type: Input
353
+ }], rootClassName: [{
354
+ type: Input
355
+ }], wrapClassName: [{
356
+ type: Input
357
+ }], destroyOnClose: [{
358
+ type: Input
359
+ }], closeOnEsc: [{
360
+ type: Input
361
+ }], closeOnMaskClick: [{
362
+ type: Input
363
+ }], panelStyle: [{
364
+ type: Input
365
+ }], openChange: [{
366
+ type: Output
367
+ }], close: [{
368
+ type: Output
369
+ }], dialogRef: [{
370
+ type: ViewChild,
371
+ args: ['dialog']
372
+ }] } });
373
+
374
+ class DefaultSolutionModalComponent {
375
+ open = false;
376
+ user;
377
+ problem;
378
+ problemIndex = 0;
379
+ solutions = [];
380
+ title;
381
+ width = 320;
382
+ rootClassName = 'srk-general-modal-root';
383
+ wrapClassName = 'srk-solutions-modal';
384
+ panelStyle = {};
385
+ openChange = new EventEmitter();
386
+ close = new EventEmitter();
387
+ formatSolutionTimestamp = formatSolutionTimestamp;
388
+ cachedUser = null;
389
+ cachedProblem;
390
+ cachedProblemIndex = 0;
391
+ cachedSolutions = [];
392
+ ngOnChanges(_changes) {
393
+ if (this.user) {
394
+ this.cachedUser = this.user;
395
+ this.cachedProblem = this.problem;
396
+ this.cachedProblemIndex = this.problemIndex;
397
+ this.cachedSolutions = this.solutions;
398
+ }
399
+ }
400
+ resolvedTitle() {
401
+ return this.title || (this.cachedUser ? getSolutionModalTitle(this.cachedProblemIndex, this.cachedUser) : '');
402
+ }
403
+ solutionMeta(solution) {
404
+ return getSolutionResultMeta(solution.result);
405
+ }
406
+ trackBySolution(index, solution) {
407
+ return `${solution.result}_${solution.time?.[0]}_${index}`;
408
+ }
409
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DefaultSolutionModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
410
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: DefaultSolutionModalComponent, isStandalone: true, selector: "srk-default-solution-modal", inputs: { open: "open", user: "user", problem: "problem", problemIndex: "problemIndex", solutions: "solutions", title: "title", width: "width", rootClassName: "rootClassName", wrapClassName: "wrapClassName", panelStyle: "panelStyle" }, outputs: { openChange: "openChange", close: "close" }, usesOnChanges: true, ngImport: i0, template: `
411
+ <srk-modal
412
+ *ngIf="cachedUser"
413
+ [open]="open"
414
+ [title]="resolvedTitle()"
415
+ [width]="width"
416
+ [rootClassName]="rootClassName"
417
+ [wrapClassName]="wrapClassName"
418
+ [panelStyle]="panelStyle"
419
+ (close)="close.emit($event)"
420
+ (openChange)="openChange.emit($event)"
421
+ >
422
+ <table class="srk-common-table srk-solutions-table">
423
+ <thead>
424
+ <tr>
425
+ <th class="srk--text-left">Result</th>
426
+ <th class="srk--text-right">Time</th>
427
+ </tr>
428
+ </thead>
429
+ <tbody>
430
+ <tr *ngFor="let solution of cachedSolutions; let index = index; trackBy: trackBySolution">
431
+ <td>
432
+ <span
433
+ class="srk-solution-result-text"
434
+ [ngClass]="solutionMeta(solution).className"
435
+ >
436
+ {{ solutionMeta(solution).label }}
437
+ </span>
438
+ </td>
439
+ <td class="srk--text-right">{{ formatSolutionTimestamp(solution) }}</td>
440
+ </tr>
441
+ </tbody>
442
+ </table>
443
+ </srk-modal>
444
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: ModalComponent, selector: "srk-modal", inputs: ["open", "title", "width", "rootClassName", "wrapClassName", "destroyOnClose", "closeOnEsc", "closeOnMaskClick", "panelStyle"], outputs: ["openChange", "close"] }] });
445
+ }
446
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DefaultSolutionModalComponent, decorators: [{
447
+ type: Component,
448
+ args: [{
449
+ selector: 'srk-default-solution-modal',
450
+ standalone: true,
451
+ imports: [CommonModule, ModalComponent],
452
+ template: `
453
+ <srk-modal
454
+ *ngIf="cachedUser"
455
+ [open]="open"
456
+ [title]="resolvedTitle()"
457
+ [width]="width"
458
+ [rootClassName]="rootClassName"
459
+ [wrapClassName]="wrapClassName"
460
+ [panelStyle]="panelStyle"
461
+ (close)="close.emit($event)"
462
+ (openChange)="openChange.emit($event)"
463
+ >
464
+ <table class="srk-common-table srk-solutions-table">
465
+ <thead>
466
+ <tr>
467
+ <th class="srk--text-left">Result</th>
468
+ <th class="srk--text-right">Time</th>
469
+ </tr>
470
+ </thead>
471
+ <tbody>
472
+ <tr *ngFor="let solution of cachedSolutions; let index = index; trackBy: trackBySolution">
473
+ <td>
474
+ <span
475
+ class="srk-solution-result-text"
476
+ [ngClass]="solutionMeta(solution).className"
477
+ >
478
+ {{ solutionMeta(solution).label }}
479
+ </span>
480
+ </td>
481
+ <td class="srk--text-right">{{ formatSolutionTimestamp(solution) }}</td>
482
+ </tr>
483
+ </tbody>
484
+ </table>
485
+ </srk-modal>
486
+ `,
487
+ }]
488
+ }], propDecorators: { open: [{
489
+ type: Input
490
+ }], user: [{
491
+ type: Input
492
+ }], problem: [{
493
+ type: Input
494
+ }], problemIndex: [{
495
+ type: Input
496
+ }], solutions: [{
497
+ type: Input
498
+ }], title: [{
499
+ type: Input
500
+ }], width: [{
501
+ type: Input
502
+ }], rootClassName: [{
503
+ type: Input
504
+ }], wrapClassName: [{
505
+ type: Input
506
+ }], panelStyle: [{
507
+ type: Input
508
+ }], openChange: [{
509
+ type: Output
510
+ }], close: [{
511
+ type: Output
512
+ }] } });
513
+
514
+ class DefaultUserModalComponent {
515
+ open = false;
516
+ user;
517
+ markers = [];
518
+ theme = EnumTheme.light;
519
+ title = 'User Info';
520
+ width = 420;
521
+ rootClassName = 'srk-general-modal-root';
522
+ wrapClassName = 'srk-user-modal';
523
+ panelStyle = {};
524
+ formatSrkAssetUrl;
525
+ openChange = new EventEmitter();
526
+ close = new EventEmitter();
527
+ resolveText = resolveText;
528
+ cachedUser = null;
529
+ ngOnChanges(_changes) {
530
+ if (this.user) {
531
+ this.cachedUser = this.user;
532
+ }
533
+ }
534
+ resolvedMarkers() {
535
+ if (!this.cachedUser) {
536
+ return [];
537
+ }
538
+ return resolveUserMarkers(this.cachedUser, this.markers).map((marker) => ({
539
+ marker,
540
+ presentation: getMarkerPresentation(marker, this.theme),
541
+ }));
542
+ }
543
+ formatAssetUrl(url, field) {
544
+ return resolveSrkAssetUrl(url, field, this.formatSrkAssetUrl);
545
+ }
546
+ trackByMarkerId(_index, entry) {
547
+ return entry.marker.id;
548
+ }
549
+ trackByMemberName(_index, member) {
550
+ return resolveText(member.name);
551
+ }
552
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DefaultUserModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
553
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: DefaultUserModalComponent, isStandalone: true, selector: "srk-default-user-modal", inputs: { open: "open", user: "user", markers: "markers", theme: "theme", title: "title", width: "width", rootClassName: "rootClassName", wrapClassName: "wrapClassName", panelStyle: "panelStyle", formatSrkAssetUrl: "formatSrkAssetUrl" }, outputs: { openChange: "openChange", close: "close" }, usesOnChanges: true, ngImport: i0, template: `
554
+ <srk-modal
555
+ *ngIf="cachedUser as modalUser"
556
+ [open]="open"
557
+ [title]="title"
558
+ [width]="width"
559
+ [rootClassName]="rootClassName"
560
+ [wrapClassName]="wrapClassName"
561
+ [panelStyle]="panelStyle"
562
+ (close)="close.emit($event)"
563
+ (openChange)="openChange.emit($event)"
564
+ >
565
+ <div class="srk-user-modal-info">
566
+ <h3 class="srk-user-modal-info-user-name">{{ resolveText(modalUser.name) }}</h3>
567
+ <p *ngIf="modalUser.organization" class="srk-user-modal-info-user-second-name">
568
+ {{ resolveText(modalUser.organization) }}
569
+ </p>
570
+ <div class="srk-user-modal-info-labels">
571
+ <span class="srk-user-modal-info-labels-label srk-user-modal-info-labels-label-preset-general">
572
+ {{ modalUser.official === false ? '* 非正式参加者' : '正式参加者' }}
573
+ </span>
574
+ <span
575
+ *ngFor="let entry of resolvedMarkers(); trackBy: trackByMarkerId"
576
+ class="srk-user-modal-info-labels-label"
577
+ [ngClass]="entry.presentation.className"
578
+ [ngStyle]="entry.presentation.style"
579
+ >
580
+ {{ resolveText(entry.marker.label) }}
581
+ </span>
582
+ </div>
583
+ <div *ngIf="modalUser.teamMembers?.length" class="srk-user-modal-info-team-members">
584
+ <ng-container *ngFor="let member of modalUser.teamMembers; let index = index; trackBy: trackByMemberName">
585
+ <span *ngIf="index > 0" class="srk-user-modal-info-team-members-slash"> / </span>
586
+ <span>{{ resolveText(member.name) }}</span>
587
+ </ng-container>
588
+ </div>
589
+ <div *ngIf="modalUser.photo" class="srk-user-modal-info-photo">
590
+ <img
591
+ [src]="formatAssetUrl(modalUser.photo, 'user.photo')"
592
+ alt="User portrait"
593
+ class="srk-user-modal-info-photo-img"
594
+ />
595
+ </div>
596
+ </div>
597
+ </srk-modal>
598
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: ModalComponent, selector: "srk-modal", inputs: ["open", "title", "width", "rootClassName", "wrapClassName", "destroyOnClose", "closeOnEsc", "closeOnMaskClick", "panelStyle"], outputs: ["openChange", "close"] }] });
599
+ }
600
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DefaultUserModalComponent, decorators: [{
601
+ type: Component,
602
+ args: [{
603
+ selector: 'srk-default-user-modal',
604
+ standalone: true,
605
+ imports: [CommonModule, ModalComponent],
606
+ template: `
607
+ <srk-modal
608
+ *ngIf="cachedUser as modalUser"
609
+ [open]="open"
610
+ [title]="title"
611
+ [width]="width"
612
+ [rootClassName]="rootClassName"
613
+ [wrapClassName]="wrapClassName"
614
+ [panelStyle]="panelStyle"
615
+ (close)="close.emit($event)"
616
+ (openChange)="openChange.emit($event)"
617
+ >
618
+ <div class="srk-user-modal-info">
619
+ <h3 class="srk-user-modal-info-user-name">{{ resolveText(modalUser.name) }}</h3>
620
+ <p *ngIf="modalUser.organization" class="srk-user-modal-info-user-second-name">
621
+ {{ resolveText(modalUser.organization) }}
622
+ </p>
623
+ <div class="srk-user-modal-info-labels">
624
+ <span class="srk-user-modal-info-labels-label srk-user-modal-info-labels-label-preset-general">
625
+ {{ modalUser.official === false ? '* 非正式参加者' : '正式参加者' }}
626
+ </span>
627
+ <span
628
+ *ngFor="let entry of resolvedMarkers(); trackBy: trackByMarkerId"
629
+ class="srk-user-modal-info-labels-label"
630
+ [ngClass]="entry.presentation.className"
631
+ [ngStyle]="entry.presentation.style"
632
+ >
633
+ {{ resolveText(entry.marker.label) }}
634
+ </span>
635
+ </div>
636
+ <div *ngIf="modalUser.teamMembers?.length" class="srk-user-modal-info-team-members">
637
+ <ng-container *ngFor="let member of modalUser.teamMembers; let index = index; trackBy: trackByMemberName">
638
+ <span *ngIf="index > 0" class="srk-user-modal-info-team-members-slash"> / </span>
639
+ <span>{{ resolveText(member.name) }}</span>
640
+ </ng-container>
641
+ </div>
642
+ <div *ngIf="modalUser.photo" class="srk-user-modal-info-photo">
643
+ <img
644
+ [src]="formatAssetUrl(modalUser.photo, 'user.photo')"
645
+ alt="User portrait"
646
+ class="srk-user-modal-info-photo-img"
647
+ />
648
+ </div>
649
+ </div>
650
+ </srk-modal>
651
+ `,
652
+ }]
653
+ }], propDecorators: { open: [{
654
+ type: Input
655
+ }], user: [{
656
+ type: Input
657
+ }], markers: [{
658
+ type: Input
659
+ }], theme: [{
660
+ type: Input
661
+ }], title: [{
662
+ type: Input
663
+ }], width: [{
664
+ type: Input
665
+ }], rootClassName: [{
666
+ type: Input
667
+ }], wrapClassName: [{
668
+ type: Input
669
+ }], panelStyle: [{
670
+ type: Input
671
+ }], formatSrkAssetUrl: [{
672
+ type: Input
673
+ }], openChange: [{
674
+ type: Output
675
+ }], close: [{
676
+ type: Output
677
+ }] } });
678
+
679
+ function getProgressDurationMinutes(contest) {
680
+ return formatTimeDuration(contest.duration, 'min');
681
+ }
682
+ function getProgressMaxAvailableMinutes(contest, localTime, td = 0) {
683
+ const startAt = new Date(contest.startAt).getTime();
684
+ const currentTime = localTime - td;
685
+ return Math.min(Math.max(Math.floor((currentTime - startAt) / 60000), 0), getProgressDurationMinutes(contest));
686
+ }
687
+ function isProgressEnded(contest, localTime, td = 0) {
688
+ const startAt = new Date(contest.startAt).getTime();
689
+ const endAt = startAt + formatTimeDuration(contest.duration, 'ms');
690
+ const currentTime = localTime - td;
691
+ return currentTime >= endAt;
692
+ }
693
+ function getProgressMetrics(data, localTime, td, timeTravelCurrentValue, inTimeMachine) {
694
+ const { contest } = data;
695
+ const startAt = new Date(contest.startAt).getTime();
696
+ const endAt = startAt + formatTimeDuration(contest.duration, 'ms');
697
+ const frozenLength = contest.frozenDuration ? formatTimeDuration(contest.frozenDuration, 'ms') : 0;
698
+ const currentTime = localTime - td;
699
+ const length = endAt - startAt;
700
+ const frozenBreakpoint = length ? 1 - frozenLength / length : 1;
701
+ const elapsed = Math.min(Math.max(currentTime - startAt, 0), length);
702
+ const remaining = length - elapsed;
703
+ const frozenAt = endAt - frozenLength;
704
+ const percent = length ? (elapsed / length) * 100 : 0;
705
+ const normalPercent = length ? (Math.max(Math.min(currentTime, frozenAt) - startAt, 0) / length) * 100 : 0;
706
+ const normalInnerPercent = Math.min(100, frozenBreakpoint ? normalPercent / frozenBreakpoint : 0);
707
+ const frozenPercent = percent - normalPercent;
708
+ const frozenInnerPercent = Math.min(100, 1 - frozenBreakpoint ? frozenPercent / (1 - frozenBreakpoint) : 0);
709
+ const timeTravelElapsed = timeTravelCurrentValue * 60 * 1000;
710
+ const timeTravelRemaining = length - timeTravelElapsed;
711
+ return {
712
+ elapsed: inTimeMachine ? timeTravelElapsed : elapsed,
713
+ remaining: inTimeMachine ? timeTravelRemaining : remaining,
714
+ frozenBreakpoint,
715
+ normalInnerPercent,
716
+ frozenInnerPercent,
717
+ timeTravelElapsed,
718
+ timeTravelRemaining,
719
+ supportRegen: canRegenerateRanklist(data),
720
+ };
721
+ }
722
+
723
+ class ProgressBarComponent {
724
+ data;
725
+ enableTimeTravel = false;
726
+ live = false;
727
+ td = 0;
728
+ timeTravel = new EventEmitter();
729
+ localTime = Date.now();
730
+ inTimeMachine = false;
731
+ timeTravelIsChanging = false;
732
+ timeTravelCurrentValue = 0;
733
+ timeTravelValue = null;
734
+ liveInterval;
735
+ ngOnInit() {
736
+ this.timeTravelCurrentValue = this.maxAvailableMinutes();
737
+ if (this.live) {
738
+ this.liveInterval = window.setInterval(() => this.handleProgressTimer(), 1000);
739
+ }
740
+ }
741
+ ngOnDestroy() {
742
+ if (this.liveInterval) {
743
+ window.clearInterval(this.liveInterval);
744
+ }
745
+ }
746
+ secToTimeStr(seconds) {
747
+ return secToTimeStr(Math.round(seconds));
748
+ }
749
+ timeLabel(minutes) {
750
+ return secToTimeStr(minutes * 60);
751
+ }
752
+ durationMinutes() {
753
+ return getProgressDurationMinutes(this.data.contest);
754
+ }
755
+ maxAvailableMinutes() {
756
+ return getProgressMaxAvailableMinutes(this.data.contest, this.localTime, this.td);
757
+ }
758
+ isEnded() {
759
+ return isProgressEnded(this.data.contest, this.localTime, this.td);
760
+ }
761
+ progressMetrics() {
762
+ return getProgressMetrics(this.data, this.localTime, this.td, this.timeTravelCurrentValue, this.inTimeMachine);
763
+ }
764
+ beginTimeTravel() {
765
+ this.timeTravelIsChanging = true;
766
+ this.inTimeMachine = true;
767
+ }
768
+ updateTimeTravelValue(event) {
769
+ this.timeTravelCurrentValue = Number(event.target.value);
770
+ }
771
+ commitTimeTravel() {
772
+ if (this.timeTravelIsChanging) {
773
+ this.handleTimeTravelChange(this.timeTravelCurrentValue);
774
+ }
775
+ }
776
+ handleProgressTimer() {
777
+ this.localTime = Date.now();
778
+ if (this.isEnded() && this.liveInterval) {
779
+ window.clearInterval(this.liveInterval);
780
+ }
781
+ if (!this.timeTravelIsChanging && this.timeTravelValue === null) {
782
+ this.timeTravelCurrentValue = this.maxAvailableMinutes();
783
+ }
784
+ }
785
+ handleTimeTravelChange(value) {
786
+ const exited = value >= this.durationMinutes() || value >= this.maxAvailableMinutes();
787
+ this.timeTravel.emit(exited ? null : value * 60 * 1000);
788
+ this.inTimeMachine = !exited;
789
+ this.timeTravelValue = exited ? null : value * 60 * 1000;
790
+ this.timeTravelIsChanging = false;
791
+ }
792
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ProgressBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
793
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: ProgressBarComponent, isStandalone: true, selector: "srk-progress-bar", inputs: { data: "data", enableTimeTravel: "enableTimeTravel", live: "live", td: "td" }, outputs: { timeTravel: "timeTravel" }, ngImport: i0, template: `
794
+ <div class="srk-progress-bar-container">
795
+ <div class="srk-progress-bar">
796
+ <div class="srk-progress-bar-body">
797
+ <div
798
+ class="srk-progress-bar-segment srk-progress-bar-normal"
799
+ [style.width.%]="progressMetrics().frozenBreakpoint * 100"
800
+ >
801
+ <div class="srk-progress-bar-fill" [style.width.%]="progressMetrics().normalInnerPercent"></div>
802
+ </div>
803
+ <div
804
+ class="srk-progress-bar-segment srk-progress-bar-frozen"
805
+ [style.width.%]="(1 - progressMetrics().frozenBreakpoint) * 100"
806
+ >
807
+ <div class="srk-progress-bar-fill" [style.width.%]="progressMetrics().frozenInnerPercent"></div>
808
+ </div>
809
+ </div>
810
+ <div
811
+ *ngIf="enableTimeTravel && progressMetrics().supportRegen"
812
+ class="srk-progress-slider-layer"
813
+ >
814
+ <div
815
+ *ngIf="timeTravelIsChanging"
816
+ class="srk-progress-slider-tooltip"
817
+ [style.left.%]="durationMinutes() ? (timeTravelCurrentValue / durationMinutes()) * 100 : 0"
818
+ >
819
+ {{ timeLabel(timeTravelCurrentValue) }}
820
+ </div>
821
+ <input
822
+ aria-label="Time Travel"
823
+ class="srk-progress-slider"
824
+ min="0"
825
+ step="1"
826
+ type="range"
827
+ [max]="durationMinutes()"
828
+ [title]="timeLabel(timeTravelCurrentValue)"
829
+ [value]="timeTravelCurrentValue"
830
+ (blur)="commitTimeTravel()"
831
+ (input)="updateTimeTravelValue($event)"
832
+ (keydown)="beginTimeTravel()"
833
+ (keyup)="commitTimeTravel()"
834
+ (mousedown)="beginTimeTravel()"
835
+ (mouseup)="commitTimeTravel()"
836
+ (touchend)="commitTimeTravel()"
837
+ (touchstart)="beginTimeTravel()"
838
+ />
839
+ </div>
840
+ </div>
841
+ <div class="srk-progress-secondary-area">
842
+ <div
843
+ class="srk-progress-secondary-area-left"
844
+ [style.display]="live || inTimeMachine ? '' : 'none'"
845
+ >
846
+ Elapsed: {{ secToTimeStr(progressMetrics().elapsed / 1000) }}
847
+ </div>
848
+ <div class="srk-progress-secondary-area-center">
849
+ <div *ngIf="inTimeMachine" class="srk-progress-time-machine-status">
850
+ <div class="srk-progress-time-machine-text">Time Travel Mode</div>
851
+ </div>
852
+ <div *ngIf="!inTimeMachine && live && !isEnded()" class="srk-progress-live-text">
853
+ Live
854
+ </div>
855
+ <div *ngIf="!inTimeMachine && (!live || isEnded())" style="visibility: hidden">
856
+ SRK
857
+ </div>
858
+ </div>
859
+ <div
860
+ class="srk-progress-secondary-area-right"
861
+ [style.display]="live || inTimeMachine ? '' : 'none'"
862
+ >
863
+ Remaining: {{ secToTimeStr(progressMetrics().remaining / 1000) }}
864
+ </div>
865
+ </div>
866
+ </div>
867
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
868
+ }
869
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ProgressBarComponent, decorators: [{
870
+ type: Component,
871
+ args: [{
872
+ selector: 'srk-progress-bar',
873
+ standalone: true,
874
+ imports: [CommonModule],
875
+ template: `
876
+ <div class="srk-progress-bar-container">
877
+ <div class="srk-progress-bar">
878
+ <div class="srk-progress-bar-body">
879
+ <div
880
+ class="srk-progress-bar-segment srk-progress-bar-normal"
881
+ [style.width.%]="progressMetrics().frozenBreakpoint * 100"
882
+ >
883
+ <div class="srk-progress-bar-fill" [style.width.%]="progressMetrics().normalInnerPercent"></div>
884
+ </div>
885
+ <div
886
+ class="srk-progress-bar-segment srk-progress-bar-frozen"
887
+ [style.width.%]="(1 - progressMetrics().frozenBreakpoint) * 100"
888
+ >
889
+ <div class="srk-progress-bar-fill" [style.width.%]="progressMetrics().frozenInnerPercent"></div>
890
+ </div>
891
+ </div>
892
+ <div
893
+ *ngIf="enableTimeTravel && progressMetrics().supportRegen"
894
+ class="srk-progress-slider-layer"
895
+ >
896
+ <div
897
+ *ngIf="timeTravelIsChanging"
898
+ class="srk-progress-slider-tooltip"
899
+ [style.left.%]="durationMinutes() ? (timeTravelCurrentValue / durationMinutes()) * 100 : 0"
900
+ >
901
+ {{ timeLabel(timeTravelCurrentValue) }}
902
+ </div>
903
+ <input
904
+ aria-label="Time Travel"
905
+ class="srk-progress-slider"
906
+ min="0"
907
+ step="1"
908
+ type="range"
909
+ [max]="durationMinutes()"
910
+ [title]="timeLabel(timeTravelCurrentValue)"
911
+ [value]="timeTravelCurrentValue"
912
+ (blur)="commitTimeTravel()"
913
+ (input)="updateTimeTravelValue($event)"
914
+ (keydown)="beginTimeTravel()"
915
+ (keyup)="commitTimeTravel()"
916
+ (mousedown)="beginTimeTravel()"
917
+ (mouseup)="commitTimeTravel()"
918
+ (touchend)="commitTimeTravel()"
919
+ (touchstart)="beginTimeTravel()"
920
+ />
921
+ </div>
922
+ </div>
923
+ <div class="srk-progress-secondary-area">
924
+ <div
925
+ class="srk-progress-secondary-area-left"
926
+ [style.display]="live || inTimeMachine ? '' : 'none'"
927
+ >
928
+ Elapsed: {{ secToTimeStr(progressMetrics().elapsed / 1000) }}
929
+ </div>
930
+ <div class="srk-progress-secondary-area-center">
931
+ <div *ngIf="inTimeMachine" class="srk-progress-time-machine-status">
932
+ <div class="srk-progress-time-machine-text">Time Travel Mode</div>
933
+ </div>
934
+ <div *ngIf="!inTimeMachine && live && !isEnded()" class="srk-progress-live-text">
935
+ Live
936
+ </div>
937
+ <div *ngIf="!inTimeMachine && (!live || isEnded())" style="visibility: hidden">
938
+ SRK
939
+ </div>
940
+ </div>
941
+ <div
942
+ class="srk-progress-secondary-area-right"
943
+ [style.display]="live || inTimeMachine ? '' : 'none'"
944
+ >
945
+ Remaining: {{ secToTimeStr(progressMetrics().remaining / 1000) }}
946
+ </div>
947
+ </div>
948
+ </div>
949
+ `,
950
+ }]
951
+ }], propDecorators: { data: [{
952
+ type: Input,
953
+ args: [{ required: true }]
954
+ }], enableTimeTravel: [{
955
+ type: Input
956
+ }], live: [{
957
+ type: Input
958
+ }], td: [{
959
+ type: Input
960
+ }], timeTravel: [{
961
+ type: Output
962
+ }] } });
963
+
964
+ class SrkStatusCellTemplateDirective {
965
+ templateRef = inject(TemplateRef);
966
+ static ngTemplateContextGuard(_directive, context) {
967
+ return true;
968
+ }
969
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SrkStatusCellTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
970
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: SrkStatusCellTemplateDirective, isStandalone: true, selector: "ng-template[srkStatusCell]", ngImport: i0 });
971
+ }
972
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SrkStatusCellTemplateDirective, decorators: [{
973
+ type: Directive,
974
+ args: [{
975
+ selector: 'ng-template[srkStatusCell]',
976
+ standalone: true,
977
+ }]
978
+ }] });
979
+
980
+ class SrkUserCellTemplateDirective {
981
+ templateRef = inject(TemplateRef);
982
+ static ngTemplateContextGuard(_directive, context) {
983
+ return true;
984
+ }
985
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SrkUserCellTemplateDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
986
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: SrkUserCellTemplateDirective, isStandalone: true, selector: "ng-template[srkUserCell]", ngImport: i0 });
987
+ }
988
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SrkUserCellTemplateDirective, decorators: [{
989
+ type: Directive,
990
+ args: [{
991
+ selector: 'ng-template[srkUserCell]',
992
+ standalone: true,
993
+ }]
994
+ }] });
995
+
996
+ class RanklistComponent {
997
+ data;
998
+ theme = EnumTheme.light;
999
+ borderedRows = false;
1000
+ rowBordered = false;
1001
+ columnBordered = false;
1002
+ stripedRows = false;
1003
+ formatSrkAssetUrl;
1004
+ splitOrganization = false;
1005
+ columnTitles;
1006
+ statusCellPreset = 'classic';
1007
+ statusColorAsText = false;
1008
+ showProblemStatisticsFooter = false;
1009
+ showDirtColumn = false;
1010
+ showSEColumn = false;
1011
+ emptyStatusPlaceholder = null;
1012
+ userAvatarPlacement = 'user';
1013
+ userClick = new EventEmitter();
1014
+ solutionClick = new EventEmitter();
1015
+ statusCellTemplate;
1016
+ userCellTemplate;
1017
+ supportedVersions = srkSupportedVersions;
1018
+ firstBloodStar = '\u2605';
1019
+ statusSeparator = ' ';
1020
+ problemStatisticsFooterRows = [
1021
+ {
1022
+ key: 'accepted',
1023
+ label: 'Accepted',
1024
+ tooltip: 'Number of participants who solved this problem',
1025
+ },
1026
+ {
1027
+ key: 'tried',
1028
+ label: 'Tried',
1029
+ tooltip: 'Number of participants who attempted this problem',
1030
+ },
1031
+ {
1032
+ key: 'submitted',
1033
+ label: 'Submitted',
1034
+ tooltip: 'Total number of valid submissions for this problem',
1035
+ },
1036
+ {
1037
+ key: 'dirt',
1038
+ label: 'Dirt',
1039
+ tooltip: 'Wrong submissions among participants who solved this problem',
1040
+ },
1041
+ {
1042
+ key: 'se',
1043
+ label: 'SE',
1044
+ tooltip: 'Average hardness, calculated as (participants - accepted) / participants',
1045
+ },
1046
+ {
1047
+ key: 'firstAccepted',
1048
+ label: 'FB at',
1049
+ tooltip: 'First Blood at, also known as first solve time, in minutes',
1050
+ },
1051
+ {
1052
+ key: 'lastAccepted',
1053
+ label: 'LB at',
1054
+ tooltip: 'Last Blood at, also known as last solve time, in minutes',
1055
+ },
1056
+ ];
1057
+ problemStatisticsSource;
1058
+ problemStatisticsCache;
1059
+ get problemStatistics() {
1060
+ if (!this.showProblemStatisticsFooter && !this.showSEColumn) {
1061
+ return [];
1062
+ }
1063
+ if (this.problemStatisticsSource !== this.data || !this.problemStatisticsCache) {
1064
+ this.problemStatisticsSource = this.data;
1065
+ this.problemStatisticsCache = calculateProblemStatisticsFooter(this.data);
1066
+ }
1067
+ return this.problemStatisticsCache;
1068
+ }
1069
+ isSupportedVersion() {
1070
+ return caniuse(this.data.version);
1071
+ }
1072
+ showTimeColumn() {
1073
+ return shouldShowTimeColumn$1(this.data.rows);
1074
+ }
1075
+ showAvatarInOrganization() {
1076
+ return this.splitOrganization && this.userAvatarPlacement === 'organization';
1077
+ }
1078
+ leftFooterColumnCount() {
1079
+ return this.data.series.length + 1 + 1 + (this.showTimeColumn() ? 1 : 0) + (this.splitOrganization ? 1 : 0);
1080
+ }
1081
+ getRankValues(row) {
1082
+ return row.rankValues || this.data.series.map(() => ({ rank: null, segmentIndex: null }));
1083
+ }
1084
+ getRankText(rankValue, row) {
1085
+ return rankValue.rank ? rankValue.rank : row.user.official === false ? '*' : '';
1086
+ }
1087
+ resolveDisplayText(text) {
1088
+ return resolveText(text);
1089
+ }
1090
+ resolveSeriesColumnTitle(series, index) {
1091
+ const seriesTitles = this.columnTitles?.series;
1092
+ if (typeof seriesTitles === 'function') {
1093
+ return seriesTitles(series, index) ?? series.title;
1094
+ }
1095
+ if (Array.isArray(seriesTitles)) {
1096
+ return seriesTitles[index] ?? series.title;
1097
+ }
1098
+ return series.title;
1099
+ }
1100
+ resolveColumnTitle(key, fallback) {
1101
+ return this.columnTitles?.[key] ?? fallback;
1102
+ }
1103
+ problemAlias(problem, problemIndex) {
1104
+ return problem.alias || numberToAlphabet(problemIndex);
1105
+ }
1106
+ problemStatsTitle(statistics) {
1107
+ const ratio = statistics.submitted ? ((statistics.accepted / statistics.submitted) * 100).toFixed(1) : 0;
1108
+ return `${statistics.accepted} / ${statistics.submitted} (${ratio}%)`;
1109
+ }
1110
+ problemHeaderBackgroundImage(problem, gradientDirection = 180) {
1111
+ return getProblemHeaderBackgroundImage$1(problem.style, this.theme, gradientDirection);
1112
+ }
1113
+ resolvedUserMarkers(user) {
1114
+ return resolveUserMarkers(user, this.data.markers).map((marker) => ({
1115
+ marker,
1116
+ presentation: getMarkerPresentation$1(marker, this.theme),
1117
+ }));
1118
+ }
1119
+ formatAssetUrl(url, field) {
1120
+ return resolveSrkAssetUrl$1(url, field, this.formatSrkAssetUrl);
1121
+ }
1122
+ formatTime(time) {
1123
+ return formatTimeDuration(time, 'min', Math.floor);
1124
+ }
1125
+ calculateDirtPercentage(row) {
1126
+ return calculateDirtPercentage(row);
1127
+ }
1128
+ calculateSEValue(row, problemStatistics) {
1129
+ return calculateSEValue(row, problemStatistics);
1130
+ }
1131
+ statusPresentation(status) {
1132
+ return getRankProblemStatusCellPresentation(status, this.data, this.statusCellPreset);
1133
+ }
1134
+ isNumber(value) {
1135
+ return typeof value === 'number';
1136
+ }
1137
+ getStatusSolutions(status) {
1138
+ return [...(status.solutions || [])].reverse();
1139
+ }
1140
+ statusCellClass(status) {
1141
+ const classNames = ['srk-prest-status-block', 'srk--text-center', 'srk--nowrap'];
1142
+ if (this.isStatusClickable(status)) {
1143
+ classNames.push('srk--cursor-pointer');
1144
+ }
1145
+ if (this.statusColorAsText) {
1146
+ classNames.push('srk-prest-status-block-color-text');
1147
+ }
1148
+ if (status.result === 'FB') {
1149
+ classNames.push('srk-prest-status-block-fb');
1150
+ }
1151
+ else if (status.result === 'AC') {
1152
+ classNames.push('srk-prest-status-block-accepted');
1153
+ }
1154
+ else if (status.result === '?') {
1155
+ classNames.push('srk-prest-status-block-frozen');
1156
+ }
1157
+ else if (status.result === 'RJ') {
1158
+ classNames.push('srk-prest-status-block-failed');
1159
+ }
1160
+ return classNames.join(' ');
1161
+ }
1162
+ isStatusClickable(status) {
1163
+ return this.getStatusSolutions(status).length > 0;
1164
+ }
1165
+ statusCellRole(status) {
1166
+ return this.isStatusClickable(status) ? 'button' : null;
1167
+ }
1168
+ statusCellTabIndex(status) {
1169
+ return this.isStatusClickable(status) ? 0 : null;
1170
+ }
1171
+ buildUserCellContext(row, rowIndex) {
1172
+ return {
1173
+ $implicit: row.user,
1174
+ user: row.user,
1175
+ row,
1176
+ rowIndex,
1177
+ ranklist: this.data,
1178
+ markers: this.data.markers,
1179
+ hideOrganization: this.splitOrganization,
1180
+ hideAvatar: this.showAvatarInOrganization(),
1181
+ onClick: (event) => this.emitUserClick(event, row, rowIndex),
1182
+ };
1183
+ }
1184
+ buildStatusCellContext(row, rowIndex, status, problemIndex) {
1185
+ return {
1186
+ $implicit: status,
1187
+ status,
1188
+ problem: this.data.problems[problemIndex],
1189
+ problemIndex,
1190
+ user: row.user,
1191
+ row,
1192
+ rowIndex,
1193
+ ranklist: this.data,
1194
+ solutions: this.getStatusSolutions(status),
1195
+ statusCellPreset: this.statusCellPreset,
1196
+ statusColorAsText: this.statusColorAsText,
1197
+ emptyStatusPlaceholder: this.emptyStatusPlaceholder,
1198
+ onClick: (event) => this.emitSolutionClick(event, row, rowIndex, status, problemIndex),
1199
+ };
1200
+ }
1201
+ activateUserCellFromKeyboard(event, row, rowIndex) {
1202
+ event.preventDefault();
1203
+ this.emitUserClick(undefined, row, rowIndex);
1204
+ }
1205
+ activateStatusCellFromKeyboard(event, row, rowIndex, status, problemIndex) {
1206
+ event.preventDefault();
1207
+ this.emitSolutionClick(undefined, row, rowIndex, status, problemIndex);
1208
+ }
1209
+ emitUserClick(event, row, rowIndex) {
1210
+ if (event) {
1211
+ captureModalTriggerPointFromMouseEvent(event, {
1212
+ source: 'user-cell',
1213
+ context: {
1214
+ rowIndex,
1215
+ userId: row.user.id || null,
1216
+ userName: this.resolveDisplayText(row.user.name),
1217
+ },
1218
+ });
1219
+ }
1220
+ this.userClick.emit({
1221
+ user: row.user,
1222
+ row,
1223
+ rowIndex,
1224
+ ranklist: this.data,
1225
+ });
1226
+ }
1227
+ emitSolutionClick(event, row, rowIndex, status, problemIndex) {
1228
+ const solutions = this.getStatusSolutions(status);
1229
+ if (!solutions.length) {
1230
+ return;
1231
+ }
1232
+ if (event) {
1233
+ captureModalTriggerPointFromMouseEvent(event, {
1234
+ source: 'status-cell',
1235
+ context: {
1236
+ rowIndex,
1237
+ problemIndex,
1238
+ problemAlias: this.data.problems[problemIndex]?.alias || null,
1239
+ problemTitle: this.data.problems[problemIndex] ? this.resolveDisplayText(this.data.problems[problemIndex].title) : null,
1240
+ userId: row.user.id || null,
1241
+ },
1242
+ });
1243
+ }
1244
+ this.solutionClick.emit({
1245
+ user: row.user,
1246
+ row,
1247
+ rowIndex,
1248
+ problemIndex,
1249
+ problem: this.data.problems[problemIndex],
1250
+ status,
1251
+ solutions,
1252
+ ranklist: this.data,
1253
+ });
1254
+ }
1255
+ resolveSeriesSegment(rankValue, series) {
1256
+ const index = rankValue.segmentIndex || rankValue.segmentIndex === 0 ? rankValue.segmentIndex : -1;
1257
+ return (series?.segments || [])[index] || {};
1258
+ }
1259
+ isSeriesSegmentedColumn(series) {
1260
+ return (series?.segments || []).some((segment) => typeof segment.style === 'string');
1261
+ }
1262
+ getSeriesSegmentClass(rankValue, series) {
1263
+ const segmentStyle = this.resolveSeriesSegment(rankValue, series).style;
1264
+ return typeof segmentStyle === 'string' ? `srk-preset-series-segment-${segmentStyle}` : '';
1265
+ }
1266
+ getSeriesSegmentStyle(rankValue, series) {
1267
+ const emptyColor = {
1268
+ [EnumTheme.light]: undefined,
1269
+ [EnumTheme.dark]: undefined,
1270
+ };
1271
+ const segmentStyle = this.resolveSeriesSegment(rankValue, series).style;
1272
+ if (!segmentStyle || typeof segmentStyle === 'string') {
1273
+ return {};
1274
+ }
1275
+ const style = resolveStyle(segmentStyle);
1276
+ const textColor = style.textColor || emptyColor;
1277
+ const backgroundColor = style.backgroundColor || emptyColor;
1278
+ return {
1279
+ color: textColor[this.theme],
1280
+ backgroundColor: backgroundColor[this.theme],
1281
+ };
1282
+ }
1283
+ getProblemStatisticsFooterCellPrimary(key, stat) {
1284
+ switch (key) {
1285
+ case 'accepted':
1286
+ return stat.accepted;
1287
+ case 'tried':
1288
+ return stat.tried;
1289
+ case 'submitted':
1290
+ return stat.submitted;
1291
+ case 'dirt':
1292
+ return stat.dirt;
1293
+ case 'se':
1294
+ return formatProblemStatisticsAverageHardness(stat);
1295
+ case 'firstAccepted':
1296
+ return formatProblemStatisticsAcceptedMinute(stat.firstAcceptedTime);
1297
+ case 'lastAccepted':
1298
+ return formatProblemStatisticsAcceptedMinute(stat.lastAcceptedTime);
1299
+ default:
1300
+ return '';
1301
+ }
1302
+ }
1303
+ getProblemStatisticsFooterCellSecondary(key, stat) {
1304
+ switch (key) {
1305
+ case 'accepted':
1306
+ return formatProblemStatisticsPercent(stat.accepted, stat.participantCount);
1307
+ case 'tried':
1308
+ return formatProblemStatisticsPercent(stat.tried, stat.participantCount);
1309
+ case 'dirt':
1310
+ return formatProblemStatisticsPercent(stat.dirt, stat.dirtSubmitted);
1311
+ default:
1312
+ return undefined;
1313
+ }
1314
+ }
1315
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: RanklistComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1316
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: RanklistComponent, isStandalone: true, selector: "srk-ranklist", inputs: { data: "data", theme: "theme", borderedRows: "borderedRows", rowBordered: "rowBordered", columnBordered: "columnBordered", stripedRows: "stripedRows", formatSrkAssetUrl: "formatSrkAssetUrl", splitOrganization: "splitOrganization", columnTitles: "columnTitles", statusCellPreset: "statusCellPreset", statusColorAsText: "statusColorAsText", showProblemStatisticsFooter: "showProblemStatisticsFooter", showDirtColumn: "showDirtColumn", showSEColumn: "showSEColumn", emptyStatusPlaceholder: "emptyStatusPlaceholder", userAvatarPlacement: "userAvatarPlacement" }, outputs: { userClick: "userClick", solutionClick: "solutionClick" }, queries: [{ propertyName: "statusCellTemplate", first: true, predicate: SrkStatusCellTemplateDirective, descendants: true }, { propertyName: "userCellTemplate", first: true, predicate: SrkUserCellTemplateDirective, descendants: true }], ngImport: i0, template: `
1317
+ <div *ngIf="data.type !== 'general'; else versionState">
1318
+ srk type "{{ data.type }}" is not supported
1319
+ </div>
1320
+
1321
+ <ng-template #versionState>
1322
+ <div *ngIf="!isSupportedVersion(); else ranklistTable">
1323
+ srk version "{{ data.version }}" is not supported (current supported: {{ supportedVersions }})
1324
+ </div>
1325
+ </ng-template>
1326
+
1327
+ <ng-template #ranklistTable>
1328
+ <div class="srk-common-table srk-main">
1329
+ <table
1330
+ [class.srk-table-row-bordered]="borderedRows || rowBordered"
1331
+ [class.srk-table-column-bordered]="columnBordered"
1332
+ [class.srk-table-row-striped]="stripedRows"
1333
+ >
1334
+ <thead>
1335
+ <tr>
1336
+ <th
1337
+ *ngFor="let seriesItem of data.series; let seriesIndex = index"
1338
+ class="srk-series-header srk--text-right srk--nowrap"
1339
+ [class.srk-series-segmented-column]="isSeriesSegmentedColumn(seriesItem)"
1340
+ >
1341
+ {{ resolveSeriesColumnTitle(seriesItem, seriesIndex) }}
1342
+ </th>
1343
+ <th
1344
+ *ngIf="splitOrganization"
1345
+ class="srk-organization-header srk--text-left srk--nowrap"
1346
+ >
1347
+ {{ resolveColumnTitle('organization', 'Organization') }}
1348
+ </th>
1349
+ <th class="srk--text-left srk--nowrap">{{ resolveColumnTitle('user', 'Name') }}</th>
1350
+ <th class="srk--text-right srk--nowrap">{{ resolveColumnTitle('score', 'Score') }}</th>
1351
+ <th *ngIf="showTimeColumn()" class="srk--text-right srk--nowrap">
1352
+ {{ resolveColumnTitle('time', 'Time') }}
1353
+ </th>
1354
+ <th
1355
+ *ngFor="let problem of data.problems; let problemIndex = index"
1356
+ class="srk--nowrap srk-problem-header"
1357
+ [style.background-image]="problemHeaderBackgroundImage(problem)"
1358
+ >
1359
+ <a
1360
+ *ngIf="problem.link; else unlinkedProblemHeader"
1361
+ [href]="problem.link"
1362
+ target="_blank"
1363
+ rel="noopener noreferrer"
1364
+ style="color: unset"
1365
+ >
1366
+ <span class="srk--display-block">{{ problemAlias(problem, problemIndex) }}</span>
1367
+ <span
1368
+ *ngIf="problem.statistics"
1369
+ class="srk--display-block srk-problem-stats"
1370
+ [title]="problemStatsTitle(problem.statistics)"
1371
+ >
1372
+ {{ problem.statistics.accepted }}
1373
+ </span>
1374
+ </a>
1375
+ <ng-template #unlinkedProblemHeader>
1376
+ <span class="srk--display-block">{{ problemAlias(problem, problemIndex) }}</span>
1377
+ <span
1378
+ *ngIf="problem.statistics"
1379
+ class="srk--display-block srk-problem-stats"
1380
+ [title]="problemStatsTitle(problem.statistics)"
1381
+ >
1382
+ {{ problem.statistics.accepted }}
1383
+ </span>
1384
+ </ng-template>
1385
+ </th>
1386
+ <th *ngIf="showDirtColumn" class="srk-dirt-header srk--text-right srk--nowrap">
1387
+ {{ resolveColumnTitle('dirt', 'Dirt') }}
1388
+ </th>
1389
+ <th *ngIf="showSEColumn" class="srk-se-header srk--text-right srk--nowrap">
1390
+ {{ resolveColumnTitle('se', 'SE') }}
1391
+ </th>
1392
+ </tr>
1393
+ </thead>
1394
+ <tbody>
1395
+ <tr *ngFor="let row of data.rows; let rowIndex = index">
1396
+ <td
1397
+ *ngFor="let rankValue of getRankValues(row); let seriesIndex = index"
1398
+ class="srk--text-right srk--nowrap"
1399
+ [ngClass]="getSeriesSegmentClass(rankValue, data.series[seriesIndex])"
1400
+ [class.srk-series-segmented-column]="isSeriesSegmentedColumn(data.series[seriesIndex])"
1401
+ [ngStyle]="getSeriesSegmentStyle(rankValue, data.series[seriesIndex])"
1402
+ >
1403
+ {{ getRankText(rankValue, row) }}
1404
+ </td>
1405
+
1406
+ <td
1407
+ *ngIf="splitOrganization"
1408
+ class="srk-organization-cell srk--text-left srk--nowrap"
1409
+ [class.srk-organization-cell-avatar]="showAvatarInOrganization() && !!row.user.avatar"
1410
+ >
1411
+ <div class="srk-organization-cell-content">
1412
+ <div *ngIf="showAvatarInOrganization() && row.user.avatar" class="srk-user-avatar">
1413
+ <img
1414
+ [src]="formatAssetUrl(row.user.avatar, 'user.avatar')"
1415
+ alt="User Avatar"
1416
+ />
1417
+ </div>
1418
+ <span
1419
+ class="srk-organization-name-text"
1420
+ [title]="row.user.organization ? resolveDisplayText(row.user.organization) : ''"
1421
+ [textContent]="row.user.organization ? resolveDisplayText(row.user.organization) : ''"
1422
+ ></span>
1423
+ </div>
1424
+ </td>
1425
+
1426
+ <ng-container *ngIf="userCellTemplate; else defaultUserCell">
1427
+ <ng-container
1428
+ [ngTemplateOutlet]="userCellTemplate.templateRef"
1429
+ [ngTemplateOutletContext]="buildUserCellContext(row, rowIndex)"
1430
+ />
1431
+ </ng-container>
1432
+ <ng-template #defaultUserCell>
1433
+ <td
1434
+ class="srk--text-left srk--nowrap srk-user-cell srk--cursor-pointer"
1435
+ role="button"
1436
+ tabindex="0"
1437
+ (click)="emitUserClick($event, row, rowIndex)"
1438
+ (keydown.enter)="activateUserCellFromKeyboard($event, row, rowIndex)"
1439
+ (keydown.space)="activateUserCellFromKeyboard($event, row, rowIndex)"
1440
+ >
1441
+ <div class="srk-user-cell-content">
1442
+ <div *ngIf="row.user.avatar && !showAvatarInOrganization()" class="srk-user-avatar">
1443
+ <img
1444
+ [src]="formatAssetUrl(row.user.avatar, 'user.avatar')"
1445
+ alt="User Avatar"
1446
+ />
1447
+ </div>
1448
+ <div class="srk-user-body">
1449
+ <div class="srk-user-name-row">
1450
+ <span
1451
+ class="srk-user-name-text"
1452
+ [title]="resolveDisplayText(row.user.name)"
1453
+ >
1454
+ {{ resolveDisplayText(row.user.name) }}
1455
+ </span>
1456
+ <span class="srk-marker-dot-group">
1457
+ <span
1458
+ *ngFor="let marker of resolvedUserMarkers(row.user)"
1459
+ class="srk-marker srk-marker-dot srk--c-tooltip"
1460
+ [ngClass]="marker.presentation.className"
1461
+ [ngStyle]="marker.presentation.style"
1462
+ [attr.data-tooltip]="resolveDisplayText(marker.marker.label)"
1463
+ ></span>
1464
+ </span>
1465
+ </div>
1466
+ <p
1467
+ *ngIf="row.user.organization && !splitOrganization"
1468
+ class="srk-user-secondary-text srk--text-ellipsis"
1469
+ title=""
1470
+ >
1471
+ {{ resolveDisplayText(row.user.organization) }}
1472
+ </p>
1473
+ </div>
1474
+ </div>
1475
+ </td>
1476
+ </ng-template>
1477
+
1478
+ <td class="srk--text-right srk--nowrap">{{ row.score.value }}</td>
1479
+ <td *ngIf="showTimeColumn()" class="srk--text-right srk--nowrap">
1480
+ {{ row.score.time ? formatTime(row.score.time) : '-' }}
1481
+ </td>
1482
+
1483
+ <ng-container *ngFor="let status of row.statuses; let problemIndex = index">
1484
+ <ng-container *ngIf="statusCellTemplate; else defaultStatusCell">
1485
+ <ng-container
1486
+ [ngTemplateOutlet]="statusCellTemplate.templateRef"
1487
+ [ngTemplateOutletContext]="buildStatusCellContext(row, rowIndex, status, problemIndex)"
1488
+ />
1489
+ </ng-container>
1490
+ <ng-template #defaultStatusCell>
1491
+ <td
1492
+ *ngIf="status.result === 'FB' || status.result === 'AC'; else failedOrFrozenStatus"
1493
+ [ngClass]="statusCellClass(status)"
1494
+ [attr.role]="statusCellRole(status)"
1495
+ [attr.tabindex]="statusCellTabIndex(status)"
1496
+ (click)="emitSolutionClick($event, row, rowIndex, status, problemIndex)"
1497
+ (keydown.enter)="activateStatusCellFromKeyboard($event, row, rowIndex, status, problemIndex)"
1498
+ (keydown.space)="activateStatusCellFromKeyboard($event, row, rowIndex, status, problemIndex)"
1499
+ >
1500
+ <span
1501
+ *ngIf="statusColorAsText && status.result === 'FB'"
1502
+ class="srk-prest-status-block-fb-star"
1503
+ [textContent]="firstBloodStar"
1504
+ ></span>
1505
+ <ng-container *ngIf="statusPresentation(status) as presentation">
1506
+ <ng-container *ngIf="isNumber(presentation.score); else statusText">
1507
+ <span class="srk-prest-status-block-score">{{ presentation.score }}</span>
1508
+ <span class="srk-prest-status-block-score-details">
1509
+ {{ presentation.scoreDetails }}
1510
+ </span>
1511
+ </ng-container>
1512
+ <ng-template #statusText>
1513
+ <ng-container *ngIf="presentation.secondary !== undefined; else singleStatus">
1514
+ <span class="srk-prest-status-block-primary">{{ presentation.primary || '' }}</span>
1515
+ <span [textContent]="statusSeparator"></span>
1516
+ <span class="srk-prest-status-block-secondary">{{ presentation.secondary }}</span>
1517
+ </ng-container>
1518
+ <ng-template #singleStatus>{{ presentation.primary }}</ng-template>
1519
+ </ng-template>
1520
+ </ng-container>
1521
+ </td>
1522
+ <ng-template #failedOrFrozenStatus>
1523
+ <td
1524
+ *ngIf="status.result === '?' || status.result === 'RJ'; else emptyStatus"
1525
+ [ngClass]="statusCellClass(status)"
1526
+ [attr.role]="statusCellRole(status)"
1527
+ [attr.tabindex]="statusCellTabIndex(status)"
1528
+ (click)="emitSolutionClick($event, row, rowIndex, status, problemIndex)"
1529
+ (keydown.enter)="activateStatusCellFromKeyboard($event, row, rowIndex, status, problemIndex)"
1530
+ (keydown.space)="activateStatusCellFromKeyboard($event, row, rowIndex, status, problemIndex)"
1531
+ >
1532
+ <ng-container *ngIf="statusPresentation(status) as presentation">
1533
+ <ng-container *ngIf="presentation.secondary !== undefined; else singleStatus">
1534
+ <span class="srk-prest-status-block-primary">{{ presentation.primary || '' }}</span>
1535
+ <span [textContent]="statusSeparator"></span>
1536
+ <span class="srk-prest-status-block-secondary">{{ presentation.secondary }}</span>
1537
+ </ng-container>
1538
+ <ng-template #singleStatus>{{ presentation.primary }}</ng-template>
1539
+ </ng-container>
1540
+ </td>
1541
+ </ng-template>
1542
+ <ng-template #emptyStatus>
1543
+ <td class="srk-status-placeholder-cell srk--text-center srk--nowrap">
1544
+ {{ emptyStatusPlaceholder }}
1545
+ </td>
1546
+ </ng-template>
1547
+ </ng-template>
1548
+ </ng-container>
1549
+
1550
+ <td *ngIf="showDirtColumn" class="srk-dirt-cell srk--text-right srk--nowrap">
1551
+ {{ calculateDirtPercentage(row) }}
1552
+ </td>
1553
+ <td *ngIf="showSEColumn" class="srk-se-cell srk--text-right srk--nowrap">
1554
+ {{ calculateSEValue(row, problemStatistics) }}
1555
+ </td>
1556
+ </tr>
1557
+ </tbody>
1558
+ <tfoot *ngIf="showProblemStatisticsFooter">
1559
+ <tr
1560
+ *ngFor="let footerRow of problemStatisticsFooterRows"
1561
+ class="srk-problem-statistics-footer-row"
1562
+ >
1563
+ <td
1564
+ class="srk-problem-statistics-footer-labels srk--text-right srk--nowrap"
1565
+ [attr.colspan]="leftFooterColumnCount()"
1566
+ >
1567
+ <span
1568
+ class="srk-problem-statistics-footer-label srk--c-tooltip"
1569
+ [attr.data-tooltip]="footerRow.tooltip"
1570
+ >
1571
+ {{ footerRow.label }}
1572
+ </span>
1573
+ </td>
1574
+ <td
1575
+ *ngFor="let stat of problemStatistics"
1576
+ class="srk-problem-statistics-footer-cell srk--text-center srk--nowrap"
1577
+ >
1578
+ <span class="srk-problem-statistics-footer-primary">
1579
+ {{ getProblemStatisticsFooterCellPrimary(footerRow.key, stat) }}
1580
+ </span>
1581
+ <ng-container *ngIf="getProblemStatisticsFooterCellSecondary(footerRow.key, stat) !== undefined">
1582
+ <span> </span>
1583
+ <span class="srk-problem-statistics-footer-secondary">
1584
+ {{ getProblemStatisticsFooterCellSecondary(footerRow.key, stat) }}
1585
+ </span>
1586
+ </ng-container>
1587
+ </td>
1588
+ <td
1589
+ *ngIf="showDirtColumn"
1590
+ class="srk-problem-statistics-footer-cell srk-extra-statistics-footer-cell srk-dirt-footer-cell srk--nowrap"
1591
+ >
1592
+ <span class="srk-problem-statistics-footer-primary"></span>
1593
+ </td>
1594
+ <td
1595
+ *ngIf="showSEColumn"
1596
+ class="srk-problem-statistics-footer-cell srk-extra-statistics-footer-cell srk-se-footer-cell srk--nowrap"
1597
+ >
1598
+ <span class="srk-problem-statistics-footer-primary"></span>
1599
+ </td>
1600
+ </tr>
1601
+ <tr class="srk-problem-statistics-footer-row srk-problem-statistics-footer-problem-label-row">
1602
+ <td
1603
+ class="srk-problem-statistics-footer-labels srk--text-right srk--nowrap"
1604
+ [attr.colspan]="leftFooterColumnCount()"
1605
+ ></td>
1606
+ <td
1607
+ *ngFor="let problem of data.problems; let problemIndex = index"
1608
+ class="srk-problem-statistics-footer-cell srk-problem-statistics-footer-problem-header srk-problem-header srk--text-center srk--nowrap"
1609
+ [style.background-image]="problemHeaderBackgroundImage(problem, 0)"
1610
+ >
1611
+ <span class="srk--display-block">{{ problemAlias(problem, problemIndex) }}</span>
1612
+ </td>
1613
+ <td
1614
+ *ngIf="showDirtColumn"
1615
+ class="srk-problem-statistics-footer-cell srk-extra-statistics-footer-cell srk-dirt-footer-cell srk--nowrap"
1616
+ >
1617
+ <span class="srk-problem-statistics-footer-primary"></span>
1618
+ </td>
1619
+ <td
1620
+ *ngIf="showSEColumn"
1621
+ class="srk-problem-statistics-footer-cell srk-extra-statistics-footer-cell srk-se-footer-cell srk--nowrap"
1622
+ >
1623
+ <span class="srk-problem-statistics-footer-primary"></span>
1624
+ </td>
1625
+ </tr>
1626
+ </tfoot>
1627
+ </table>
1628
+ </div>
1629
+ </ng-template>
1630
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1631
+ }
1632
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: RanklistComponent, decorators: [{
1633
+ type: Component,
1634
+ args: [{
1635
+ selector: 'srk-ranklist',
1636
+ standalone: true,
1637
+ changeDetection: ChangeDetectionStrategy.OnPush,
1638
+ imports: [CommonModule],
1639
+ template: `
1640
+ <div *ngIf="data.type !== 'general'; else versionState">
1641
+ srk type "{{ data.type }}" is not supported
1642
+ </div>
1643
+
1644
+ <ng-template #versionState>
1645
+ <div *ngIf="!isSupportedVersion(); else ranklistTable">
1646
+ srk version "{{ data.version }}" is not supported (current supported: {{ supportedVersions }})
1647
+ </div>
1648
+ </ng-template>
1649
+
1650
+ <ng-template #ranklistTable>
1651
+ <div class="srk-common-table srk-main">
1652
+ <table
1653
+ [class.srk-table-row-bordered]="borderedRows || rowBordered"
1654
+ [class.srk-table-column-bordered]="columnBordered"
1655
+ [class.srk-table-row-striped]="stripedRows"
1656
+ >
1657
+ <thead>
1658
+ <tr>
1659
+ <th
1660
+ *ngFor="let seriesItem of data.series; let seriesIndex = index"
1661
+ class="srk-series-header srk--text-right srk--nowrap"
1662
+ [class.srk-series-segmented-column]="isSeriesSegmentedColumn(seriesItem)"
1663
+ >
1664
+ {{ resolveSeriesColumnTitle(seriesItem, seriesIndex) }}
1665
+ </th>
1666
+ <th
1667
+ *ngIf="splitOrganization"
1668
+ class="srk-organization-header srk--text-left srk--nowrap"
1669
+ >
1670
+ {{ resolveColumnTitle('organization', 'Organization') }}
1671
+ </th>
1672
+ <th class="srk--text-left srk--nowrap">{{ resolveColumnTitle('user', 'Name') }}</th>
1673
+ <th class="srk--text-right srk--nowrap">{{ resolveColumnTitle('score', 'Score') }}</th>
1674
+ <th *ngIf="showTimeColumn()" class="srk--text-right srk--nowrap">
1675
+ {{ resolveColumnTitle('time', 'Time') }}
1676
+ </th>
1677
+ <th
1678
+ *ngFor="let problem of data.problems; let problemIndex = index"
1679
+ class="srk--nowrap srk-problem-header"
1680
+ [style.background-image]="problemHeaderBackgroundImage(problem)"
1681
+ >
1682
+ <a
1683
+ *ngIf="problem.link; else unlinkedProblemHeader"
1684
+ [href]="problem.link"
1685
+ target="_blank"
1686
+ rel="noopener noreferrer"
1687
+ style="color: unset"
1688
+ >
1689
+ <span class="srk--display-block">{{ problemAlias(problem, problemIndex) }}</span>
1690
+ <span
1691
+ *ngIf="problem.statistics"
1692
+ class="srk--display-block srk-problem-stats"
1693
+ [title]="problemStatsTitle(problem.statistics)"
1694
+ >
1695
+ {{ problem.statistics.accepted }}
1696
+ </span>
1697
+ </a>
1698
+ <ng-template #unlinkedProblemHeader>
1699
+ <span class="srk--display-block">{{ problemAlias(problem, problemIndex) }}</span>
1700
+ <span
1701
+ *ngIf="problem.statistics"
1702
+ class="srk--display-block srk-problem-stats"
1703
+ [title]="problemStatsTitle(problem.statistics)"
1704
+ >
1705
+ {{ problem.statistics.accepted }}
1706
+ </span>
1707
+ </ng-template>
1708
+ </th>
1709
+ <th *ngIf="showDirtColumn" class="srk-dirt-header srk--text-right srk--nowrap">
1710
+ {{ resolveColumnTitle('dirt', 'Dirt') }}
1711
+ </th>
1712
+ <th *ngIf="showSEColumn" class="srk-se-header srk--text-right srk--nowrap">
1713
+ {{ resolveColumnTitle('se', 'SE') }}
1714
+ </th>
1715
+ </tr>
1716
+ </thead>
1717
+ <tbody>
1718
+ <tr *ngFor="let row of data.rows; let rowIndex = index">
1719
+ <td
1720
+ *ngFor="let rankValue of getRankValues(row); let seriesIndex = index"
1721
+ class="srk--text-right srk--nowrap"
1722
+ [ngClass]="getSeriesSegmentClass(rankValue, data.series[seriesIndex])"
1723
+ [class.srk-series-segmented-column]="isSeriesSegmentedColumn(data.series[seriesIndex])"
1724
+ [ngStyle]="getSeriesSegmentStyle(rankValue, data.series[seriesIndex])"
1725
+ >
1726
+ {{ getRankText(rankValue, row) }}
1727
+ </td>
1728
+
1729
+ <td
1730
+ *ngIf="splitOrganization"
1731
+ class="srk-organization-cell srk--text-left srk--nowrap"
1732
+ [class.srk-organization-cell-avatar]="showAvatarInOrganization() && !!row.user.avatar"
1733
+ >
1734
+ <div class="srk-organization-cell-content">
1735
+ <div *ngIf="showAvatarInOrganization() && row.user.avatar" class="srk-user-avatar">
1736
+ <img
1737
+ [src]="formatAssetUrl(row.user.avatar, 'user.avatar')"
1738
+ alt="User Avatar"
1739
+ />
1740
+ </div>
1741
+ <span
1742
+ class="srk-organization-name-text"
1743
+ [title]="row.user.organization ? resolveDisplayText(row.user.organization) : ''"
1744
+ [textContent]="row.user.organization ? resolveDisplayText(row.user.organization) : ''"
1745
+ ></span>
1746
+ </div>
1747
+ </td>
1748
+
1749
+ <ng-container *ngIf="userCellTemplate; else defaultUserCell">
1750
+ <ng-container
1751
+ [ngTemplateOutlet]="userCellTemplate.templateRef"
1752
+ [ngTemplateOutletContext]="buildUserCellContext(row, rowIndex)"
1753
+ />
1754
+ </ng-container>
1755
+ <ng-template #defaultUserCell>
1756
+ <td
1757
+ class="srk--text-left srk--nowrap srk-user-cell srk--cursor-pointer"
1758
+ role="button"
1759
+ tabindex="0"
1760
+ (click)="emitUserClick($event, row, rowIndex)"
1761
+ (keydown.enter)="activateUserCellFromKeyboard($event, row, rowIndex)"
1762
+ (keydown.space)="activateUserCellFromKeyboard($event, row, rowIndex)"
1763
+ >
1764
+ <div class="srk-user-cell-content">
1765
+ <div *ngIf="row.user.avatar && !showAvatarInOrganization()" class="srk-user-avatar">
1766
+ <img
1767
+ [src]="formatAssetUrl(row.user.avatar, 'user.avatar')"
1768
+ alt="User Avatar"
1769
+ />
1770
+ </div>
1771
+ <div class="srk-user-body">
1772
+ <div class="srk-user-name-row">
1773
+ <span
1774
+ class="srk-user-name-text"
1775
+ [title]="resolveDisplayText(row.user.name)"
1776
+ >
1777
+ {{ resolveDisplayText(row.user.name) }}
1778
+ </span>
1779
+ <span class="srk-marker-dot-group">
1780
+ <span
1781
+ *ngFor="let marker of resolvedUserMarkers(row.user)"
1782
+ class="srk-marker srk-marker-dot srk--c-tooltip"
1783
+ [ngClass]="marker.presentation.className"
1784
+ [ngStyle]="marker.presentation.style"
1785
+ [attr.data-tooltip]="resolveDisplayText(marker.marker.label)"
1786
+ ></span>
1787
+ </span>
1788
+ </div>
1789
+ <p
1790
+ *ngIf="row.user.organization && !splitOrganization"
1791
+ class="srk-user-secondary-text srk--text-ellipsis"
1792
+ title=""
1793
+ >
1794
+ {{ resolveDisplayText(row.user.organization) }}
1795
+ </p>
1796
+ </div>
1797
+ </div>
1798
+ </td>
1799
+ </ng-template>
1800
+
1801
+ <td class="srk--text-right srk--nowrap">{{ row.score.value }}</td>
1802
+ <td *ngIf="showTimeColumn()" class="srk--text-right srk--nowrap">
1803
+ {{ row.score.time ? formatTime(row.score.time) : '-' }}
1804
+ </td>
1805
+
1806
+ <ng-container *ngFor="let status of row.statuses; let problemIndex = index">
1807
+ <ng-container *ngIf="statusCellTemplate; else defaultStatusCell">
1808
+ <ng-container
1809
+ [ngTemplateOutlet]="statusCellTemplate.templateRef"
1810
+ [ngTemplateOutletContext]="buildStatusCellContext(row, rowIndex, status, problemIndex)"
1811
+ />
1812
+ </ng-container>
1813
+ <ng-template #defaultStatusCell>
1814
+ <td
1815
+ *ngIf="status.result === 'FB' || status.result === 'AC'; else failedOrFrozenStatus"
1816
+ [ngClass]="statusCellClass(status)"
1817
+ [attr.role]="statusCellRole(status)"
1818
+ [attr.tabindex]="statusCellTabIndex(status)"
1819
+ (click)="emitSolutionClick($event, row, rowIndex, status, problemIndex)"
1820
+ (keydown.enter)="activateStatusCellFromKeyboard($event, row, rowIndex, status, problemIndex)"
1821
+ (keydown.space)="activateStatusCellFromKeyboard($event, row, rowIndex, status, problemIndex)"
1822
+ >
1823
+ <span
1824
+ *ngIf="statusColorAsText && status.result === 'FB'"
1825
+ class="srk-prest-status-block-fb-star"
1826
+ [textContent]="firstBloodStar"
1827
+ ></span>
1828
+ <ng-container *ngIf="statusPresentation(status) as presentation">
1829
+ <ng-container *ngIf="isNumber(presentation.score); else statusText">
1830
+ <span class="srk-prest-status-block-score">{{ presentation.score }}</span>
1831
+ <span class="srk-prest-status-block-score-details">
1832
+ {{ presentation.scoreDetails }}
1833
+ </span>
1834
+ </ng-container>
1835
+ <ng-template #statusText>
1836
+ <ng-container *ngIf="presentation.secondary !== undefined; else singleStatus">
1837
+ <span class="srk-prest-status-block-primary">{{ presentation.primary || '' }}</span>
1838
+ <span [textContent]="statusSeparator"></span>
1839
+ <span class="srk-prest-status-block-secondary">{{ presentation.secondary }}</span>
1840
+ </ng-container>
1841
+ <ng-template #singleStatus>{{ presentation.primary }}</ng-template>
1842
+ </ng-template>
1843
+ </ng-container>
1844
+ </td>
1845
+ <ng-template #failedOrFrozenStatus>
1846
+ <td
1847
+ *ngIf="status.result === '?' || status.result === 'RJ'; else emptyStatus"
1848
+ [ngClass]="statusCellClass(status)"
1849
+ [attr.role]="statusCellRole(status)"
1850
+ [attr.tabindex]="statusCellTabIndex(status)"
1851
+ (click)="emitSolutionClick($event, row, rowIndex, status, problemIndex)"
1852
+ (keydown.enter)="activateStatusCellFromKeyboard($event, row, rowIndex, status, problemIndex)"
1853
+ (keydown.space)="activateStatusCellFromKeyboard($event, row, rowIndex, status, problemIndex)"
1854
+ >
1855
+ <ng-container *ngIf="statusPresentation(status) as presentation">
1856
+ <ng-container *ngIf="presentation.secondary !== undefined; else singleStatus">
1857
+ <span class="srk-prest-status-block-primary">{{ presentation.primary || '' }}</span>
1858
+ <span [textContent]="statusSeparator"></span>
1859
+ <span class="srk-prest-status-block-secondary">{{ presentation.secondary }}</span>
1860
+ </ng-container>
1861
+ <ng-template #singleStatus>{{ presentation.primary }}</ng-template>
1862
+ </ng-container>
1863
+ </td>
1864
+ </ng-template>
1865
+ <ng-template #emptyStatus>
1866
+ <td class="srk-status-placeholder-cell srk--text-center srk--nowrap">
1867
+ {{ emptyStatusPlaceholder }}
1868
+ </td>
1869
+ </ng-template>
1870
+ </ng-template>
1871
+ </ng-container>
1872
+
1873
+ <td *ngIf="showDirtColumn" class="srk-dirt-cell srk--text-right srk--nowrap">
1874
+ {{ calculateDirtPercentage(row) }}
1875
+ </td>
1876
+ <td *ngIf="showSEColumn" class="srk-se-cell srk--text-right srk--nowrap">
1877
+ {{ calculateSEValue(row, problemStatistics) }}
1878
+ </td>
1879
+ </tr>
1880
+ </tbody>
1881
+ <tfoot *ngIf="showProblemStatisticsFooter">
1882
+ <tr
1883
+ *ngFor="let footerRow of problemStatisticsFooterRows"
1884
+ class="srk-problem-statistics-footer-row"
1885
+ >
1886
+ <td
1887
+ class="srk-problem-statistics-footer-labels srk--text-right srk--nowrap"
1888
+ [attr.colspan]="leftFooterColumnCount()"
1889
+ >
1890
+ <span
1891
+ class="srk-problem-statistics-footer-label srk--c-tooltip"
1892
+ [attr.data-tooltip]="footerRow.tooltip"
1893
+ >
1894
+ {{ footerRow.label }}
1895
+ </span>
1896
+ </td>
1897
+ <td
1898
+ *ngFor="let stat of problemStatistics"
1899
+ class="srk-problem-statistics-footer-cell srk--text-center srk--nowrap"
1900
+ >
1901
+ <span class="srk-problem-statistics-footer-primary">
1902
+ {{ getProblemStatisticsFooterCellPrimary(footerRow.key, stat) }}
1903
+ </span>
1904
+ <ng-container *ngIf="getProblemStatisticsFooterCellSecondary(footerRow.key, stat) !== undefined">
1905
+ <span> </span>
1906
+ <span class="srk-problem-statistics-footer-secondary">
1907
+ {{ getProblemStatisticsFooterCellSecondary(footerRow.key, stat) }}
1908
+ </span>
1909
+ </ng-container>
1910
+ </td>
1911
+ <td
1912
+ *ngIf="showDirtColumn"
1913
+ class="srk-problem-statistics-footer-cell srk-extra-statistics-footer-cell srk-dirt-footer-cell srk--nowrap"
1914
+ >
1915
+ <span class="srk-problem-statistics-footer-primary"></span>
1916
+ </td>
1917
+ <td
1918
+ *ngIf="showSEColumn"
1919
+ class="srk-problem-statistics-footer-cell srk-extra-statistics-footer-cell srk-se-footer-cell srk--nowrap"
1920
+ >
1921
+ <span class="srk-problem-statistics-footer-primary"></span>
1922
+ </td>
1923
+ </tr>
1924
+ <tr class="srk-problem-statistics-footer-row srk-problem-statistics-footer-problem-label-row">
1925
+ <td
1926
+ class="srk-problem-statistics-footer-labels srk--text-right srk--nowrap"
1927
+ [attr.colspan]="leftFooterColumnCount()"
1928
+ ></td>
1929
+ <td
1930
+ *ngFor="let problem of data.problems; let problemIndex = index"
1931
+ class="srk-problem-statistics-footer-cell srk-problem-statistics-footer-problem-header srk-problem-header srk--text-center srk--nowrap"
1932
+ [style.background-image]="problemHeaderBackgroundImage(problem, 0)"
1933
+ >
1934
+ <span class="srk--display-block">{{ problemAlias(problem, problemIndex) }}</span>
1935
+ </td>
1936
+ <td
1937
+ *ngIf="showDirtColumn"
1938
+ class="srk-problem-statistics-footer-cell srk-extra-statistics-footer-cell srk-dirt-footer-cell srk--nowrap"
1939
+ >
1940
+ <span class="srk-problem-statistics-footer-primary"></span>
1941
+ </td>
1942
+ <td
1943
+ *ngIf="showSEColumn"
1944
+ class="srk-problem-statistics-footer-cell srk-extra-statistics-footer-cell srk-se-footer-cell srk--nowrap"
1945
+ >
1946
+ <span class="srk-problem-statistics-footer-primary"></span>
1947
+ </td>
1948
+ </tr>
1949
+ </tfoot>
1950
+ </table>
1951
+ </div>
1952
+ </ng-template>
1953
+ `,
1954
+ }]
1955
+ }], propDecorators: { data: [{
1956
+ type: Input,
1957
+ args: [{ required: true }]
1958
+ }], theme: [{
1959
+ type: Input
1960
+ }], borderedRows: [{
1961
+ type: Input
1962
+ }], rowBordered: [{
1963
+ type: Input
1964
+ }], columnBordered: [{
1965
+ type: Input
1966
+ }], stripedRows: [{
1967
+ type: Input
1968
+ }], formatSrkAssetUrl: [{
1969
+ type: Input
1970
+ }], splitOrganization: [{
1971
+ type: Input
1972
+ }], columnTitles: [{
1973
+ type: Input
1974
+ }], statusCellPreset: [{
1975
+ type: Input
1976
+ }], statusColorAsText: [{
1977
+ type: Input
1978
+ }], showProblemStatisticsFooter: [{
1979
+ type: Input
1980
+ }], showDirtColumn: [{
1981
+ type: Input
1982
+ }], showSEColumn: [{
1983
+ type: Input
1984
+ }], emptyStatusPlaceholder: [{
1985
+ type: Input
1986
+ }], userAvatarPlacement: [{
1987
+ type: Input
1988
+ }], userClick: [{
1989
+ type: Output
1990
+ }], solutionClick: [{
1991
+ type: Output
1992
+ }], statusCellTemplate: [{
1993
+ type: ContentChild,
1994
+ args: [SrkStatusCellTemplateDirective]
1995
+ }], userCellTemplate: [{
1996
+ type: ContentChild,
1997
+ args: [SrkUserCellTemplateDirective]
1998
+ }] } });
1999
+
2000
+ /**
2001
+ * Generated bundle index. Do not edit.
2002
+ */
2003
+
2004
+ export { DefaultSolutionModalComponent, DefaultUserModalComponent, ModalComponent, ProgressBarComponent, RanklistComponent, SrkStatusCellTemplateDirective, SrkUserCellTemplateDirective };
2005
+ //# sourceMappingURL=algoux-standard-ranklist-renderer-component-angular.mjs.map