@algoux/standard-ranklist-renderer-component-angular 0.5.1 → 0.6.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.
Files changed (33) hide show
  1. package/README.md +48 -0
  2. package/package.json +8 -3
  3. package/dist/README.md +0 -100
  4. package/dist/esm2022/algoux-standard-ranklist-renderer-component-angular.mjs +0 -5
  5. package/dist/esm2022/lib/index.mjs +0 -8
  6. package/dist/esm2022/lib/modal/default-solution-modal.component.mjs +0 -146
  7. package/dist/esm2022/lib/modal/default-user-modal.component.mjs +0 -172
  8. package/dist/esm2022/lib/modal/modal-interactions.mjs +0 -2
  9. package/dist/esm2022/lib/modal/modal.component.mjs +0 -263
  10. package/dist/esm2022/lib/progress/progress-bar.component.mjs +0 -247
  11. package/dist/esm2022/lib/progress/progress-utils.mjs +0 -45
  12. package/dist/esm2022/lib/ranklist/ranklist-utils.mjs +0 -110
  13. package/dist/esm2022/lib/ranklist/ranklist.component.mjs +0 -593
  14. package/dist/esm2022/lib/ranklist/status-cell-template.directive.mjs +0 -18
  15. package/dist/esm2022/lib/ranklist/user-cell-template.directive.mjs +0 -18
  16. package/dist/esm2022/lib/types.mjs +0 -2
  17. package/dist/esm2022/public-api.mjs +0 -2
  18. package/dist/fesm2022/algoux-standard-ranklist-renderer-component-angular.mjs +0 -1585
  19. package/dist/fesm2022/algoux-standard-ranklist-renderer-component-angular.mjs.map +0 -1
  20. package/dist/index.d.ts +0 -5
  21. package/dist/lib/index.d.ts +0 -8
  22. package/dist/lib/modal/default-solution-modal.component.d.ts +0 -30
  23. package/dist/lib/modal/default-user-modal.component.d.ts +0 -34
  24. package/dist/lib/modal/modal-interactions.d.ts +0 -1
  25. package/dist/lib/modal/modal.component.d.ts +0 -49
  26. package/dist/lib/progress/progress-bar.component.d.ts +0 -31
  27. package/dist/lib/progress/progress-utils.d.ts +0 -15
  28. package/dist/lib/ranklist/ranklist-utils.d.ts +0 -22
  29. package/dist/lib/ranklist/ranklist.component.d.ts +0 -57
  30. package/dist/lib/ranklist/status-cell-template.directive.d.ts +0 -23
  31. package/dist/lib/ranklist/user-cell-template.directive.d.ts +0 -20
  32. package/dist/lib/types.d.ts +0 -27
  33. package/dist/public-api.d.ts +0 -1
