@algoux/standard-ranklist-renderer-component-react 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1014 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => {
4
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
+ return value;
6
+ };
7
+ import "@algoux/standard-ranklist-renderer-component-styles";
8
+ import classnames from "classnames";
9
+ import React from "react";
10
+ import { numberToAlphabet, resolveText, resolveUserMarkers, EnumTheme, resolveStyle, formatTimeDuration, secToTimeStr } from "@algoux/standard-ranklist-utils";
11
+ export { convertToStaticRanklist } from "@algoux/standard-ranklist-utils";
12
+ import { getProblemHeaderBackgroundImage, getAcceptedStatusDetails, captureModalTriggerPointFromMouseEvent, getMarkerPresentation, resolveSrkAssetUrl, caniuse, srkSupportedVersions, shouldShowTimeColumn, getProgressDurationMinutes, getProgressMaxAvailableMinutes, isProgressEnded, getProgressMetrics, ensureModalInteractionTracker, unregisterModalFocusScope, MODAL_ANIMATION_DURATION_MS, resolveModalTransformOrigin, lockModalBodyScroll, unlockModalBodyScroll, registerModalFocusScope, SRK_ANIMATED_MODAL_ROOT_CLASS, getSolutionResultMeta, formatSolutionTimestamp, getSolutionModalTitle } from "@algoux/standard-ranklist-renderer-component-core";
13
+ export { caniuse, srkSupportedVersions } from "@algoux/standard-ranklist-renderer-component-core";
14
+ import ReactDOM from "react-dom";
15
+ var jsxRuntime = { exports: {} };
16
+ var reactJsxRuntime_production_min = {};
17
+ /** @license React v16.14.0
18
+ * react-jsx-runtime.production.min.js
19
+ *
20
+ * Copyright (c) Facebook, Inc. and its affiliates.
21
+ *
22
+ * This source code is licensed under the MIT license found in the
23
+ * LICENSE file in the root directory of this source tree.
24
+ */
25
+ var f = React, g = 60103;
26
+ reactJsxRuntime_production_min.Fragment = 60107;
27
+ if ("function" === typeof Symbol && Symbol.for) {
28
+ var h = Symbol.for;
29
+ g = h("react.element");
30
+ reactJsxRuntime_production_min.Fragment = h("react.fragment");
31
+ }
32
+ var m = f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner, n = Object.prototype.hasOwnProperty, p = { key: true, ref: true, __self: true, __source: true };
33
+ function q(c, a, k) {
34
+ var b, d = {}, e = null, l = null;
35
+ void 0 !== k && (e = "" + k);
36
+ void 0 !== a.key && (e = "" + a.key);
37
+ void 0 !== a.ref && (l = a.ref);
38
+ for (b in a)
39
+ n.call(a, b) && !p.hasOwnProperty(b) && (d[b] = a[b]);
40
+ if (c && c.defaultProps)
41
+ for (b in a = c.defaultProps, a)
42
+ void 0 === d[b] && (d[b] = a[b]);
43
+ return { $$typeof: g, type: c, key: e, ref: l, props: d, _owner: m.current };
44
+ }
45
+ reactJsxRuntime_production_min.jsx = q;
46
+ reactJsxRuntime_production_min.jsxs = q;
47
+ {
48
+ jsxRuntime.exports = reactJsxRuntime_production_min;
49
+ }
50
+ const jsx = jsxRuntime.exports.jsx;
51
+ const jsxs = jsxRuntime.exports.jsxs;
52
+ const Fragment = jsxRuntime.exports.Fragment;
53
+ function ProblemHeaderCell({
54
+ problem,
55
+ index,
56
+ theme
57
+ }) {
58
+ const alias = problem.alias ? problem.alias : numberToAlphabet(index);
59
+ const stat = problem.statistics;
60
+ const statDesc = stat ? `${stat.accepted} / ${stat.submitted} (${stat.submitted ? (stat.accepted / stat.submitted * 100).toFixed(1) : 0}%)` : "";
61
+ const innerComp = /* @__PURE__ */ jsxs(Fragment, {
62
+ children: [/* @__PURE__ */ jsx("span", {
63
+ className: "srk--display-block",
64
+ children: alias
65
+ }), stat ? /* @__PURE__ */ jsx("span", {
66
+ title: statDesc,
67
+ className: "srk--display-block srk-problem-stats",
68
+ children: stat.accepted
69
+ }) : null]
70
+ });
71
+ const cellComp = problem.link ? /* @__PURE__ */ jsx("a", {
72
+ href: problem.link,
73
+ target: "_blank",
74
+ rel: "noopener noreferrer",
75
+ style: {
76
+ color: "unset"
77
+ },
78
+ children: innerComp
79
+ }) : innerComp;
80
+ return /* @__PURE__ */ jsx("th", {
81
+ className: "srk--nowrap srk-problem-header",
82
+ style: {
83
+ backgroundImage: getProblemHeaderBackgroundImage(problem.style, theme)
84
+ },
85
+ children: cellComp
86
+ }, problem.alias || resolveText(problem.title));
87
+ }
88
+ function StatusCell({
89
+ status,
90
+ problem,
91
+ problemIndex,
92
+ user,
93
+ row,
94
+ rowIndex,
95
+ ranklist,
96
+ onSolutionClick
97
+ }) {
98
+ const problemKey = (problem == null ? void 0 : problem.alias) || resolveText(problem == null ? void 0 : problem.title) || problemIndex;
99
+ const problemTitle = resolveText(problem == null ? void 0 : problem.title) || null;
100
+ const solutions = [...status.solutions || []].reverse();
101
+ const hasSolutions = solutions.length > 0;
102
+ const isClickable = hasSolutions && !!onSolutionClick;
103
+ const commonClassName = classnames("srk-prest-status-block srk--text-center srk--nowrap", {
104
+ "srk--cursor-pointer": isClickable
105
+ });
106
+ const onClick = isClickable ? (event) => {
107
+ event.preventDefault();
108
+ captureModalTriggerPointFromMouseEvent(event.nativeEvent, {
109
+ source: "status-cell",
110
+ context: {
111
+ rowIndex,
112
+ problemIndex,
113
+ problemAlias: (problem == null ? void 0 : problem.alias) || null,
114
+ problemTitle,
115
+ userId: user.id || null
116
+ }
117
+ });
118
+ onSolutionClick == null ? void 0 : onSolutionClick({
119
+ user,
120
+ row,
121
+ rowIndex,
122
+ problemIndex,
123
+ problem,
124
+ status,
125
+ solutions,
126
+ ranklist
127
+ });
128
+ } : void 0;
129
+ if (status.result === "FB") {
130
+ return /* @__PURE__ */ jsx("td", {
131
+ onClick,
132
+ className: classnames(commonClassName, "srk-prest-status-block-fb"),
133
+ children: renderAcceptedStatusBody(status)
134
+ }, problemKey);
135
+ }
136
+ if (status.result === "AC") {
137
+ return /* @__PURE__ */ jsx("td", {
138
+ onClick,
139
+ className: classnames(commonClassName, "srk-prest-status-block-accepted"),
140
+ children: renderAcceptedStatusBody(status)
141
+ }, problemKey);
142
+ }
143
+ if (status.result === "?") {
144
+ return /* @__PURE__ */ jsx("td", {
145
+ onClick,
146
+ className: classnames(commonClassName, "srk-prest-status-block-frozen"),
147
+ children: status.tries
148
+ }, problemKey);
149
+ }
150
+ if (status.result === "RJ") {
151
+ return /* @__PURE__ */ jsx("td", {
152
+ onClick,
153
+ className: classnames(commonClassName, "srk-prest-status-block-failed"),
154
+ children: status.tries
155
+ }, problemKey);
156
+ }
157
+ return /* @__PURE__ */ jsx("td", {}, problemKey);
158
+ }
159
+ function renderAcceptedStatusBody(status) {
160
+ const details = getAcceptedStatusDetails(status);
161
+ if (typeof status.score === "number") {
162
+ return /* @__PURE__ */ jsxs(Fragment, {
163
+ children: [/* @__PURE__ */ jsx("span", {
164
+ className: "srk-prest-status-block-score",
165
+ children: status.score
166
+ }), /* @__PURE__ */ jsx("span", {
167
+ className: "srk-prest-status-block-score-details",
168
+ children: details
169
+ })]
170
+ });
171
+ }
172
+ return /* @__PURE__ */ jsx(Fragment, {
173
+ children: details
174
+ });
175
+ }
176
+ function UserCell({
177
+ user,
178
+ row,
179
+ rowIndex,
180
+ ranklist,
181
+ markers = [],
182
+ theme,
183
+ formatSrkAssetUrl,
184
+ onUserClick
185
+ }) {
186
+ const userMarkers = resolveUserMarkers(user, markers);
187
+ const markerCalcStyles = userMarkers.map((marker) => getMarkerPresentation(marker, theme));
188
+ const name = resolveText(user.name);
189
+ return /* @__PURE__ */ jsx("td", {
190
+ className: classnames("srk--text-left srk--nowrap srk-user-cell", {
191
+ "srk--cursor-pointer": !!onUserClick
192
+ }),
193
+ title: "",
194
+ onClick: (event) => {
195
+ event.preventDefault();
196
+ captureModalTriggerPointFromMouseEvent(event.nativeEvent, {
197
+ source: "user-cell",
198
+ context: {
199
+ rowIndex,
200
+ userId: user.id || null,
201
+ userName: name
202
+ }
203
+ });
204
+ onUserClick == null ? void 0 : onUserClick({
205
+ user,
206
+ row,
207
+ rowIndex,
208
+ ranklist
209
+ });
210
+ },
211
+ children: /* @__PURE__ */ jsxs("div", {
212
+ className: "srk-user-cell-content",
213
+ children: [user.avatar && /* @__PURE__ */ jsx("div", {
214
+ className: "srk-user-avatar",
215
+ children: /* @__PURE__ */ jsx("img", {
216
+ src: formatSrkAssetUrl(user.avatar, "user.avatar"),
217
+ alt: "User Avatar"
218
+ })
219
+ }), /* @__PURE__ */ jsxs("div", {
220
+ className: "srk-user-body",
221
+ children: [/* @__PURE__ */ jsxs("div", {
222
+ className: "srk-user-name-row",
223
+ children: [/* @__PURE__ */ jsx("span", {
224
+ className: "srk-user-name-text",
225
+ title: name,
226
+ children: name
227
+ }), /* @__PURE__ */ jsx("span", {
228
+ className: "srk-marker-dot-group",
229
+ children: markerCalcStyles.map((markerStyle, index) => /* @__PURE__ */ jsx("span", {
230
+ className: classnames("srk-marker srk-marker-dot srk--c-tooltip", markerStyle.className),
231
+ style: markerStyle.style,
232
+ "data-tooltip": resolveText(userMarkers[index].label)
233
+ }, userMarkers[index].id))
234
+ })]
235
+ }), !!user.organization && /* @__PURE__ */ jsx("p", {
236
+ className: "srk-user-secondary-text srk--text-ellipsis",
237
+ title: "",
238
+ children: resolveText(user.organization)
239
+ })]
240
+ })]
241
+ })
242
+ });
243
+ }
244
+ class Ranklist extends React.Component {
245
+ constructor(props) {
246
+ super(props);
247
+ __publicField(this, "formatSrkAssetUrl", (url, field) => {
248
+ return resolveSrkAssetUrl(url, field, this.props.formatSrkAssetUrl);
249
+ });
250
+ __publicField(this, "renderContestBanner", () => {
251
+ const banner = this.props.data.contest.banner;
252
+ if (!banner) {
253
+ return null;
254
+ }
255
+ let imgSrc = "";
256
+ let link = "";
257
+ if (typeof banner === "string") {
258
+ imgSrc = banner;
259
+ } else {
260
+ imgSrc = banner.image;
261
+ link = banner.link;
262
+ }
263
+ const imgComp = /* @__PURE__ */ jsx("img", {
264
+ src: this.formatSrkAssetUrl(imgSrc, "contest.banner"),
265
+ alt: "Contest Banner",
266
+ className: "srk--full-width"
267
+ });
268
+ if (link) {
269
+ return this.genExternalLink(link, imgComp);
270
+ } else {
271
+ return imgComp;
272
+ }
273
+ });
274
+ __publicField(this, "renderSingleSeriesBody", (rk, series, row) => {
275
+ const theme = this.props.theme;
276
+ const innerComp = rk.rank ? rk.rank : row.user.official === false ? "\uFF0A" : "";
277
+ const segment = (series.segments || [])[rk.segmentIndex || rk.segmentIndex === 0 ? rk.segmentIndex : -1] || {};
278
+ const segmentStyle = segment.style;
279
+ let className = "";
280
+ let textColor = {
281
+ [EnumTheme.light]: void 0,
282
+ [EnumTheme.dark]: void 0
283
+ };
284
+ let backgroundColor = {
285
+ [EnumTheme.light]: void 0,
286
+ [EnumTheme.dark]: void 0
287
+ };
288
+ if (typeof segmentStyle === "string") {
289
+ className = `srk-preset-series-segment-${segmentStyle}`;
290
+ } else if (segmentStyle) {
291
+ const style = resolveStyle(segmentStyle);
292
+ textColor = style.textColor;
293
+ backgroundColor = style.backgroundColor;
294
+ }
295
+ return /* @__PURE__ */ jsx("td", {
296
+ className: classnames("srk--text-right srk--nowrap", className),
297
+ style: {
298
+ color: textColor[theme],
299
+ backgroundColor: backgroundColor[theme]
300
+ },
301
+ children: innerComp
302
+ }, series.title);
303
+ });
304
+ this.state = {
305
+ marker: "all",
306
+ error: null
307
+ };
308
+ }
309
+ genExternalLink(link, children) {
310
+ return /* @__PURE__ */ jsx("a", {
311
+ href: link,
312
+ target: "_blank",
313
+ rel: "noopener noreferrer",
314
+ style: {
315
+ color: "unset"
316
+ },
317
+ children
318
+ });
319
+ }
320
+ render() {
321
+ var _a, _b, _c;
322
+ const {
323
+ error
324
+ } = this.state;
325
+ if (error) {
326
+ return /* @__PURE__ */ jsx("div", {
327
+ children: /* @__PURE__ */ jsxs("div", {
328
+ className: "srk-error",
329
+ children: [/* @__PURE__ */ jsxs("pre", {
330
+ children: ["Error: ", error]
331
+ }), /* @__PURE__ */ jsx("p", {
332
+ children: "Please wait for data correction or refresh page"
333
+ })]
334
+ })
335
+ });
336
+ }
337
+ const {
338
+ data,
339
+ borderedRows,
340
+ stripedRows
341
+ } = this.props;
342
+ const {
343
+ type,
344
+ version,
345
+ problems,
346
+ series,
347
+ rows
348
+ } = data;
349
+ const ProblemHeaderCellComponent = ((_a = this.props.components) == null ? void 0 : _a.problemHeaderCell) || ProblemHeaderCell;
350
+ const UserCellComponent = ((_b = this.props.components) == null ? void 0 : _b.userCell) || UserCell;
351
+ const StatusCellComponent = ((_c = this.props.components) == null ? void 0 : _c.statusCell) || StatusCell;
352
+ if (type !== "general") {
353
+ return /* @__PURE__ */ jsxs("div", {
354
+ children: ['srk type "', type, '" is not supported']
355
+ });
356
+ }
357
+ if (!caniuse(version)) {
358
+ return /* @__PURE__ */ jsxs("div", {
359
+ children: ['srk version "', version, '" is not supported (current supported: ', srkSupportedVersions, ")"]
360
+ });
361
+ }
362
+ const showTimeColumn = shouldShowTimeColumn(rows);
363
+ return /* @__PURE__ */ jsx(Fragment, {
364
+ children: /* @__PURE__ */ jsx("div", {
365
+ className: "srk-common-table srk-main",
366
+ children: /* @__PURE__ */ jsxs("table", {
367
+ className: classnames({
368
+ "srk-table-row-bordered": borderedRows,
369
+ "srk-table-row-striped": stripedRows
370
+ }),
371
+ children: [/* @__PURE__ */ jsx("thead", {
372
+ children: /* @__PURE__ */ jsxs("tr", {
373
+ children: [series.map((s) => /* @__PURE__ */ jsx("th", {
374
+ className: "srk-series-header srk--text-right srk--nowrap",
375
+ children: s.title
376
+ }, s.title)), /* @__PURE__ */ jsx("th", {
377
+ className: "srk--text-left srk--nowrap",
378
+ children: "Name"
379
+ }), /* @__PURE__ */ jsx("th", {
380
+ className: "srk--nowrap",
381
+ children: "Score"
382
+ }), showTimeColumn && /* @__PURE__ */ jsx("th", {
383
+ className: "srk--nowrap",
384
+ children: "Time"
385
+ }), problems.map((problem, index) => /* @__PURE__ */ jsx(ProblemHeaderCellComponent, {
386
+ problem,
387
+ index,
388
+ theme: this.props.theme
389
+ }, problem.alias || resolveText(problem.title)))]
390
+ })
391
+ }), /* @__PURE__ */ jsx("tbody", {
392
+ children: rows.map((r, index) => {
393
+ if (!r.rankValues) {
394
+ console.warn("Rank values is not provided, you may need to pass static ranklist data generated by `convertToStaticRanklist()`");
395
+ }
396
+ const rankValues = r.rankValues || series.map((s) => ({
397
+ rank: null,
398
+ segmentIndex: null
399
+ }));
400
+ return /* @__PURE__ */ jsxs("tr", {
401
+ children: [rankValues.map((rk, index2) => this.renderSingleSeriesBody(rk, series[index2], r)), /* @__PURE__ */ jsx(UserCellComponent, {
402
+ user: r.user,
403
+ row: r,
404
+ rowIndex: index,
405
+ ranklist: data,
406
+ markers: data.markers,
407
+ theme: this.props.theme,
408
+ formatSrkAssetUrl: this.formatSrkAssetUrl,
409
+ onUserClick: this.props.onUserClick
410
+ }), /* @__PURE__ */ jsx("td", {
411
+ className: "srk--text-right srk--nowrap",
412
+ children: r.score.value
413
+ }), showTimeColumn && /* @__PURE__ */ jsx("td", {
414
+ className: "srk--text-right srk--nowrap",
415
+ children: r.score.time ? formatTimeDuration(r.score.time, "min", Math.floor) : "-"
416
+ }), r.statuses.map((status, statusIndex) => /* @__PURE__ */ jsx(StatusCellComponent, {
417
+ status,
418
+ problem: problems[statusIndex],
419
+ problemIndex: statusIndex,
420
+ user: r.user,
421
+ row: r,
422
+ rowIndex: index,
423
+ ranklist: data,
424
+ onSolutionClick: this.props.onSolutionClick
425
+ }, (problems[statusIndex] || {}).alias || resolveText((problems[statusIndex] || {}).title) || statusIndex))]
426
+ }, r.user.id || resolveText(r.user.name));
427
+ })
428
+ })]
429
+ })
430
+ })
431
+ });
432
+ }
433
+ }
434
+ __publicField(Ranklist, "defaultProps", {
435
+ theme: EnumTheme.light,
436
+ borderedRows: false,
437
+ stripedRows: false
438
+ });
439
+ class ProgressBar extends React.Component {
440
+ constructor(props) {
441
+ super(props);
442
+ __publicField(this, "liveInterval");
443
+ __publicField(this, "handleProgressTimer", () => {
444
+ const now = Date.now();
445
+ this.setState({
446
+ localTime: now
447
+ });
448
+ if (this.isEnded) {
449
+ window.clearInterval(this.liveInterval);
450
+ }
451
+ });
452
+ __publicField(this, "handleTimeTravelChange", (value) => {
453
+ var _a, _b;
454
+ let exited = value >= this.durationMinutes || value >= this.maxAvailableMinutes;
455
+ (_b = (_a = this.props).onTimeTravel) == null ? void 0 : _b.call(_a, exited ? null : value * 60 * 1e3);
456
+ this.setState({
457
+ inTimeMachine: !exited,
458
+ timeTravelValue: exited ? null : value * 60 * 1e3,
459
+ timeTravelIsChanging: false
460
+ });
461
+ });
462
+ __publicField(this, "beginTimeTravel", () => {
463
+ this.setState({
464
+ timeTravelIsChanging: true,
465
+ inTimeMachine: true
466
+ });
467
+ });
468
+ __publicField(this, "handleSliderChange", (event) => {
469
+ this.setState({
470
+ timeTravelCurrentValue: Number(event.target.value)
471
+ });
472
+ });
473
+ __publicField(this, "commitTimeTravel", () => {
474
+ this.handleTimeTravelChange(this.state.timeTravelCurrentValue);
475
+ });
476
+ __publicField(this, "handleSliderKeyDown", (event) => {
477
+ if (["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End", "PageUp", "PageDown"].includes(event.key)) {
478
+ this.beginTimeTravel();
479
+ }
480
+ });
481
+ __publicField(this, "handleSliderKeyUp", (event) => {
482
+ if (["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End", "PageUp", "PageDown"].includes(event.key)) {
483
+ this.commitTimeTravel();
484
+ }
485
+ });
486
+ const localTime = Date.now();
487
+ this.state = {
488
+ localTime,
489
+ inTimeMachine: false,
490
+ timeTravelIsChanging: false,
491
+ timeTravelCurrentValue: ProgressBar.getMaxAvailableMinutes(props.data.contest, localTime, props.td),
492
+ timeTravelValue: null,
493
+ isMounted: false
494
+ };
495
+ }
496
+ static getDurationMinutes(contest) {
497
+ return getProgressDurationMinutes(contest);
498
+ }
499
+ static getMaxAvailableMinutes(contest, localTime, td) {
500
+ return getProgressMaxAvailableMinutes(contest, localTime, td);
501
+ }
502
+ get durationMinutes() {
503
+ return ProgressBar.getDurationMinutes(this.props.data.contest);
504
+ }
505
+ get maxAvailableMinutes() {
506
+ return ProgressBar.getMaxAvailableMinutes(this.props.data.contest, this.state.localTime, this.props.td);
507
+ }
508
+ get isEnded() {
509
+ return isProgressEnded(this.props.data.contest, this.state.localTime, this.props.td);
510
+ }
511
+ componentDidMount() {
512
+ if (this.props.live) {
513
+ this.liveInterval = window.setInterval(() => {
514
+ this.handleProgressTimer();
515
+ }, 1e3);
516
+ }
517
+ this.setState({
518
+ isMounted: true
519
+ });
520
+ }
521
+ componentDidUpdate(prevProps) {
522
+ var _a, _b, _c, _d, _e, _f;
523
+ if (!this.props.live && prevProps.live) {
524
+ if (this.liveInterval) {
525
+ window.clearInterval(this.liveInterval);
526
+ }
527
+ }
528
+ if (this.props.live && !prevProps.live) {
529
+ this.liveInterval = window.setInterval(() => {
530
+ this.handleProgressTimer();
531
+ }, 1e3);
532
+ }
533
+ if (!this.state.timeTravelIsChanging && this.state.timeTravelCurrentValue !== this.maxAvailableMinutes && this.state.timeTravelValue === null) {
534
+ this.setState({
535
+ timeTravelCurrentValue: this.maxAvailableMinutes
536
+ });
537
+ }
538
+ if (JSON.stringify((_b = (_a = this.props.data) == null ? void 0 : _a.contest) == null ? void 0 : _b.title) !== JSON.stringify((_d = (_c = prevProps.data) == null ? void 0 : _c.contest) == null ? void 0 : _d.title)) {
539
+ this.setState({
540
+ timeTravelIsChanging: false,
541
+ timeTravelCurrentValue: this.maxAvailableMinutes,
542
+ timeTravelValue: null,
543
+ inTimeMachine: false
544
+ });
545
+ (_f = (_e = this.props).onTimeTravel) == null ? void 0 : _f.call(_e, null);
546
+ }
547
+ }
548
+ componentWillUnmount() {
549
+ if (this.liveInterval) {
550
+ window.clearInterval(this.liveInterval);
551
+ }
552
+ }
553
+ render() {
554
+ const {
555
+ data,
556
+ enableTimeTravel = false,
557
+ live = false,
558
+ td = 0
559
+ } = this.props;
560
+ const inTimeMachine = this.state.inTimeMachine;
561
+ const progressMetrics = getProgressMetrics(data, this.state.localTime, td, this.state.timeTravelCurrentValue, inTimeMachine);
562
+ return /* @__PURE__ */ jsxs("div", {
563
+ className: "srk-progress-bar-container",
564
+ children: [/* @__PURE__ */ jsxs("div", {
565
+ className: "srk-progress-bar",
566
+ children: [/* @__PURE__ */ jsxs("div", {
567
+ className: "srk-progress-bar-body",
568
+ children: [/* @__PURE__ */ jsx("div", {
569
+ className: "srk-progress-bar-segment srk-progress-bar-normal",
570
+ style: {
571
+ width: `${progressMetrics.frozenBreakpoint * 100}%`
572
+ },
573
+ children: /* @__PURE__ */ jsx("div", {
574
+ className: "srk-progress-bar-fill",
575
+ style: {
576
+ width: `${progressMetrics.normalInnerPercent}%`
577
+ }
578
+ })
579
+ }), /* @__PURE__ */ jsx("div", {
580
+ className: "srk-progress-bar-segment srk-progress-bar-frozen",
581
+ style: {
582
+ width: `${(1 - progressMetrics.frozenBreakpoint) * 100}%`
583
+ },
584
+ children: /* @__PURE__ */ jsx("div", {
585
+ className: "srk-progress-bar-fill",
586
+ style: {
587
+ width: `${progressMetrics.frozenInnerPercent}%`
588
+ }
589
+ })
590
+ })]
591
+ }), enableTimeTravel && progressMetrics.supportRegen && this.state.isMounted && /* @__PURE__ */ jsxs("div", {
592
+ className: "srk-progress-slider-layer",
593
+ children: [this.state.timeTravelIsChanging && /* @__PURE__ */ jsx("div", {
594
+ className: "srk-progress-slider-tooltip",
595
+ style: {
596
+ left: `${this.durationMinutes ? this.state.timeTravelCurrentValue / this.durationMinutes * 100 : 0}%`
597
+ },
598
+ children: secToTimeStr(this.state.timeTravelCurrentValue * 60)
599
+ }), /* @__PURE__ */ jsx("input", {
600
+ "aria-label": "Time Travel",
601
+ className: "srk-progress-slider",
602
+ max: this.durationMinutes,
603
+ min: 0,
604
+ onBlur: () => {
605
+ if (this.state.timeTravelIsChanging) {
606
+ this.commitTimeTravel();
607
+ }
608
+ },
609
+ onChange: this.handleSliderChange,
610
+ onKeyDown: this.handleSliderKeyDown,
611
+ onKeyUp: this.handleSliderKeyUp,
612
+ onMouseDown: this.beginTimeTravel,
613
+ onMouseUp: this.commitTimeTravel,
614
+ onTouchEnd: this.commitTimeTravel,
615
+ onTouchStart: this.beginTimeTravel,
616
+ step: 1,
617
+ title: secToTimeStr(this.state.timeTravelCurrentValue * 60),
618
+ type: "range",
619
+ value: this.state.timeTravelCurrentValue
620
+ })]
621
+ })]
622
+ }), /* @__PURE__ */ jsxs("div", {
623
+ className: "srk-progress-secondary-area",
624
+ children: [/* @__PURE__ */ jsxs("div", {
625
+ className: "srk-progress-secondary-area-left",
626
+ style: live || inTimeMachine ? {} : {
627
+ display: "none"
628
+ },
629
+ children: ["Elapsed: ", secToTimeStr(Math.round(progressMetrics.elapsed / 1e3))]
630
+ }), /* @__PURE__ */ jsx("div", {
631
+ className: "srk-progress-secondary-area-center",
632
+ children: this.state.inTimeMachine ? /* @__PURE__ */ jsx("div", {
633
+ className: "srk-progress-time-machine-status",
634
+ children: /* @__PURE__ */ jsx("div", {
635
+ className: "srk-progress-time-machine-text",
636
+ children: "Time Travel Mode"
637
+ })
638
+ }) : live && !this.isEnded ? /* @__PURE__ */ jsx("div", {
639
+ className: "srk-progress-live-text",
640
+ children: "Live"
641
+ }) : /* @__PURE__ */ jsx("div", {
642
+ style: {
643
+ visibility: "hidden"
644
+ },
645
+ children: "SRK"
646
+ })
647
+ }), /* @__PURE__ */ jsxs("div", {
648
+ className: "srk-progress-secondary-area-right",
649
+ style: live || inTimeMachine ? {} : {
650
+ display: "none"
651
+ },
652
+ children: ["Remaining: ", secToTimeStr(Math.round(progressMetrics.remaining / 1e3))]
653
+ })]
654
+ })]
655
+ });
656
+ }
657
+ }
658
+ function MarkerLabel(props) {
659
+ if (!props.marker) {
660
+ return null;
661
+ }
662
+ const {
663
+ marker,
664
+ theme
665
+ } = props;
666
+ const markerPresentation = getMarkerPresentation(marker, theme);
667
+ return /* @__PURE__ */ jsx("span", {
668
+ className: classnames(markerPresentation.className, props.className),
669
+ style: {
670
+ ...markerPresentation.style,
671
+ ...props.style
672
+ },
673
+ children: resolveText(marker.label)
674
+ });
675
+ }
676
+ let nextModalId = 0;
677
+ const useModalLayoutEffect = typeof window === "undefined" ? React.useEffect : React.useLayoutEffect;
678
+ function Modal({
679
+ open,
680
+ title,
681
+ children,
682
+ width,
683
+ rootClassName,
684
+ wrapClassName,
685
+ style,
686
+ destroyOnClose = true,
687
+ closeOnEsc = true,
688
+ closeOnMaskClick = true,
689
+ onClose
690
+ }) {
691
+ const titleIdRef = React.useRef(`srk-modal-title-${++nextModalId}`);
692
+ const dialogRef = React.useRef(null);
693
+ const closeTimerRef = React.useRef(null);
694
+ const openTimerRef = React.useRef(null);
695
+ const focusScopeIdRef = React.useRef(null);
696
+ const [isMounted, setIsMounted] = React.useState(open || !destroyOnClose);
697
+ const [animationState, setAnimationState] = React.useState(open ? "pre-open" : "closing");
698
+ const [transformOrigin, setTransformOrigin] = React.useState({
699
+ x: 0,
700
+ y: 0
701
+ });
702
+ React.useEffect(() => {
703
+ ensureModalInteractionTracker();
704
+ }, []);
705
+ React.useEffect(() => {
706
+ return () => {
707
+ if (closeTimerRef.current !== null) {
708
+ window.clearTimeout(closeTimerRef.current);
709
+ }
710
+ if (openTimerRef.current !== null) {
711
+ window.clearTimeout(openTimerRef.current);
712
+ }
713
+ unregisterModalFocusScope(focusScopeIdRef.current);
714
+ focusScopeIdRef.current = null;
715
+ };
716
+ }, []);
717
+ React.useEffect(() => {
718
+ if (closeTimerRef.current !== null) {
719
+ window.clearTimeout(closeTimerRef.current);
720
+ closeTimerRef.current = null;
721
+ }
722
+ if (openTimerRef.current !== null) {
723
+ window.clearTimeout(openTimerRef.current);
724
+ openTimerRef.current = null;
725
+ }
726
+ if (open) {
727
+ setIsMounted(true);
728
+ setTransformOrigin({
729
+ x: 0,
730
+ y: 0
731
+ });
732
+ setAnimationState("pre-open");
733
+ return;
734
+ }
735
+ setAnimationState("closing");
736
+ if (destroyOnClose && isMounted && typeof window !== "undefined") {
737
+ closeTimerRef.current = window.setTimeout(() => {
738
+ setIsMounted(false);
739
+ }, MODAL_ANIMATION_DURATION_MS);
740
+ }
741
+ }, [destroyOnClose, isMounted, open]);
742
+ React.useEffect(() => {
743
+ if (!open || !isMounted || animationState !== "pre-open") {
744
+ return;
745
+ }
746
+ const resolution = resolveModalTransformOrigin(dialogRef.current);
747
+ setTransformOrigin(resolution.origin);
748
+ openTimerRef.current = window.setTimeout(() => {
749
+ setAnimationState("opening");
750
+ openTimerRef.current = null;
751
+ }, 0);
752
+ return () => {
753
+ if (openTimerRef.current !== null) {
754
+ window.clearTimeout(openTimerRef.current);
755
+ openTimerRef.current = null;
756
+ }
757
+ };
758
+ }, [animationState, isMounted, open]);
759
+ const shouldLockBody = open || destroyOnClose && isMounted;
760
+ React.useEffect(() => {
761
+ if (!shouldLockBody) {
762
+ return;
763
+ }
764
+ lockModalBodyScroll();
765
+ return () => {
766
+ unlockModalBodyScroll();
767
+ };
768
+ }, [shouldLockBody]);
769
+ const handleEscape = React.useCallback((event) => {
770
+ if (closeOnEsc) {
771
+ onClose == null ? void 0 : onClose(event, "escape");
772
+ }
773
+ }, [closeOnEsc, onClose]);
774
+ useModalLayoutEffect(() => {
775
+ if (open && isMounted) {
776
+ focusScopeIdRef.current = registerModalFocusScope(dialogRef.current, {
777
+ onEscape: closeOnEsc ? handleEscape : void 0
778
+ });
779
+ return;
780
+ }
781
+ if (!open && focusScopeIdRef.current !== null) {
782
+ unregisterModalFocusScope(focusScopeIdRef.current);
783
+ focusScopeIdRef.current = null;
784
+ }
785
+ }, [closeOnEsc, handleEscape, isMounted, open]);
786
+ if (!isMounted && destroyOnClose) {
787
+ return null;
788
+ }
789
+ const dialogStyle = {
790
+ ...style,
791
+ width: width ? `${width}px` : style == null ? void 0 : style.width,
792
+ ["--srk-modal-origin-x"]: `${transformOrigin.x}px`,
793
+ ["--srk-modal-origin-y"]: `${transformOrigin.y}px`,
794
+ ["--srk-modal-max-width"]: width ? `${width}px` : typeof (style == null ? void 0 : style.width) === "string" ? style.width : void 0
795
+ };
796
+ const modalNode = /* @__PURE__ */ jsxs("div", {
797
+ className: classnames("srk-modal-root", SRK_ANIMATED_MODAL_ROOT_CLASS, "srk-react-modal-root", rootClassName),
798
+ "data-srk-modal-state": animationState,
799
+ children: [/* @__PURE__ */ jsx("div", {
800
+ className: "srk-modal-mask"
801
+ }), /* @__PURE__ */ jsx("div", {
802
+ className: classnames("srk-modal-wrap", wrapClassName),
803
+ tabIndex: -1,
804
+ onMouseDown: (event) => {
805
+ if (closeOnMaskClick && event.target === event.currentTarget) {
806
+ onClose == null ? void 0 : onClose(event, "mask");
807
+ }
808
+ },
809
+ children: /* @__PURE__ */ jsx("div", {
810
+ "aria-labelledby": title ? titleIdRef.current : void 0,
811
+ "aria-modal": "true",
812
+ className: "srk-modal",
813
+ "data-srk-modal-panel": "true",
814
+ ref: dialogRef,
815
+ role: "dialog",
816
+ style: dialogStyle,
817
+ tabIndex: -1,
818
+ children: /* @__PURE__ */ jsxs("div", {
819
+ className: "srk-modal-content",
820
+ children: [/* @__PURE__ */ jsx("button", {
821
+ "aria-label": "Close",
822
+ className: "srk-modal-close",
823
+ type: "button",
824
+ onClick: (event) => onClose == null ? void 0 : onClose(event, "close-button"),
825
+ children: /* @__PURE__ */ jsx("span", {
826
+ className: "srk-modal-close-x"
827
+ })
828
+ }), title !== void 0 && /* @__PURE__ */ jsx("div", {
829
+ className: "srk-modal-header",
830
+ children: /* @__PURE__ */ jsx("div", {
831
+ className: "srk-modal-title",
832
+ id: titleIdRef.current,
833
+ children: title
834
+ })
835
+ }), /* @__PURE__ */ jsx("div", {
836
+ className: "srk-modal-body",
837
+ children
838
+ })]
839
+ })
840
+ })
841
+ })]
842
+ });
843
+ if (typeof document === "undefined") {
844
+ return modalNode;
845
+ }
846
+ return ReactDOM.createPortal(modalNode, document.body);
847
+ }
848
+ function UserModalContent({
849
+ user,
850
+ userMarkers,
851
+ theme,
852
+ formatSrkAssetUrl
853
+ }) {
854
+ const hasMembers = !!user.teamMembers && user.teamMembers.length > 0;
855
+ return /* @__PURE__ */ jsxs("div", {
856
+ className: "srk-user-modal-info",
857
+ children: [/* @__PURE__ */ jsx("h3", {
858
+ className: "srk-user-modal-info-user-name",
859
+ children: resolveText(user.name)
860
+ }), !!user.organization && /* @__PURE__ */ jsx("p", {
861
+ className: "srk-user-modal-info-user-second-name",
862
+ children: resolveText(user.organization)
863
+ }), /* @__PURE__ */ jsxs("div", {
864
+ className: "srk-user-modal-info-labels",
865
+ children: [/* @__PURE__ */ jsx("span", {
866
+ className: "srk-user-modal-info-labels-label srk-user-modal-info-labels-label-preset-general",
867
+ children: user.official === false ? "\uFF0A \u975E\u6B63\u5F0F\u53C2\u52A0\u8005" : "\u6B63\u5F0F\u53C2\u52A0\u8005"
868
+ }), userMarkers.map((marker, index) => /* @__PURE__ */ jsx(MarkerLabel, {
869
+ marker,
870
+ theme,
871
+ className: "srk-user-modal-info-labels-label"
872
+ }, index))]
873
+ }), hasMembers && /* @__PURE__ */ jsx("div", {
874
+ className: "srk-user-modal-info-team-members",
875
+ children: user.teamMembers.map((member, index) => /* @__PURE__ */ jsxs("span", {
876
+ children: [index > 0 && /* @__PURE__ */ jsx("span", {
877
+ className: "srk-user-modal-info-team-members-slash",
878
+ children: " / "
879
+ }), /* @__PURE__ */ jsx("span", {
880
+ children: resolveText(member.name)
881
+ })]
882
+ }, resolveText(member.name)))
883
+ }), user.photo && /* @__PURE__ */ jsx("div", {
884
+ className: "srk-user-modal-info-photo",
885
+ children: /* @__PURE__ */ jsx("img", {
886
+ src: formatSrkAssetUrl(user.photo, "user.photo"),
887
+ alt: "User portrait",
888
+ className: "srk-user-modal-info-photo-img"
889
+ })
890
+ })]
891
+ });
892
+ }
893
+ function DefaultUserModal({
894
+ open,
895
+ user,
896
+ markers = [],
897
+ theme = EnumTheme.light,
898
+ title = "User Info",
899
+ width = 420,
900
+ formatSrkAssetUrl,
901
+ onClose,
902
+ rootClassName = "srk-general-modal-root",
903
+ wrapClassName = "srk-user-modal",
904
+ style
905
+ }) {
906
+ const [cachedUser, setCachedUser] = React.useState(user || null);
907
+ React.useEffect(() => {
908
+ if (user) {
909
+ setCachedUser(user);
910
+ }
911
+ }, [user]);
912
+ if (!cachedUser) {
913
+ return null;
914
+ }
915
+ return /* @__PURE__ */ jsx(Modal, {
916
+ open,
917
+ onClose,
918
+ rootClassName,
919
+ style,
920
+ title,
921
+ width,
922
+ wrapClassName,
923
+ children: /* @__PURE__ */ jsx(UserModalContent, {
924
+ user: cachedUser,
925
+ userMarkers: resolveUserMarkers(cachedUser, markers),
926
+ theme,
927
+ formatSrkAssetUrl: (url, field) => resolveSrkAssetUrl(url, field, formatSrkAssetUrl)
928
+ })
929
+ });
930
+ }
931
+ function SolutionResultLabel({
932
+ result
933
+ }) {
934
+ const resultMeta = getSolutionResultMeta(result);
935
+ return /* @__PURE__ */ jsx("span", {
936
+ className: classnames("srk-solution-result-text", resultMeta.className),
937
+ children: resultMeta.label
938
+ });
939
+ }
940
+ function SolutionTable({
941
+ solutions
942
+ }) {
943
+ return /* @__PURE__ */ jsxs("table", {
944
+ className: "srk-common-table srk-solutions-table",
945
+ children: [/* @__PURE__ */ jsx("thead", {
946
+ children: /* @__PURE__ */ jsxs("tr", {
947
+ children: [/* @__PURE__ */ jsx("th", {
948
+ className: "srk--text-left",
949
+ children: "Result"
950
+ }), /* @__PURE__ */ jsx("th", {
951
+ className: "srk--text-right",
952
+ children: "Time"
953
+ })]
954
+ })
955
+ }), /* @__PURE__ */ jsx("tbody", {
956
+ children: solutions.map((solution, index) => /* @__PURE__ */ jsxs("tr", {
957
+ children: [/* @__PURE__ */ jsx("td", {
958
+ children: /* @__PURE__ */ jsx(SolutionResultLabel, {
959
+ result: solution.result
960
+ })
961
+ }), /* @__PURE__ */ jsx("td", {
962
+ className: "srk--text-right",
963
+ children: formatSolutionTimestamp(solution)
964
+ })]
965
+ }, `${solution.result}_${solution.time[0]}_${index}`))
966
+ })]
967
+ });
968
+ }
969
+ function DefaultSolutionModal({
970
+ open,
971
+ user,
972
+ problem,
973
+ problemIndex,
974
+ solutions,
975
+ title,
976
+ width = 320,
977
+ onClose,
978
+ rootClassName = "srk-general-modal-root",
979
+ wrapClassName = "srk-solutions-modal",
980
+ style
981
+ }) {
982
+ const [cachedPayload, setCachedPayload] = React.useState(user ? {
983
+ user,
984
+ problem,
985
+ problemIndex,
986
+ solutions
987
+ } : null);
988
+ React.useEffect(() => {
989
+ if (user) {
990
+ setCachedPayload({
991
+ user,
992
+ problem,
993
+ problemIndex,
994
+ solutions
995
+ });
996
+ }
997
+ }, [problem, problemIndex, solutions, user]);
998
+ if (!cachedPayload) {
999
+ return null;
1000
+ }
1001
+ return /* @__PURE__ */ jsx(Modal, {
1002
+ open,
1003
+ onClose,
1004
+ rootClassName,
1005
+ style,
1006
+ title: title || getSolutionModalTitle(cachedPayload.problemIndex, cachedPayload.user),
1007
+ width,
1008
+ wrapClassName,
1009
+ children: /* @__PURE__ */ jsx(SolutionTable, {
1010
+ solutions: cachedPayload.solutions
1011
+ })
1012
+ });
1013
+ }
1014
+ export { DefaultSolutionModal, DefaultUserModal, MarkerLabel, Modal, ProgressBar, Ranklist };