@@ -1,1585 +0,0 @@
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, caniuse, captureModalTriggerPointFromMouseEvent } 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
- stripedRows = false;
1001
- formatSrkAssetUrl;
1002
- userClick = new EventEmitter();
1003
- solutionClick = new EventEmitter();
1004
- statusCellTemplate;
1005
- userCellTemplate;
1006
- supportedVersions = srkSupportedVersions;
1007
- isSupportedVersion() {
1008
- return caniuse(this.data.version);
1009
- }
1010
- showTimeColumn() {
1011
- return shouldShowTimeColumn(this.data.rows);
1012
- }
1013
- getRankValues(row) {
1014
- return row.rankValues || this.data.series.map(() => ({ rank: null, segmentIndex: null }));
1015
- }
1016
- getRankText(rankValue, row) {
1017
- return rankValue.rank ? rankValue.rank : row.user.official === false ? '*' : '';
1018
- }
1019
- resolveDisplayText(text) {
1020
- return resolveText(text);
1021
- }
1022
- problemAlias(problem, problemIndex) {
1023
- return problem.alias || numberToAlphabet(problemIndex);
1024
- }
1025
- problemStatsTitle(statistics) {
1026
- const ratio = statistics.submitted ? ((statistics.accepted / statistics.submitted) * 100).toFixed(1) : 0;
1027
- return `${statistics.accepted} / ${statistics.submitted} (${ratio}%)`;
1028
- }
1029
- problemHeaderBackgroundImage(problem) {
1030
- return getProblemHeaderBackgroundImage(problem.style, this.theme);
1031
- }
1032
- resolvedUserMarkers(user) {
1033
- return resolveUserMarkers(user, this.data.markers).map((marker) => ({
1034
- marker,
1035
- presentation: getMarkerPresentation(marker, this.theme),
1036
- }));
1037
- }
1038
- formatAssetUrl(url, field) {
1039
- return resolveSrkAssetUrl(url, field, this.formatSrkAssetUrl);
1040
- }
1041
- formatTime(time) {
1042
- return formatTimeDuration(time, 'min', Math.floor);
1043
- }
1044
- acceptedStatusDetails(status) {
1045
- return getAcceptedStatusDetails(status);
1046
- }
1047
- isNumber(value) {
1048
- return typeof value === 'number';
1049
- }
1050
- getStatusSolutions(status) {
1051
- return [...(status.solutions || [])].reverse();
1052
- }
1053
- statusCellClass(status) {
1054
- const classNames = ['srk-prest-status-block', 'srk--text-center', 'srk--nowrap'];
1055
- if (this.isStatusClickable(status)) {
1056
- classNames.push('srk--cursor-pointer');
1057
- }
1058
- if (status.result === 'FB') {
1059
- classNames.push('srk-prest-status-block-fb');
1060
- }
1061
- else if (status.result === 'AC') {
1062
- classNames.push('srk-prest-status-block-accepted');
1063
- }
1064
- else if (status.result === '?') {
1065
- classNames.push('srk-prest-status-block-frozen');
1066
- }
1067
- else if (status.result === 'RJ') {
1068
- classNames.push('srk-prest-status-block-failed');
1069
- }
1070
- return classNames.join(' ');
1071
- }
1072
- isStatusClickable(status) {
1073
- return this.getStatusSolutions(status).length > 0;
1074
- }
1075
- statusCellRole(status) {
1076
- return this.isStatusClickable(status) ? 'button' : null;
1077
- }
1078
- statusCellTabIndex(status) {
1079
- return this.isStatusClickable(status) ? 0 : null;
1080
- }
1081
- buildUserCellContext(row, rowIndex) {
1082
- return {
1083
- $implicit: row.user,
1084
- user: row.user,
1085
- row,
1086
- rowIndex,
1087
- ranklist: this.data,
1088
- markers: this.data.markers,
1089
- onClick: (event) => this.emitUserClick(event, row, rowIndex),
1090
- };
1091
- }
1092
- buildStatusCellContext(row, rowIndex, status, problemIndex) {
1093
- return {
1094
- $implicit: status,
1095
- status,
1096
- problem: this.data.problems[problemIndex],
1097
- problemIndex,
1098
- user: row.user,
1099
- row,
1100
- rowIndex,
1101
- ranklist: this.data,
1102
- solutions: this.getStatusSolutions(status),
1103
- onClick: (event) => this.emitSolutionClick(event, row, rowIndex, status, problemIndex),
1104
- };
1105
- }
1106
- activateUserCellFromKeyboard(event, row, rowIndex) {
1107
- event.preventDefault();
1108
- this.emitUserClick(undefined, row, rowIndex);
1109
- }
1110
- activateStatusCellFromKeyboard(event, row, rowIndex, status, problemIndex) {
1111
- event.preventDefault();
1112
- this.emitSolutionClick(undefined, row, rowIndex, status, problemIndex);
1113
- }
1114
- emitUserClick(event, row, rowIndex) {
1115
- if (event) {
1116
- captureModalTriggerPointFromMouseEvent(event, {
1117
- source: 'user-cell',
1118
- context: {
1119
- rowIndex,
1120
- userId: row.user.id || null,
1121
- userName: this.resolveDisplayText(row.user.name),
1122
- },
1123
- });
1124
- }
1125
- this.userClick.emit({
1126
- user: row.user,
1127
- row,
1128
- rowIndex,
1129
- ranklist: this.data,
1130
- });
1131
- }
1132
- emitSolutionClick(event, row, rowIndex, status, problemIndex) {
1133
- const solutions = this.getStatusSolutions(status);
1134
- if (!solutions.length) {
1135
- return;
1136
- }
1137
- if (event) {
1138
- captureModalTriggerPointFromMouseEvent(event, {
1139
- source: 'status-cell',
1140
- context: {
1141
- rowIndex,
1142
- problemIndex,
1143
- problemAlias: this.data.problems[problemIndex]?.alias || null,
1144
- problemTitle: this.data.problems[problemIndex]?.title || null,
1145
- userId: row.user.id || null,
1146
- },
1147
- });
1148
- }
1149
- this.solutionClick.emit({
1150
- user: row.user,
1151
- row,
1152
- rowIndex,
1153
- problemIndex,
1154
- problem: this.data.problems[problemIndex],
1155
- status,
1156
- solutions,
1157
- ranklist: this.data,
1158
- });
1159
- }
1160
- resolveSeriesSegment(rankValue, series) {
1161
- const index = rankValue.segmentIndex || rankValue.segmentIndex === 0 ? rankValue.segmentIndex : -1;
1162
- return (series?.segments || [])[index] || {};
1163
- }
1164
- getSeriesSegmentClass(rankValue, series) {
1165
- const segmentStyle = this.resolveSeriesSegment(rankValue, series).style;
1166
- return typeof segmentStyle === 'string' ? `srk-preset-series-segment-${segmentStyle}` : '';
1167
- }
1168
- getSeriesSegmentStyle(rankValue, series) {
1169
- const emptyColor = {
1170
- [EnumTheme.light]: undefined,
1171
- [EnumTheme.dark]: undefined,
1172
- };
1173
- const segmentStyle = this.resolveSeriesSegment(rankValue, series).style;
1174
- if (!segmentStyle || typeof segmentStyle === 'string') {
1175
- return {};
1176
- }
1177
- const style = resolveStyle(segmentStyle);
1178
- const textColor = style.textColor || emptyColor;
1179
- const backgroundColor = style.backgroundColor || emptyColor;
1180
- return {
1181
- color: textColor[this.theme],
1182
- backgroundColor: backgroundColor[this.theme],
1183
- };
1184
- }
1185
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: RanklistComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1186
- 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", stripedRows: "stripedRows", formatSrkAssetUrl: "formatSrkAssetUrl" }, 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: `
1187
- <div *ngIf="data.type !== 'general'; else versionState">
1188
- srk type "{{ data.type }}" is not supported
1189
- </div>
1190
-
1191
- <ng-template #versionState>
1192
- <div *ngIf="!isSupportedVersion(); else ranklistTable">
1193
- srk version "{{ data.version }}" is not supported (current supported: {{ supportedVersions }})
1194
- </div>
1195
- </ng-template>
1196
-
1197
- <ng-template #ranklistTable>
1198
- <div class="srk-common-table srk-main">
1199
- <table
1200
- [class.srk-table-row-bordered]="borderedRows"
1201
- [class.srk-table-row-striped]="stripedRows"
1202
- >
1203
- <thead>
1204
- <tr>
1205
- <th
1206
- *ngFor="let seriesItem of data.series"
1207
- class="srk-series-header srk--text-right srk--nowrap"
1208
- >
1209
- {{ seriesItem.title }}
1210
- </th>
1211
- <th class="srk--text-left srk--nowrap">Name</th>
1212
- <th class="srk--nowrap">Score</th>
1213
- <th *ngIf="showTimeColumn()" class="srk--nowrap">Time</th>
1214
- <th
1215
- *ngFor="let problem of data.problems; let problemIndex = index"
1216
- class="srk--nowrap srk-problem-header"
1217
- [style.background-image]="problemHeaderBackgroundImage(problem)"
1218
- >
1219
- <a
1220
- *ngIf="problem.link; else unlinkedProblemHeader"
1221
- [href]="problem.link"
1222
- target="_blank"
1223
- rel="noopener noreferrer"
1224
- style="color: unset"
1225
- >
1226
- <span class="srk--display-block">{{ problemAlias(problem, problemIndex) }}</span>
1227
- <span
1228
- *ngIf="problem.statistics"
1229
- class="srk--display-block srk-problem-stats"
1230
- [title]="problemStatsTitle(problem.statistics)"
1231
- >
1232
- {{ problem.statistics.accepted }}
1233
- </span>
1234
- </a>
1235
- <ng-template #unlinkedProblemHeader>
1236
- <span class="srk--display-block">{{ problemAlias(problem, problemIndex) }}</span>
1237
- <span
1238
- *ngIf="problem.statistics"
1239
- class="srk--display-block srk-problem-stats"
1240
- [title]="problemStatsTitle(problem.statistics)"
1241
- >
1242
- {{ problem.statistics.accepted }}
1243
- </span>
1244
- </ng-template>
1245
- </th>
1246
- </tr>
1247
- </thead>
1248
- <tbody>
1249
- <tr *ngFor="let row of data.rows; let rowIndex = index">
1250
- <td
1251
- *ngFor="let rankValue of getRankValues(row); let seriesIndex = index"
1252
- class="srk--text-right srk--nowrap"
1253
- [ngClass]="getSeriesSegmentClass(rankValue, data.series[seriesIndex])"
1254
- [ngStyle]="getSeriesSegmentStyle(rankValue, data.series[seriesIndex])"
1255
- >
1256
- {{ getRankText(rankValue, row) }}
1257
- </td>
1258
-
1259
- <ng-container *ngIf="userCellTemplate; else defaultUserCell">
1260
- <ng-container
1261
- [ngTemplateOutlet]="userCellTemplate.templateRef"
1262
- [ngTemplateOutletContext]="buildUserCellContext(row, rowIndex)"
1263
- />
1264
- </ng-container>
1265
- <ng-template #defaultUserCell>
1266
- <td
1267
- class="srk--text-left srk--nowrap srk-user-cell srk--cursor-pointer"
1268
- role="button"
1269
- tabindex="0"
1270
- (click)="emitUserClick($event, row, rowIndex)"
1271
- (keydown.enter)="activateUserCellFromKeyboard($event, row, rowIndex)"
1272
- (keydown.space)="activateUserCellFromKeyboard($event, row, rowIndex)"
1273
- >
1274
- <div class="srk-user-cell-content">
1275
- <div *ngIf="row.user.avatar" class="srk-user-avatar">
1276
- <img
1277
- [src]="formatAssetUrl(row.user.avatar, 'user.avatar')"
1278
- alt="User Avatar"
1279
- />
1280
- </div>
1281
- <div class="srk-user-body">
1282
- <div class="srk-user-name-row">
1283
- <span
1284
- class="srk-user-name-text"
1285
- [title]="resolveDisplayText(row.user.name)"
1286
- >
1287
- {{ resolveDisplayText(row.user.name) }}
1288
- </span>
1289
- <span class="srk-marker-dot-group">
1290
- <span
1291
- *ngFor="let marker of resolvedUserMarkers(row.user)"
1292
- class="srk-marker srk-marker-dot srk--c-tooltip"
1293
- [ngClass]="marker.presentation.className"
1294
- [ngStyle]="marker.presentation.style"
1295
- [attr.data-tooltip]="resolveDisplayText(marker.marker.label)"
1296
- ></span>
1297
- </span>
1298
- </div>
1299
- <p
1300
- *ngIf="row.user.organization"
1301
- class="srk-user-secondary-text srk--text-ellipsis"
1302
- title=""
1303
- >
1304
- {{ resolveDisplayText(row.user.organization) }}
1305
- </p>
1306
- </div>
1307
- </div>
1308
- </td>
1309
- </ng-template>
1310
-
1311
- <td class="srk--text-right srk--nowrap">{{ row.score.value }}</td>
1312
- <td *ngIf="showTimeColumn()" class="srk--text-right srk--nowrap">
1313
- {{ row.score.time ? formatTime(row.score.time) : '-' }}
1314
- </td>
1315
-
1316
- <ng-container *ngFor="let status of row.statuses; let problemIndex = index">
1317
- <ng-container *ngIf="statusCellTemplate; else defaultStatusCell">
1318
- <ng-container
1319
- [ngTemplateOutlet]="statusCellTemplate.templateRef"
1320
- [ngTemplateOutletContext]="buildStatusCellContext(row, rowIndex, status, problemIndex)"
1321
- />
1322
- </ng-container>
1323
- <ng-template #defaultStatusCell>
1324
- <td
1325
- *ngIf="status.result === 'FB' || status.result === 'AC'; else failedOrFrozenStatus"
1326
- [ngClass]="statusCellClass(status)"
1327
- [attr.role]="statusCellRole(status)"
1328
- [attr.tabindex]="statusCellTabIndex(status)"
1329
- (click)="emitSolutionClick($event, row, rowIndex, status, problemIndex)"
1330
- (keydown.enter)="activateStatusCellFromKeyboard($event, row, rowIndex, status, problemIndex)"
1331
- (keydown.space)="activateStatusCellFromKeyboard($event, row, rowIndex, status, problemIndex)"
1332
- >
1333
- <ng-container *ngIf="isNumber(status.score); else acceptedDetails">
1334
- <span class="srk-prest-status-block-score">{{ status.score }}</span>
1335
- <span class="srk-prest-status-block-score-details">
1336
- {{ acceptedStatusDetails(status) }}
1337
- </span>
1338
- </ng-container>
1339
- <ng-template #acceptedDetails>
1340
- {{ acceptedStatusDetails(status) }}
1341
- </ng-template>
1342
- </td>
1343
- <ng-template #failedOrFrozenStatus>
1344
- <td
1345
- *ngIf="status.result === '?' || status.result === 'RJ'; else emptyStatus"
1346
- [ngClass]="statusCellClass(status)"
1347
- [attr.role]="statusCellRole(status)"
1348
- [attr.tabindex]="statusCellTabIndex(status)"
1349
- (click)="emitSolutionClick($event, row, rowIndex, status, problemIndex)"
1350
- (keydown.enter)="activateStatusCellFromKeyboard($event, row, rowIndex, status, problemIndex)"
1351
- (keydown.space)="activateStatusCellFromKeyboard($event, row, rowIndex, status, problemIndex)"
1352
- >
1353
- {{ status.tries }}
1354
- </td>
1355
- </ng-template>
1356
- <ng-template #emptyStatus>
1357
- <td></td>
1358
- </ng-template>
1359
- </ng-template>
1360
- </ng-container>
1361
- </tr>
1362
- </tbody>
1363
- </table>
1364
- </div>
1365
- </ng-template>
1366
- `, 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 });
1367
- }
1368
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: RanklistComponent, decorators: [{
1369
- type: Component,
1370
- args: [{
1371
- selector: 'srk-ranklist',
1372
- standalone: true,
1373
- changeDetection: ChangeDetectionStrategy.OnPush,
1374
- imports: [CommonModule],
1375
- template: `
1376
- <div *ngIf="data.type !== 'general'; else versionState">
1377
- srk type "{{ data.type }}" is not supported
1378
- </div>
1379
-
1380
- <ng-template #versionState>
1381
- <div *ngIf="!isSupportedVersion(); else ranklistTable">
1382
- srk version "{{ data.version }}" is not supported (current supported: {{ supportedVersions }})
1383
- </div>
1384
- </ng-template>
1385
-
1386
- <ng-template #ranklistTable>
1387
- <div class="srk-common-table srk-main">
1388
- <table
1389
- [class.srk-table-row-bordered]="borderedRows"
1390
- [class.srk-table-row-striped]="stripedRows"
1391
- >
1392
- <thead>
1393
- <tr>
1394
- <th
1395
- *ngFor="let seriesItem of data.series"
1396
- class="srk-series-header srk--text-right srk--nowrap"
1397
- >
1398
- {{ seriesItem.title }}
1399
- </th>
1400
- <th class="srk--text-left srk--nowrap">Name</th>
1401
- <th class="srk--nowrap">Score</th>
1402
- <th *ngIf="showTimeColumn()" class="srk--nowrap">Time</th>
1403
- <th
1404
- *ngFor="let problem of data.problems; let problemIndex = index"
1405
- class="srk--nowrap srk-problem-header"
1406
- [style.background-image]="problemHeaderBackgroundImage(problem)"
1407
- >
1408
- <a
1409
- *ngIf="problem.link; else unlinkedProblemHeader"
1410
- [href]="problem.link"
1411
- target="_blank"
1412
- rel="noopener noreferrer"
1413
- style="color: unset"
1414
- >
1415
- <span class="srk--display-block">{{ problemAlias(problem, problemIndex) }}</span>
1416
- <span
1417
- *ngIf="problem.statistics"
1418
- class="srk--display-block srk-problem-stats"
1419
- [title]="problemStatsTitle(problem.statistics)"
1420
- >
1421
- {{ problem.statistics.accepted }}
1422
- </span>
1423
- </a>
1424
- <ng-template #unlinkedProblemHeader>
1425
- <span class="srk--display-block">{{ problemAlias(problem, problemIndex) }}</span>
1426
- <span
1427
- *ngIf="problem.statistics"
1428
- class="srk--display-block srk-problem-stats"
1429
- [title]="problemStatsTitle(problem.statistics)"
1430
- >
1431
- {{ problem.statistics.accepted }}
1432
- </span>
1433
- </ng-template>
1434
- </th>
1435
- </tr>
1436
- </thead>
1437
- <tbody>
1438
- <tr *ngFor="let row of data.rows; let rowIndex = index">
1439
- <td
1440
- *ngFor="let rankValue of getRankValues(row); let seriesIndex = index"
1441
- class="srk--text-right srk--nowrap"
1442
- [ngClass]="getSeriesSegmentClass(rankValue, data.series[seriesIndex])"
1443
- [ngStyle]="getSeriesSegmentStyle(rankValue, data.series[seriesIndex])"
1444
- >
1445
- {{ getRankText(rankValue, row) }}
1446
- </td>
1447
-
1448
- <ng-container *ngIf="userCellTemplate; else defaultUserCell">
1449
- <ng-container
1450
- [ngTemplateOutlet]="userCellTemplate.templateRef"
1451
- [ngTemplateOutletContext]="buildUserCellContext(row, rowIndex)"
1452
- />
1453
- </ng-container>
1454
- <ng-template #defaultUserCell>
1455
- <td
1456
- class="srk--text-left srk--nowrap srk-user-cell srk--cursor-pointer"
1457
- role="button"
1458
- tabindex="0"
1459
- (click)="emitUserClick($event, row, rowIndex)"
1460
- (keydown.enter)="activateUserCellFromKeyboard($event, row, rowIndex)"
1461
- (keydown.space)="activateUserCellFromKeyboard($event, row, rowIndex)"
1462
- >
1463
- <div class="srk-user-cell-content">
1464
- <div *ngIf="row.user.avatar" class="srk-user-avatar">
1465
- <img
1466
- [src]="formatAssetUrl(row.user.avatar, 'user.avatar')"
1467
- alt="User Avatar"
1468
- />
1469
- </div>
1470
- <div class="srk-user-body">
1471
- <div class="srk-user-name-row">
1472
- <span
1473
- class="srk-user-name-text"
1474
- [title]="resolveDisplayText(row.user.name)"
1475
- >
1476
- {{ resolveDisplayText(row.user.name) }}
1477
- </span>
1478
- <span class="srk-marker-dot-group">
1479
- <span
1480
- *ngFor="let marker of resolvedUserMarkers(row.user)"
1481
- class="srk-marker srk-marker-dot srk--c-tooltip"
1482
- [ngClass]="marker.presentation.className"
1483
- [ngStyle]="marker.presentation.style"
1484
- [attr.data-tooltip]="resolveDisplayText(marker.marker.label)"
1485
- ></span>
1486
- </span>
1487
- </div>
1488
- <p
1489
- *ngIf="row.user.organization"
1490
- class="srk-user-secondary-text srk--text-ellipsis"
1491
- title=""
1492
- >
1493
- {{ resolveDisplayText(row.user.organization) }}
1494
- </p>
1495
- </div>
1496
- </div>
1497
- </td>
1498
- </ng-template>
1499
-
1500
- <td class="srk--text-right srk--nowrap">{{ row.score.value }}</td>
1501
- <td *ngIf="showTimeColumn()" class="srk--text-right srk--nowrap">
1502
- {{ row.score.time ? formatTime(row.score.time) : '-' }}
1503
- </td>
1504
-
1505
- <ng-container *ngFor="let status of row.statuses; let problemIndex = index">
1506
- <ng-container *ngIf="statusCellTemplate; else defaultStatusCell">
1507
- <ng-container
1508
- [ngTemplateOutlet]="statusCellTemplate.templateRef"
1509
- [ngTemplateOutletContext]="buildStatusCellContext(row, rowIndex, status, problemIndex)"
1510
- />
1511
- </ng-container>
1512
- <ng-template #defaultStatusCell>
1513
- <td
1514
- *ngIf="status.result === 'FB' || status.result === 'AC'; else failedOrFrozenStatus"
1515
- [ngClass]="statusCellClass(status)"
1516
- [attr.role]="statusCellRole(status)"
1517
- [attr.tabindex]="statusCellTabIndex(status)"
1518
- (click)="emitSolutionClick($event, row, rowIndex, status, problemIndex)"
1519
- (keydown.enter)="activateStatusCellFromKeyboard($event, row, rowIndex, status, problemIndex)"
1520
- (keydown.space)="activateStatusCellFromKeyboard($event, row, rowIndex, status, problemIndex)"
1521
- >
1522
- <ng-container *ngIf="isNumber(status.score); else acceptedDetails">
1523
- <span class="srk-prest-status-block-score">{{ status.score }}</span>
1524
- <span class="srk-prest-status-block-score-details">
1525
- {{ acceptedStatusDetails(status) }}
1526
- </span>
1527
- </ng-container>
1528
- <ng-template #acceptedDetails>
1529
- {{ acceptedStatusDetails(status) }}
1530
- </ng-template>
1531
- </td>
1532
- <ng-template #failedOrFrozenStatus>
1533
- <td
1534
- *ngIf="status.result === '?' || status.result === 'RJ'; else emptyStatus"
1535
- [ngClass]="statusCellClass(status)"
1536
- [attr.role]="statusCellRole(status)"
1537
- [attr.tabindex]="statusCellTabIndex(status)"
1538
- (click)="emitSolutionClick($event, row, rowIndex, status, problemIndex)"
1539
- (keydown.enter)="activateStatusCellFromKeyboard($event, row, rowIndex, status, problemIndex)"
1540
- (keydown.space)="activateStatusCellFromKeyboard($event, row, rowIndex, status, problemIndex)"
1541
- >
1542
- {{ status.tries }}
1543
- </td>
1544
- </ng-template>
1545
- <ng-template #emptyStatus>
1546
- <td></td>
1547
- </ng-template>
1548
- </ng-template>
1549
- </ng-container>
1550
- </tr>
1551
- </tbody>
1552
- </table>
1553
- </div>
1554
- </ng-template>
1555
- `,
1556
- }]
1557
- }], propDecorators: { data: [{
1558
- type: Input,
1559
- args: [{ required: true }]
1560
- }], theme: [{
1561
- type: Input
1562
- }], borderedRows: [{
1563
- type: Input
1564
- }], stripedRows: [{
1565
- type: Input
1566
- }], formatSrkAssetUrl: [{
1567
- type: Input
1568
- }], userClick: [{
1569
- type: Output
1570
- }], solutionClick: [{
1571
- type: Output
1572
- }], statusCellTemplate: [{
1573
- type: ContentChild,
1574
- args: [SrkStatusCellTemplateDirective]
1575
- }], userCellTemplate: [{
1576
- type: ContentChild,
1577
- args: [SrkUserCellTemplateDirective]
1578
- }] } });
1579
-
1580
- /**
1581
- * Generated bundle index. Do not edit.
1582
- */
1583
-
1584
- export { DefaultSolutionModalComponent, DefaultUserModalComponent, ModalComponent, ProgressBarComponent, RanklistComponent, SrkStatusCellTemplateDirective, SrkUserCellTemplateDirective };
1585
- //# sourceMappingURL=algoux-standard-ranklist-renderer-component-angular.mjs.map