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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ export declare const srkSupportedVersions: any;
2
+ export declare function caniuse(version: string): boolean;
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});var le=require("color"),c=require("@algoux/standard-ranklist-utils"),ce=require("semver");function z(e){return e&&typeof e=="object"&&"default"in e?e:{default:e}}var B=z(le),se=z(ce);const U={[c.EnumTheme.light]:"#ffffff",[c.EnumTheme.dark]:"#191919"},ae=["FB","AC","?","NOUT","CE","UKE",null];function ue(e,t,r){if(typeof r=="function")return r(e,t);if(e.startsWith("//"))return e;const o=e.match(/^([a-zA-Z][a-zA-Z0-9+.-]*):/);if(o){const n=o[1].toLowerCase();return n==="http"||n==="https"||n==="data"?e:(console.warn(`unsupported protocol "${n}" in srk asset url:`,e),"")}return console.warn("unsupported srk asset url:",e),""}function de(e,t,r=180){const{backgroundColor:o}=c.resolveStyle(e||{}),n=o[t]||U[t],i=B.default(n).string(),l=B.default(n).alpha(.27).string();return`linear-gradient(${r}deg, ${i} 0%, ${i} 10%, ${l} 10%, transparent 100%)`}function fe(e,t){return typeof e.style=="string"?{className:`srk-preset-marker-${e.style}`}:e.style?{style:{backgroundColor:c.resolveStyle(e.style).backgroundColor[t]}}:{}}function me(e){switch(e){case"FB":return{label:"First Blood",className:"srk-preset-result-fb"};case"AC":return{label:"Accepted",className:"srk-preset-result-ac"};case"RJ":return{label:"Rejected",className:"srk-preset-result-rj"};case"?":return{label:"Frozen",className:"srk-preset-result-fz"};case"WA":return{label:"Wrong Answer",className:"srk-preset-result-rj"};case"PE":return{label:"Presentation Error",className:"srk-preset-result-rj"};case"TLE":return{label:"Time Limit Exceeded",className:"srk-preset-result-rj"};case"MLE":return{label:"Memory Limit Exceeded",className:"srk-preset-result-rj"};case"OLE":return{label:"Output Limit Exceeded",className:"srk-preset-result-rj"};case"RTE":return{label:"Runtime Error",className:"srk-preset-result-rj"};case"NOUT":return{label:"No Output"};case"CE":return{label:"Compile Error"};case"UKE":return{label:"Unknown Error"};case null:return{label:"--"};default:return{label:e}}}function X(e){return`${e.tries}/${e.time?c.formatTimeDuration(e.time,"min",Math.floor):"-"}`}function g(e){return(e==null?void 0:e.result)==="AC"||(e==null?void 0:e.result)==="FB"}function K(e){return(e==null?void 0:e.result)==="RJ"||(e==null?void 0:e.result)==="?"}function v(e){return(e==null?void 0:e.tries)||0}function H(e,t){const r=ke(t),o=e.solutions||[];for(let n=o.length-1;n>=0;n-=1){const i=o[n];if(!r.includes(i.result))return i}}function f(e,t){if(!e)return"-";const r=we(e,t);if(r==="ms"){const n=Math.floor(c.formatTimeDuration(e,"ms")),i=Math.floor(n/1e3);return`${F(i)}.${b(n%1e3,3)}`}if(r==="s")return F(Math.floor(c.formatTimeDuration(e,"s")));const o=Math.floor(c.formatTimeDuration(e,"min"));return`${Math.floor(o/60)}:${b(o%60,2)}`}function ge(e,t,r="classic"){if(!e.result)return{};if(r==="classic"){if(g(e)){const o=X(e);return typeof e.score=="number"?{score:e.score,scoreDetails:o}:{primary:o}}return{primary:typeof e.tries=="number"?String(e.tries):""}}if(g(e)){const o=Math.max((e.tries||0)-1,0);if(r==="detailed")return{primary:f(e.time,t),secondary:o>0?`(-${o})`:""};const n=o>0?`+${o}`:"+";return r==="compact"?{primary:n,secondary:f(e.time,t)}:{primary:n}}if(K(e)){const o=e.tries||0,n=`-${o}`;if(r==="detailed")return{primary:"",secondary:o>0?`(-${o})`:""};if(r==="compact"&&o>0){const i=H(e,t);if(i)return{primary:n,secondary:f(i.time,t)}}return{primary:n}}return{}}function pe(e){const t=e.statuses.reduce((r,o)=>{if(!g(o))return r;const n=v(o);return{wrong:r.wrong+Math.max(n-1,0),submitted:r.submitted+n}},{wrong:0,submitted:0});return!t.submitted||!t.wrong?"0%":`${Math.floor(t.wrong/t.submitted*100)}%`}function ye(e){const t=e.rows.length;return e.problems.map((r,o)=>{const n={participantCount:t,accepted:0,tried:0,submitted:0,dirt:0,dirtSubmitted:0};return e.rows.forEach(i=>{const l=i.statuses[o],u=v(l);u>0&&(n.tried+=1,n.submitted+=u),g(l)&&(n.accepted+=1,n.dirt+=Math.max(u-1,0),n.dirtSubmitted+=u,l.time&&((!n.firstAcceptedTime||N(l.time,n.firstAcceptedTime)<0)&&(n.firstAcceptedTime=l.time),(!n.lastAcceptedTime||N(l.time,n.lastAcceptedTime)>0)&&(n.lastAcceptedTime=l.time)))}),n})}function be(e,t){return!e.firstAcceptedTime||!e.lastAcceptedTime?"-":`${f(e.firstAcceptedTime,t)} / ${f(e.lastAcceptedTime,t)}`}function he(e){return e?String(Math.floor(c.formatTimeDuration(e,"min"))):"-"}function Te(e,t){return t<=0?"(-)":`(${Math.floor(e/t*100)}%)`}function Me(e){const t=V(e);return t===void 0?"-":W(t)}function Se(e,t){const r=e.statuses.reduce((o,n,i)=>{if(!g(n))return o;const l=V(t[i]);return l!==void 0&&o.push(l),o},[]);return r.length?W(r.reduce((o,n)=>o+n,0)/r.length):"0.00"}function Pe(e,t){return`Solutions of ${c.numberToAlphabet(e)} (${c.resolveText(t.name)})`}function Ae(e){return c.secToTimeStr(c.formatTimeDuration(e.time,"s"))}function ve(e){return e.some(t=>{var r;return Boolean((r=t.score)==null?void 0:r.time)})}function we(e,t){const r=t.sorter;return(r==null?void 0:r.algorithm)==="ICPC"&&r.config.timePrecision?r.config.timePrecision:e[1]}function ke(e){const t=e.sorter;return(t==null?void 0:t.algorithm)==="ICPC"&&t.config.noPenaltyResults?t.config.noPenaltyResults:ae}function V(e){if(!(!e||e.participantCount<=0))return(e.participantCount-e.accepted)/e.participantCount}function W(e){return e.toFixed(2)}function N(e,t){return c.formatTimeDuration(e,"ms")-c.formatTimeDuration(t,"ms")}function F(e){const t=Math.floor(e/3600),r=Math.floor(e%3600/60),o=e%60;return`${t}:${b(r,2)}:${b(o,2)}`}function b(e,t){const r=String(Math.trunc(Math.abs(e)));return r.length>=t?r:`${"0".repeat(t-r.length)}${r}`}function J(e){return c.formatTimeDuration(e.duration,"min")}function Re(e,t,r=0){const o=new Date(e.startAt).getTime(),n=t-r;return Math.min(Math.max(Math.floor((n-o)/6e4),0),J(e))}function xe(e,t,r=0){const n=new Date(e.startAt).getTime()+c.formatTimeDuration(e.duration,"ms");return t-r>=n}function Ee(e){const t={};for(let r=0;r<=e;++r)t[r]="";return t}function Ce(e,t,r,o,n){const{contest:i}=e,l=new Date(i.startAt).getTime(),u=l+c.formatTimeDuration(i.duration,"ms"),I=i.frozenDuration?c.formatTimeDuration(i.frozenDuration,"ms"):0,O=t-r,d=u-l,p=d?1-I/d:1,S=Math.min(Math.max(O-l,0),d),ee=d-S,te=u-I,re=d?S/d*100:0,_=d?Math.max(Math.min(O,te)-l,0)/d*100:0,oe=Math.min(100,p?_/p:0),ne=re-_,ie=Math.min(100,1-p?ne/(1-p):0),P=o*60*1e3,$=d-P;return{elapsed:n?P:S,remaining:n?$:ee,frozenBreakpoint:p,normalInnerPercent:oe,frozenInnerPercent:ie,timeTravelElapsed:P,timeTravelRemaining:$,supportRegen:c.canRegenerateRanklist(e)}}const q=">=0.3.0 <=0.3.12";function De(e){return se.default.satisfies(e,q)}const Le=280,Ie="srk-animated-modal-root",M="srk-scrolling-effect";let y=null,a=null,j=!1,m=0,w="",k="",R="",x="",E="",C="",D="",L="",h=0,T=0,A=1,s=[],Y=!1;function Oe(e){var r,o;if(e.key!=="Escape")return;const t=s[s.length-1];!t||((r=t.onEscape)==null||r.call(t,e),(o=e.stopImmediatePropagation)==null||o.call(e),e.stopPropagation())}function _e(){typeof document=="undefined"||Y||(document.addEventListener("keydown",Oe),Y=!0)}function $e(){typeof document=="undefined"||j||(document.addEventListener("mousedown",e=>{a={x:e.clientX,y:e.clientY}},!0),document.addEventListener("touchstart",e=>{const t=e.touches[0]||e.changedTouches[0];!t||(a={x:t.clientX,y:t.clientY})},{capture:!0,passive:!0}),j=!0)}function Z(e,t,r){y={x:e,y:t,timestamp:Date.now(),source:r==null?void 0:r.source,context:r==null?void 0:r.context}}function Be(e,t){Z(e.clientX,e.clientY,t)}function G(e=500){return!y||Date.now()-y.timestamp>e?null:y}function Ne(){return a}function Fe(e){if(typeof window=="undefined"||!e)return{origin:{x:0,y:0},source:"fallback",explicitTriggerPoint:null,globalInteractionPoint:a,dialogRect:null,fallbackPoint:{x:0,y:0}};const t=e.getBoundingClientRect(),r={x:t.width/2,y:Math.min(t.height*.22,96)},o=G();return o?{origin:{x:o.x-t.left,y:o.y-t.top},source:"explicit-trigger",explicitTriggerPoint:o,globalInteractionPoint:a,dialogRect:{left:t.left,top:t.top,width:t.width,height:t.height},fallbackPoint:r}:a?{origin:{x:a.x-t.left,y:a.y-t.top},source:"global-pointer",explicitTriggerPoint:null,globalInteractionPoint:a,dialogRect:{left:t.left,top:t.top,width:t.width,height:t.height},fallbackPoint:r}:{origin:r,source:"fallback",explicitTriggerPoint:null,globalInteractionPoint:null,dialogRect:{left:t.left,top:t.top,width:t.width,height:t.height},fallbackPoint:r}}function je(){return typeof document=="undefined"?null:document.activeElement instanceof HTMLElement?document.activeElement:null}function Q(e){try{e.focus({preventScroll:!0})}catch{e.focus()}}function Ye(e,t={}){if(typeof document=="undefined"||!e)return null;_e();const r=s.find(l=>l.dialogElement===e);if(r)return r.onEscape=t.onEscape,r.id;const o=e.hasAttribute("tabindex"),n=e.getAttribute("tabindex");o||e.setAttribute("tabindex","-1");const i={id:A,dialogElement:e,restoreElement:je(),hadTabIndex:o,previousTabIndex:n,onEscape:t.onEscape};return A+=1,s.push(i),Q(e),i.id}function ze(e){var l;if(typeof document=="undefined"||e==null)return;const t=s.findIndex(u=>u.id===e);if(t===-1)return;const[r]=s.splice(t,1);if(r.hadTabIndex?r.previousTabIndex===null?r.dialogElement.removeAttribute("tabindex"):r.dialogElement.setAttribute("tabindex",r.previousTabIndex):r.dialogElement.removeAttribute("tabindex"),!(t===s.length))return;const n=s[s.length-1],i=(l=r.restoreElement)!=null&&l.isConnected?r.restoreElement:n==null?void 0:n.dialogElement;i&&Q(i)}function Ue(e){var t;return e==null?!1:((t=s[s.length-1])==null?void 0:t.id)===e}function Xe(){typeof document!="undefined"&&(m===0&&(h=window.scrollX,T=window.scrollY,w=document.body.style.overflow,k=document.body.style.overflowX,R=document.body.style.overflowY,x=document.body.style.position,E=document.body.style.top,C=document.body.style.left,D=document.body.style.right,L=document.body.style.width,document.body.classList.add(M),document.body.style.overflow="hidden",document.body.style.overflowX="hidden",document.body.style.overflowY="hidden",document.body.style.position="fixed",document.body.style.top=`-${T}px`,document.body.style.left=`-${h}px`,document.body.style.right="0",document.body.style.width="100%"),m+=1)}function Ke(){if(!(typeof document=="undefined"||m===0)&&(m-=1,m===0&&(document.body.classList.remove(M),document.body.style.overflow=w,document.body.style.overflowX=k,document.body.style.overflowY=R,document.body.style.position=x,document.body.style.top=E,document.body.style.left=C,document.body.style.right=D,document.body.style.width=L,!(typeof navigator!="undefined"&&/jsdom/i.test(navigator.userAgent))&&typeof window.scrollTo=="function")))try{window.scrollTo(h,T)}catch{}}function He(){y=null,a=null,m=0,A=1,s=[],w="",k="",R="",x="",E="",C="",D="",L="",h=0,T=0,typeof document!="undefined"&&(document.body.classList.remove(M),document.body.style.overflow="",document.body.style.overflowX="",document.body.style.overflowY="",document.body.style.position="",document.body.style.top="",document.body.style.left="",document.body.style.right="",document.body.style.width="")}Object.defineProperty(exports,"convertToStaticRanklist",{enumerable:!0,get:function(){return c.convertToStaticRanklist}});exports.MODAL_ANIMATION_DURATION_MS=Le;exports.SRK_ANIMATED_MODAL_ROOT_CLASS=Ie;exports.SRK_SCROLL_LOCK_BODY_CLASS=M;exports.buildSliderMarks=Ee;exports.calculateDirtPercentage=pe;exports.calculateProblemStatisticsFooter=ye;exports.calculateSEValue=Se;exports.caniuse=De;exports.captureModalTriggerPointFromMouseEvent=Be;exports.defaultBackgroundColor=U;exports.ensureModalInteractionTracker=$e;exports.formatProblemStatisticsAcceptedMinute=he;exports.formatProblemStatisticsAcceptedRange=be;exports.formatProblemStatisticsAverageHardness=Me;exports.formatProblemStatisticsPercent=Te;exports.formatRanklistStatusTime=f;exports.formatSolutionTimestamp=Ae;exports.getAcceptedStatusDetails=X;exports.getLastModalInteractionPoint=Ne;exports.getLastPenaltySolution=H;exports.getMarkerPresentation=fe;exports.getProblemHeaderBackgroundImage=de;exports.getProgressDurationMinutes=J;exports.getProgressMaxAvailableMinutes=Re;exports.getProgressMetrics=Ce;exports.getRankProblemStatusCellPresentation=ge;exports.getRankProblemStatusTries=v;exports.getRecentModalTriggerPoint=G;exports.getSolutionModalTitle=Pe;exports.getSolutionResultMeta=me;exports.isAcceptedRankProblemStatus=g;exports.isProgressEnded=xe;exports.isRejectedRankProblemStatus=K;exports.isTopModalFocusScope=Ue;exports.lockModalBodyScroll=Xe;exports.registerModalFocusScope=Ye;exports.resetModalInteractionStateForTests=He;exports.resolveModalTransformOrigin=Fe;exports.resolveSrkAssetUrl=ue;exports.setModalTriggerPoint=Z;exports.shouldShowTimeColumn=ve;exports.srkSupportedVersions=q;exports.unlockModalBodyScroll=Ke;exports.unregisterModalFocusScope=ze;
@@ -0,0 +1,6 @@
1
+ export * from './types';
2
+ export * from './ranklist';
3
+ export * from './progress';
4
+ export * from './caniuse';
5
+ export * from './modal';
6
+ export { convertToStaticRanklist } from '@algoux/standard-ranklist-utils';
package/dist/index.js ADDED
@@ -0,0 +1,679 @@
1
+ import Color from "color";
2
+ import { EnumTheme, resolveStyle, formatTimeDuration, numberToAlphabet, resolveText, secToTimeStr, canRegenerateRanklist } from "@algoux/standard-ranklist-utils";
3
+ export { convertToStaticRanklist } from "@algoux/standard-ranklist-utils";
4
+ import semver from "semver";
5
+ const defaultBackgroundColor = {
6
+ [EnumTheme.light]: "#ffffff",
7
+ [EnumTheme.dark]: "#191919"
8
+ };
9
+ const defaultNoPenaltyResults = ["FB", "AC", "?", "NOUT", "CE", "UKE", null];
10
+ function resolveSrkAssetUrl(url, field, formatter) {
11
+ if (typeof formatter === "function") {
12
+ return formatter(url, field);
13
+ }
14
+ if (url.startsWith("//")) {
15
+ return url;
16
+ }
17
+ const protocolMatch = url.match(/^([a-zA-Z][a-zA-Z0-9+.-]*):/);
18
+ if (protocolMatch) {
19
+ const protocol = protocolMatch[1].toLowerCase();
20
+ if (protocol === "http" || protocol === "https" || protocol === "data") {
21
+ return url;
22
+ }
23
+ console.warn(`unsupported protocol "${protocol}" in srk asset url:`, url);
24
+ return "";
25
+ }
26
+ console.warn("unsupported srk asset url:", url);
27
+ return "";
28
+ }
29
+ function getProblemHeaderBackgroundImage(style, theme, gradientDirection = 180) {
30
+ const { backgroundColor } = resolveStyle(style || {});
31
+ const resolvedColor = backgroundColor[theme] || defaultBackgroundColor[theme];
32
+ const bgColorStr = Color(resolvedColor).string();
33
+ const bgColorAlphaStr = Color(resolvedColor).alpha(0.27).string();
34
+ return `linear-gradient(${gradientDirection}deg, ${bgColorStr} 0%, ${bgColorStr} 10%, ${bgColorAlphaStr} 10%, transparent 100%)`;
35
+ }
36
+ function getMarkerPresentation(marker, theme) {
37
+ if (typeof marker.style === "string") {
38
+ return { className: `srk-preset-marker-${marker.style}` };
39
+ }
40
+ if (marker.style) {
41
+ return {
42
+ style: {
43
+ backgroundColor: resolveStyle(marker.style).backgroundColor[theme]
44
+ }
45
+ };
46
+ }
47
+ return {};
48
+ }
49
+ function getSolutionResultMeta(result) {
50
+ switch (result) {
51
+ case "FB":
52
+ return { label: "First Blood", className: "srk-preset-result-fb" };
53
+ case "AC":
54
+ return { label: "Accepted", className: "srk-preset-result-ac" };
55
+ case "RJ":
56
+ return { label: "Rejected", className: "srk-preset-result-rj" };
57
+ case "?":
58
+ return { label: "Frozen", className: "srk-preset-result-fz" };
59
+ case "WA":
60
+ return { label: "Wrong Answer", className: "srk-preset-result-rj" };
61
+ case "PE":
62
+ return { label: "Presentation Error", className: "srk-preset-result-rj" };
63
+ case "TLE":
64
+ return { label: "Time Limit Exceeded", className: "srk-preset-result-rj" };
65
+ case "MLE":
66
+ return { label: "Memory Limit Exceeded", className: "srk-preset-result-rj" };
67
+ case "OLE":
68
+ return { label: "Output Limit Exceeded", className: "srk-preset-result-rj" };
69
+ case "RTE":
70
+ return { label: "Runtime Error", className: "srk-preset-result-rj" };
71
+ case "NOUT":
72
+ return { label: "No Output" };
73
+ case "CE":
74
+ return { label: "Compile Error" };
75
+ case "UKE":
76
+ return { label: "Unknown Error" };
77
+ case null:
78
+ return { label: "--" };
79
+ default:
80
+ return { label: result };
81
+ }
82
+ }
83
+ function getAcceptedStatusDetails(status) {
84
+ return `${status.tries}/${status.time ? formatTimeDuration(status.time, "min", Math.floor) : "-"}`;
85
+ }
86
+ function isAcceptedRankProblemStatus(status) {
87
+ return (status == null ? void 0 : status.result) === "AC" || (status == null ? void 0 : status.result) === "FB";
88
+ }
89
+ function isRejectedRankProblemStatus(status) {
90
+ return (status == null ? void 0 : status.result) === "RJ" || (status == null ? void 0 : status.result) === "?";
91
+ }
92
+ function getRankProblemStatusTries(status) {
93
+ return (status == null ? void 0 : status.tries) || 0;
94
+ }
95
+ function getLastPenaltySolution(status, ranklist) {
96
+ const noPenaltyResults = getNoPenaltyResults(ranklist);
97
+ const solutions = status.solutions || [];
98
+ for (let index = solutions.length - 1; index >= 0; index -= 1) {
99
+ const solution = solutions[index];
100
+ if (!noPenaltyResults.includes(solution.result)) {
101
+ return solution;
102
+ }
103
+ }
104
+ return void 0;
105
+ }
106
+ function formatRanklistStatusTime(time, ranklist) {
107
+ if (!time) {
108
+ return "-";
109
+ }
110
+ const precision = getRanklistTimePrecision(time, ranklist);
111
+ if (precision === "ms") {
112
+ const totalMs = Math.floor(formatTimeDuration(time, "ms"));
113
+ const totalSeconds = Math.floor(totalMs / 1e3);
114
+ return `${formatClockSeconds(totalSeconds)}.${padNumber(totalMs % 1e3, 3)}`;
115
+ }
116
+ if (precision === "s") {
117
+ return formatClockSeconds(Math.floor(formatTimeDuration(time, "s")));
118
+ }
119
+ const totalMinutes = Math.floor(formatTimeDuration(time, "min"));
120
+ return `${Math.floor(totalMinutes / 60)}:${padNumber(totalMinutes % 60, 2)}`;
121
+ }
122
+ function getRankProblemStatusCellPresentation(status, ranklist, preset = "classic") {
123
+ if (!status.result) {
124
+ return {};
125
+ }
126
+ if (preset === "classic") {
127
+ if (isAcceptedRankProblemStatus(status)) {
128
+ const details = getAcceptedStatusDetails(status);
129
+ if (typeof status.score === "number") {
130
+ return {
131
+ score: status.score,
132
+ scoreDetails: details
133
+ };
134
+ }
135
+ return { primary: details };
136
+ }
137
+ return {
138
+ primary: typeof status.tries === "number" ? String(status.tries) : ""
139
+ };
140
+ }
141
+ if (isAcceptedRankProblemStatus(status)) {
142
+ const wrongTries = Math.max((status.tries || 0) - 1, 0);
143
+ if (preset === "detailed") {
144
+ return {
145
+ primary: formatRanklistStatusTime(status.time, ranklist),
146
+ secondary: wrongTries > 0 ? `(-${wrongTries})` : ""
147
+ };
148
+ }
149
+ const primary = wrongTries > 0 ? `+${wrongTries}` : "+";
150
+ if (preset === "compact") {
151
+ return {
152
+ primary,
153
+ secondary: formatRanklistStatusTime(status.time, ranklist)
154
+ };
155
+ }
156
+ return { primary };
157
+ }
158
+ if (isRejectedRankProblemStatus(status)) {
159
+ const tries = status.tries || 0;
160
+ const primary = `-${tries}`;
161
+ if (preset === "detailed") {
162
+ return {
163
+ primary: "",
164
+ secondary: tries > 0 ? `(-${tries})` : ""
165
+ };
166
+ }
167
+ if (preset === "compact" && tries > 0) {
168
+ const lastPenaltySolution = getLastPenaltySolution(status, ranklist);
169
+ if (lastPenaltySolution) {
170
+ return {
171
+ primary,
172
+ secondary: formatRanklistStatusTime(lastPenaltySolution.time, ranklist)
173
+ };
174
+ }
175
+ }
176
+ return { primary };
177
+ }
178
+ return {};
179
+ }
180
+ function calculateDirtPercentage(row) {
181
+ const totals = row.statuses.reduce((result, status) => {
182
+ if (!isAcceptedRankProblemStatus(status)) {
183
+ return result;
184
+ }
185
+ const tries = getRankProblemStatusTries(status);
186
+ return {
187
+ wrong: result.wrong + Math.max(tries - 1, 0),
188
+ submitted: result.submitted + tries
189
+ };
190
+ }, { wrong: 0, submitted: 0 });
191
+ if (!totals.submitted || !totals.wrong) {
192
+ return "0%";
193
+ }
194
+ return `${Math.floor(totals.wrong / totals.submitted * 100)}%`;
195
+ }
196
+ function calculateProblemStatisticsFooter(ranklist) {
197
+ const participantCount = ranklist.rows.length;
198
+ return ranklist.problems.map((_, problemIndex) => {
199
+ const stat = {
200
+ participantCount,
201
+ accepted: 0,
202
+ tried: 0,
203
+ submitted: 0,
204
+ dirt: 0,
205
+ dirtSubmitted: 0
206
+ };
207
+ ranklist.rows.forEach((row) => {
208
+ const status = row.statuses[problemIndex];
209
+ const tries = getRankProblemStatusTries(status);
210
+ if (tries > 0) {
211
+ stat.tried += 1;
212
+ stat.submitted += tries;
213
+ }
214
+ if (!isAcceptedRankProblemStatus(status)) {
215
+ return;
216
+ }
217
+ stat.accepted += 1;
218
+ stat.dirt += Math.max(tries - 1, 0);
219
+ stat.dirtSubmitted += tries;
220
+ if (!status.time) {
221
+ return;
222
+ }
223
+ if (!stat.firstAcceptedTime || compareTimeDuration(status.time, stat.firstAcceptedTime) < 0) {
224
+ stat.firstAcceptedTime = status.time;
225
+ }
226
+ if (!stat.lastAcceptedTime || compareTimeDuration(status.time, stat.lastAcceptedTime) > 0) {
227
+ stat.lastAcceptedTime = status.time;
228
+ }
229
+ });
230
+ return stat;
231
+ });
232
+ }
233
+ function formatProblemStatisticsAcceptedRange(stat, ranklist) {
234
+ if (!stat.firstAcceptedTime || !stat.lastAcceptedTime) {
235
+ return "-";
236
+ }
237
+ return `${formatRanklistStatusTime(stat.firstAcceptedTime, ranklist)} / ${formatRanklistStatusTime(stat.lastAcceptedTime, ranklist)}`;
238
+ }
239
+ function formatProblemStatisticsAcceptedMinute(time) {
240
+ if (!time) {
241
+ return "-";
242
+ }
243
+ return String(Math.floor(formatTimeDuration(time, "min")));
244
+ }
245
+ function formatProblemStatisticsPercent(numerator, denominator) {
246
+ if (denominator <= 0) {
247
+ return "(-)";
248
+ }
249
+ return `(${Math.floor(numerator / denominator * 100)}%)`;
250
+ }
251
+ function formatProblemStatisticsAverageHardness(stat) {
252
+ const value = getProblemStatisticsAverageHardness(stat);
253
+ if (value === void 0) {
254
+ return "-";
255
+ }
256
+ return formatAverageHardnessValue(value);
257
+ }
258
+ function calculateSEValue(row, problemStatistics) {
259
+ const values = row.statuses.reduce((result, status, index) => {
260
+ if (!isAcceptedRankProblemStatus(status)) {
261
+ return result;
262
+ }
263
+ const problemSE = getProblemStatisticsAverageHardness(problemStatistics[index]);
264
+ if (problemSE !== void 0) {
265
+ result.push(problemSE);
266
+ }
267
+ return result;
268
+ }, []);
269
+ if (!values.length) {
270
+ return "0.00";
271
+ }
272
+ return formatAverageHardnessValue(values.reduce((sum, value) => sum + value, 0) / values.length);
273
+ }
274
+ function getSolutionModalTitle(problemIndex, user) {
275
+ return `Solutions of ${numberToAlphabet(problemIndex)} (${resolveText(user.name)})`;
276
+ }
277
+ function formatSolutionTimestamp(solution) {
278
+ return secToTimeStr(formatTimeDuration(solution.time, "s"));
279
+ }
280
+ function shouldShowTimeColumn(rows) {
281
+ return rows.some((row) => {
282
+ var _a;
283
+ return Boolean((_a = row.score) == null ? void 0 : _a.time);
284
+ });
285
+ }
286
+ function getRanklistTimePrecision(time, ranklist) {
287
+ const sorter = ranklist.sorter;
288
+ if ((sorter == null ? void 0 : sorter.algorithm) === "ICPC" && sorter.config.timePrecision) {
289
+ return sorter.config.timePrecision;
290
+ }
291
+ return time[1];
292
+ }
293
+ function getNoPenaltyResults(ranklist) {
294
+ const sorter = ranklist.sorter;
295
+ if ((sorter == null ? void 0 : sorter.algorithm) === "ICPC" && sorter.config.noPenaltyResults) {
296
+ return sorter.config.noPenaltyResults;
297
+ }
298
+ return defaultNoPenaltyResults;
299
+ }
300
+ function getProblemStatisticsAverageHardness(stat) {
301
+ if (!stat || stat.participantCount <= 0) {
302
+ return void 0;
303
+ }
304
+ return (stat.participantCount - stat.accepted) / stat.participantCount;
305
+ }
306
+ function formatAverageHardnessValue(value) {
307
+ return value.toFixed(2);
308
+ }
309
+ function compareTimeDuration(a, b) {
310
+ return formatTimeDuration(a, "ms") - formatTimeDuration(b, "ms");
311
+ }
312
+ function formatClockSeconds(totalSeconds) {
313
+ const hours = Math.floor(totalSeconds / 3600);
314
+ const minutes = Math.floor(totalSeconds % 3600 / 60);
315
+ const seconds = totalSeconds % 60;
316
+ return `${hours}:${padNumber(minutes, 2)}:${padNumber(seconds, 2)}`;
317
+ }
318
+ function padNumber(num, size) {
319
+ const raw = String(Math.trunc(Math.abs(num)));
320
+ return raw.length >= size ? raw : `${"0".repeat(size - raw.length)}${raw}`;
321
+ }
322
+ function getProgressDurationMinutes(contest) {
323
+ return formatTimeDuration(contest.duration, "min");
324
+ }
325
+ function getProgressMaxAvailableMinutes(contest, localTime, td = 0) {
326
+ const startAt = new Date(contest.startAt).getTime();
327
+ const currentTime = localTime - td;
328
+ return Math.min(Math.max(Math.floor((currentTime - startAt) / 6e4), 0), getProgressDurationMinutes(contest));
329
+ }
330
+ function isProgressEnded(contest, localTime, td = 0) {
331
+ const startAt = new Date(contest.startAt).getTime();
332
+ const endAt = startAt + formatTimeDuration(contest.duration, "ms");
333
+ const currentTime = localTime - td;
334
+ return currentTime >= endAt;
335
+ }
336
+ function buildSliderMarks(maxAvailableMinutes) {
337
+ const sliderMarks = {};
338
+ for (let i = 0; i <= maxAvailableMinutes; ++i) {
339
+ sliderMarks[i] = "";
340
+ }
341
+ return sliderMarks;
342
+ }
343
+ function getProgressMetrics(data, localTime, td, timeTravelCurrentValue, inTimeMachine) {
344
+ const { contest } = data;
345
+ const startAt = new Date(contest.startAt).getTime();
346
+ const endAt = startAt + formatTimeDuration(contest.duration, "ms");
347
+ const frozenLength = contest.frozenDuration ? formatTimeDuration(contest.frozenDuration, "ms") : 0;
348
+ const currentTime = localTime - td;
349
+ const length = endAt - startAt;
350
+ const frozenBreakpoint = length ? 1 - frozenLength / length : 1;
351
+ const elapsed = Math.min(Math.max(currentTime - startAt, 0), length);
352
+ const remaining = length - elapsed;
353
+ const frozenAt = endAt - frozenLength;
354
+ const percent = length ? elapsed / length * 100 : 0;
355
+ const normalPercent = length ? Math.max(Math.min(currentTime, frozenAt) - startAt, 0) / length * 100 : 0;
356
+ const normalInnerPercent = Math.min(100, frozenBreakpoint ? normalPercent / frozenBreakpoint : 0);
357
+ const frozenPercent = percent - normalPercent;
358
+ const frozenInnerPercent = Math.min(100, 1 - frozenBreakpoint ? frozenPercent / (1 - frozenBreakpoint) : 0);
359
+ const timeTravelElapsed = timeTravelCurrentValue * 60 * 1e3;
360
+ const timeTravelRemaining = length - timeTravelElapsed;
361
+ return {
362
+ elapsed: inTimeMachine ? timeTravelElapsed : elapsed,
363
+ remaining: inTimeMachine ? timeTravelRemaining : remaining,
364
+ frozenBreakpoint,
365
+ normalInnerPercent,
366
+ frozenInnerPercent,
367
+ timeTravelElapsed,
368
+ timeTravelRemaining,
369
+ supportRegen: canRegenerateRanklist(data)
370
+ };
371
+ }
372
+ const srkSupportedVersions = ">=0.3.0 <=0.3.12";
373
+ function caniuse(version) {
374
+ return semver.satisfies(version, srkSupportedVersions);
375
+ }
376
+ const MODAL_ANIMATION_DURATION_MS = 280;
377
+ const SRK_ANIMATED_MODAL_ROOT_CLASS = "srk-animated-modal-root";
378
+ const SRK_SCROLL_LOCK_BODY_CLASS = "srk-scrolling-effect";
379
+ let lastExplicitTriggerPoint = null;
380
+ let lastInteractionPoint = null;
381
+ let hasInteractionTracker = false;
382
+ let lockedModalCount = 0;
383
+ let previousBodyOverflow = "";
384
+ let previousBodyOverflowX = "";
385
+ let previousBodyOverflowY = "";
386
+ let previousBodyPosition = "";
387
+ let previousBodyTop = "";
388
+ let previousBodyLeft = "";
389
+ let previousBodyRight = "";
390
+ let previousBodyWidth = "";
391
+ let previousScrollX = 0;
392
+ let previousScrollY = 0;
393
+ let nextModalFocusScopeId = 1;
394
+ let modalFocusStack = [];
395
+ let hasModalFocusEscapeListener = false;
396
+ function handleModalFocusKeyDown(event) {
397
+ var _a, _b;
398
+ if (event.key !== "Escape") {
399
+ return;
400
+ }
401
+ const topScope = modalFocusStack[modalFocusStack.length - 1];
402
+ if (!topScope) {
403
+ return;
404
+ }
405
+ (_a = topScope.onEscape) == null ? void 0 : _a.call(topScope, event);
406
+ (_b = event.stopImmediatePropagation) == null ? void 0 : _b.call(event);
407
+ event.stopPropagation();
408
+ }
409
+ function ensureModalFocusEscapeListener() {
410
+ if (typeof document === "undefined" || hasModalFocusEscapeListener) {
411
+ return;
412
+ }
413
+ document.addEventListener("keydown", handleModalFocusKeyDown);
414
+ hasModalFocusEscapeListener = true;
415
+ }
416
+ function ensureModalInteractionTracker() {
417
+ if (typeof document === "undefined" || hasInteractionTracker) {
418
+ return;
419
+ }
420
+ document.addEventListener("mousedown", (event) => {
421
+ lastInteractionPoint = { x: event.clientX, y: event.clientY };
422
+ }, true);
423
+ document.addEventListener("touchstart", (event) => {
424
+ const touch = event.touches[0] || event.changedTouches[0];
425
+ if (!touch) {
426
+ return;
427
+ }
428
+ lastInteractionPoint = { x: touch.clientX, y: touch.clientY };
429
+ }, { capture: true, passive: true });
430
+ hasInteractionTracker = true;
431
+ }
432
+ function setModalTriggerPoint(x, y, options) {
433
+ lastExplicitTriggerPoint = {
434
+ x,
435
+ y,
436
+ timestamp: Date.now(),
437
+ source: options == null ? void 0 : options.source,
438
+ context: options == null ? void 0 : options.context
439
+ };
440
+ }
441
+ function captureModalTriggerPointFromMouseEvent(event, options) {
442
+ setModalTriggerPoint(event.clientX, event.clientY, options);
443
+ }
444
+ function getRecentModalTriggerPoint(maxAgeMs = 500) {
445
+ if (!lastExplicitTriggerPoint) {
446
+ return null;
447
+ }
448
+ if (Date.now() - lastExplicitTriggerPoint.timestamp > maxAgeMs) {
449
+ return null;
450
+ }
451
+ return lastExplicitTriggerPoint;
452
+ }
453
+ function getLastModalInteractionPoint() {
454
+ return lastInteractionPoint;
455
+ }
456
+ function resolveModalTransformOrigin(dialogElement) {
457
+ if (typeof window === "undefined" || !dialogElement) {
458
+ return {
459
+ origin: { x: 0, y: 0 },
460
+ source: "fallback",
461
+ explicitTriggerPoint: null,
462
+ globalInteractionPoint: lastInteractionPoint,
463
+ dialogRect: null,
464
+ fallbackPoint: { x: 0, y: 0 }
465
+ };
466
+ }
467
+ const rect = dialogElement.getBoundingClientRect();
468
+ const fallbackPoint = {
469
+ x: rect.width / 2,
470
+ y: Math.min(rect.height * 0.22, 96)
471
+ };
472
+ const explicitTriggerPoint = getRecentModalTriggerPoint();
473
+ if (explicitTriggerPoint) {
474
+ return {
475
+ origin: {
476
+ x: explicitTriggerPoint.x - rect.left,
477
+ y: explicitTriggerPoint.y - rect.top
478
+ },
479
+ source: "explicit-trigger",
480
+ explicitTriggerPoint,
481
+ globalInteractionPoint: lastInteractionPoint,
482
+ dialogRect: {
483
+ left: rect.left,
484
+ top: rect.top,
485
+ width: rect.width,
486
+ height: rect.height
487
+ },
488
+ fallbackPoint
489
+ };
490
+ }
491
+ if (!lastInteractionPoint) {
492
+ return {
493
+ origin: fallbackPoint,
494
+ source: "fallback",
495
+ explicitTriggerPoint: null,
496
+ globalInteractionPoint: null,
497
+ dialogRect: {
498
+ left: rect.left,
499
+ top: rect.top,
500
+ width: rect.width,
501
+ height: rect.height
502
+ },
503
+ fallbackPoint
504
+ };
505
+ }
506
+ return {
507
+ origin: {
508
+ x: lastInteractionPoint.x - rect.left,
509
+ y: lastInteractionPoint.y - rect.top
510
+ },
511
+ source: "global-pointer",
512
+ explicitTriggerPoint: null,
513
+ globalInteractionPoint: lastInteractionPoint,
514
+ dialogRect: {
515
+ left: rect.left,
516
+ top: rect.top,
517
+ width: rect.width,
518
+ height: rect.height
519
+ },
520
+ fallbackPoint
521
+ };
522
+ }
523
+ function activeHTMLElement() {
524
+ if (typeof document === "undefined") {
525
+ return null;
526
+ }
527
+ return document.activeElement instanceof HTMLElement ? document.activeElement : null;
528
+ }
529
+ function focusWithoutScroll(element) {
530
+ try {
531
+ element.focus({ preventScroll: true });
532
+ } catch {
533
+ element.focus();
534
+ }
535
+ }
536
+ function registerModalFocusScope(dialogElement, options = {}) {
537
+ if (typeof document === "undefined" || !dialogElement) {
538
+ return null;
539
+ }
540
+ ensureModalFocusEscapeListener();
541
+ const existing = modalFocusStack.find((entry) => entry.dialogElement === dialogElement);
542
+ if (existing) {
543
+ existing.onEscape = options.onEscape;
544
+ return existing.id;
545
+ }
546
+ const hadTabIndex = dialogElement.hasAttribute("tabindex");
547
+ const previousTabIndex = dialogElement.getAttribute("tabindex");
548
+ if (!hadTabIndex) {
549
+ dialogElement.setAttribute("tabindex", "-1");
550
+ }
551
+ const scope = {
552
+ id: nextModalFocusScopeId,
553
+ dialogElement,
554
+ restoreElement: activeHTMLElement(),
555
+ hadTabIndex,
556
+ previousTabIndex,
557
+ onEscape: options.onEscape
558
+ };
559
+ nextModalFocusScopeId += 1;
560
+ modalFocusStack.push(scope);
561
+ focusWithoutScroll(dialogElement);
562
+ return scope.id;
563
+ }
564
+ function unregisterModalFocusScope(scopeId) {
565
+ var _a;
566
+ if (typeof document === "undefined" || scopeId == null) {
567
+ return;
568
+ }
569
+ const index = modalFocusStack.findIndex((entry) => entry.id === scopeId);
570
+ if (index === -1) {
571
+ return;
572
+ }
573
+ const [scope] = modalFocusStack.splice(index, 1);
574
+ if (scope.hadTabIndex) {
575
+ if (scope.previousTabIndex === null) {
576
+ scope.dialogElement.removeAttribute("tabindex");
577
+ } else {
578
+ scope.dialogElement.setAttribute("tabindex", scope.previousTabIndex);
579
+ }
580
+ } else {
581
+ scope.dialogElement.removeAttribute("tabindex");
582
+ }
583
+ const wasTopScope = index === modalFocusStack.length;
584
+ if (!wasTopScope) {
585
+ return;
586
+ }
587
+ const nextTopScope = modalFocusStack[modalFocusStack.length - 1];
588
+ const restoreElement = ((_a = scope.restoreElement) == null ? void 0 : _a.isConnected) ? scope.restoreElement : nextTopScope == null ? void 0 : nextTopScope.dialogElement;
589
+ if (restoreElement) {
590
+ focusWithoutScroll(restoreElement);
591
+ }
592
+ }
593
+ function isTopModalFocusScope(scopeId) {
594
+ var _a;
595
+ if (scopeId == null) {
596
+ return false;
597
+ }
598
+ return ((_a = modalFocusStack[modalFocusStack.length - 1]) == null ? void 0 : _a.id) === scopeId;
599
+ }
600
+ function lockModalBodyScroll() {
601
+ if (typeof document === "undefined") {
602
+ return;
603
+ }
604
+ if (lockedModalCount === 0) {
605
+ previousScrollX = window.scrollX;
606
+ previousScrollY = window.scrollY;
607
+ previousBodyOverflow = document.body.style.overflow;
608
+ previousBodyOverflowX = document.body.style.overflowX;
609
+ previousBodyOverflowY = document.body.style.overflowY;
610
+ previousBodyPosition = document.body.style.position;
611
+ previousBodyTop = document.body.style.top;
612
+ previousBodyLeft = document.body.style.left;
613
+ previousBodyRight = document.body.style.right;
614
+ previousBodyWidth = document.body.style.width;
615
+ document.body.classList.add(SRK_SCROLL_LOCK_BODY_CLASS);
616
+ document.body.style.overflow = "hidden";
617
+ document.body.style.overflowX = "hidden";
618
+ document.body.style.overflowY = "hidden";
619
+ document.body.style.position = "fixed";
620
+ document.body.style.top = `-${previousScrollY}px`;
621
+ document.body.style.left = `-${previousScrollX}px`;
622
+ document.body.style.right = "0";
623
+ document.body.style.width = "100%";
624
+ }
625
+ lockedModalCount += 1;
626
+ }
627
+ function unlockModalBodyScroll() {
628
+ if (typeof document === "undefined" || lockedModalCount === 0) {
629
+ return;
630
+ }
631
+ lockedModalCount -= 1;
632
+ if (lockedModalCount === 0) {
633
+ document.body.classList.remove(SRK_SCROLL_LOCK_BODY_CLASS);
634
+ document.body.style.overflow = previousBodyOverflow;
635
+ document.body.style.overflowX = previousBodyOverflowX;
636
+ document.body.style.overflowY = previousBodyOverflowY;
637
+ document.body.style.position = previousBodyPosition;
638
+ document.body.style.top = previousBodyTop;
639
+ document.body.style.left = previousBodyLeft;
640
+ document.body.style.right = previousBodyRight;
641
+ document.body.style.width = previousBodyWidth;
642
+ const isJsdom = typeof navigator !== "undefined" && /jsdom/i.test(navigator.userAgent);
643
+ if (!isJsdom && typeof window.scrollTo === "function") {
644
+ try {
645
+ window.scrollTo(previousScrollX, previousScrollY);
646
+ } catch {
647
+ }
648
+ }
649
+ }
650
+ }
651
+ function resetModalInteractionStateForTests() {
652
+ lastExplicitTriggerPoint = null;
653
+ lastInteractionPoint = null;
654
+ lockedModalCount = 0;
655
+ nextModalFocusScopeId = 1;
656
+ modalFocusStack = [];
657
+ previousBodyOverflow = "";
658
+ previousBodyOverflowX = "";
659
+ previousBodyOverflowY = "";
660
+ previousBodyPosition = "";
661
+ previousBodyTop = "";
662
+ previousBodyLeft = "";
663
+ previousBodyRight = "";
664
+ previousBodyWidth = "";
665
+ previousScrollX = 0;
666
+ previousScrollY = 0;
667
+ if (typeof document !== "undefined") {
668
+ document.body.classList.remove(SRK_SCROLL_LOCK_BODY_CLASS);
669
+ document.body.style.overflow = "";
670
+ document.body.style.overflowX = "";
671
+ document.body.style.overflowY = "";
672
+ document.body.style.position = "";
673
+ document.body.style.top = "";
674
+ document.body.style.left = "";
675
+ document.body.style.right = "";
676
+ document.body.style.width = "";
677
+ }
678
+ }
679
+ export { MODAL_ANIMATION_DURATION_MS, SRK_ANIMATED_MODAL_ROOT_CLASS, SRK_SCROLL_LOCK_BODY_CLASS, buildSliderMarks, calculateDirtPercentage, calculateProblemStatisticsFooter, calculateSEValue, caniuse, captureModalTriggerPointFromMouseEvent, defaultBackgroundColor, ensureModalInteractionTracker, formatProblemStatisticsAcceptedMinute, formatProblemStatisticsAcceptedRange, formatProblemStatisticsAverageHardness, formatProblemStatisticsPercent, formatRanklistStatusTime, formatSolutionTimestamp, getAcceptedStatusDetails, getLastModalInteractionPoint, getLastPenaltySolution, getMarkerPresentation, getProblemHeaderBackgroundImage, getProgressDurationMinutes, getProgressMaxAvailableMinutes, getProgressMetrics, getRankProblemStatusCellPresentation, getRankProblemStatusTries, getRecentModalTriggerPoint, getSolutionModalTitle, getSolutionResultMeta, isAcceptedRankProblemStatus, isProgressEnded, isRejectedRankProblemStatus, isTopModalFocusScope, lockModalBodyScroll, registerModalFocusScope, resetModalInteractionStateForTests, resolveModalTransformOrigin, resolveSrkAssetUrl, setModalTriggerPoint, shouldShowTimeColumn, srkSupportedVersions, unlockModalBodyScroll, unregisterModalFocusScope };
@@ -0,0 +1,47 @@
1
+ export declare const MODAL_ANIMATION_DURATION_MS = 280;
2
+ export declare const SRK_ANIMATED_MODAL_ROOT_CLASS = "srk-animated-modal-root";
3
+ export declare const SRK_SCROLL_LOCK_BODY_CLASS = "srk-scrolling-effect";
4
+ export interface InteractionPoint {
5
+ x: number;
6
+ y: number;
7
+ }
8
+ export interface ModalTriggerPoint extends InteractionPoint {
9
+ timestamp: number;
10
+ source?: string;
11
+ context?: Record<string, unknown>;
12
+ }
13
+ export declare type TransformOriginResolution = {
14
+ origin: InteractionPoint;
15
+ source: 'explicit-trigger' | 'global-pointer' | 'fallback';
16
+ explicitTriggerPoint: ModalTriggerPoint | null;
17
+ globalInteractionPoint: InteractionPoint | null;
18
+ dialogRect: {
19
+ left: number;
20
+ top: number;
21
+ width: number;
22
+ height: number;
23
+ } | null;
24
+ fallbackPoint: InteractionPoint;
25
+ };
26
+ export declare type ModalFocusScopeId = number;
27
+ export interface ModalFocusScopeOptions {
28
+ onEscape?: (event: KeyboardEvent) => void;
29
+ }
30
+ export declare function ensureModalInteractionTracker(): void;
31
+ export declare function setModalTriggerPoint(x: number, y: number, options?: {
32
+ source?: string;
33
+ context?: Record<string, unknown>;
34
+ }): void;
35
+ export declare function captureModalTriggerPointFromMouseEvent(event: Pick<MouseEvent, 'clientX' | 'clientY'>, options?: {
36
+ source?: string;
37
+ context?: Record<string, unknown>;
38
+ }): void;
39
+ export declare function getRecentModalTriggerPoint(maxAgeMs?: number): ModalTriggerPoint | null;
40
+ export declare function getLastModalInteractionPoint(): InteractionPoint | null;
41
+ export declare function resolveModalTransformOrigin(dialogElement: HTMLElement | null): TransformOriginResolution;
42
+ export declare function registerModalFocusScope(dialogElement: HTMLElement | null, options?: ModalFocusScopeOptions): ModalFocusScopeId | null;
43
+ export declare function unregisterModalFocusScope(scopeId: ModalFocusScopeId | null | undefined): void;
44
+ export declare function isTopModalFocusScope(scopeId: ModalFocusScopeId | null | undefined): boolean;
45
+ export declare function lockModalBodyScroll(): void;
46
+ export declare function unlockModalBodyScroll(): void;
47
+ export declare function resetModalInteractionStateForTests(): void;
@@ -0,0 +1,16 @@
1
+ import type * as srk from '@algoux/standard-ranklist';
2
+ export interface ProgressMetrics {
3
+ elapsed: number;
4
+ remaining: number;
5
+ frozenBreakpoint: number;
6
+ normalInnerPercent: number;
7
+ frozenInnerPercent: number;
8
+ timeTravelElapsed: number;
9
+ timeTravelRemaining: number;
10
+ supportRegen: boolean;
11
+ }
12
+ export declare function getProgressDurationMinutes(contest: srk.Contest): number;
13
+ export declare function getProgressMaxAvailableMinutes(contest: srk.Contest, localTime: number, td?: number): number;
14
+ export declare function isProgressEnded(contest: srk.Contest, localTime: number, td?: number): boolean;
15
+ export declare function buildSliderMarks(maxAvailableMinutes: number): Record<number, string>;
16
+ export declare function getProgressMetrics(data: srk.Ranklist, localTime: number, td: number, timeTravelCurrentValue: number, inTimeMachine: boolean): ProgressMetrics;
@@ -0,0 +1,62 @@
1
+ import type * as srk from '@algoux/standard-ranklist';
2
+ import { EnumTheme } from '@algoux/standard-ranklist-utils';
3
+ import type { StaticRanklist, StaticRanklistRow } from './types';
4
+ export declare const defaultBackgroundColor: Record<EnumTheme, string>;
5
+ export interface MarkerPresentation {
6
+ className?: string;
7
+ style?: {
8
+ backgroundColor?: string;
9
+ };
10
+ }
11
+ export interface SolutionResultMeta {
12
+ label: string;
13
+ className?: string;
14
+ }
15
+ export declare type RanklistStatusCellPreset = 'classic' | 'detailed' | 'minimal' | 'compact';
16
+ export declare type RanklistUserAvatarPlacement = 'user' | 'organization';
17
+ export interface RanklistColumnTitles {
18
+ series?: string[] | ((series: srk.RankSeries, index: number) => string | undefined);
19
+ organization?: string;
20
+ user?: string;
21
+ score?: string;
22
+ time?: string;
23
+ dirt?: string;
24
+ se?: string;
25
+ }
26
+ export interface ProblemStatisticsFooter {
27
+ participantCount: number;
28
+ accepted: number;
29
+ tried: number;
30
+ submitted: number;
31
+ dirt: number;
32
+ dirtSubmitted: number;
33
+ firstAcceptedTime?: srk.TimeDuration;
34
+ lastAcceptedTime?: srk.TimeDuration;
35
+ }
36
+ export interface RanklistStatusCellPresentation {
37
+ primary?: string;
38
+ secondary?: string;
39
+ score?: number;
40
+ scoreDetails?: string;
41
+ }
42
+ export declare function resolveSrkAssetUrl(url: string, field: string, formatter?: (url: string, field: string) => string): string;
43
+ export declare function getProblemHeaderBackgroundImage(style: srk.Style | undefined, theme: EnumTheme, gradientDirection?: number): string;
44
+ export declare function getMarkerPresentation(marker: srk.Marker, theme: EnumTheme): MarkerPresentation;
45
+ export declare function getSolutionResultMeta(result: srk.Solution['result']): SolutionResultMeta;
46
+ export declare function getAcceptedStatusDetails(status: srk.RankProblemStatus): string;
47
+ export declare function isAcceptedRankProblemStatus(status: srk.RankProblemStatus | undefined): boolean;
48
+ export declare function isRejectedRankProblemStatus(status: srk.RankProblemStatus | undefined): boolean;
49
+ export declare function getRankProblemStatusTries(status: srk.RankProblemStatus | undefined): number;
50
+ export declare function getLastPenaltySolution(status: srk.RankProblemStatus, ranklist: srk.Ranklist): srk.Solution | undefined;
51
+ export declare function formatRanklistStatusTime(time: srk.TimeDuration | undefined, ranklist: srk.Ranklist): string;
52
+ export declare function getRankProblemStatusCellPresentation(status: srk.RankProblemStatus, ranklist: srk.Ranklist, preset?: RanklistStatusCellPreset): RanklistStatusCellPresentation;
53
+ export declare function calculateDirtPercentage(row: Pick<StaticRanklistRow, 'statuses'>): string;
54
+ export declare function calculateProblemStatisticsFooter(ranklist: StaticRanklist): ProblemStatisticsFooter[];
55
+ export declare function formatProblemStatisticsAcceptedRange(stat: ProblemStatisticsFooter, ranklist: srk.Ranklist): string;
56
+ export declare function formatProblemStatisticsAcceptedMinute(time: srk.TimeDuration | undefined): string;
57
+ export declare function formatProblemStatisticsPercent(numerator: number, denominator: number): string;
58
+ export declare function formatProblemStatisticsAverageHardness(stat: ProblemStatisticsFooter): string;
59
+ export declare function calculateSEValue(row: Pick<StaticRanklistRow, 'statuses'>, problemStatistics: ProblemStatisticsFooter[]): string;
60
+ export declare function getSolutionModalTitle(problemIndex: number, user: srk.User): string;
61
+ export declare function formatSolutionTimestamp(solution: srk.Solution): string;
62
+ export declare function shouldShowTimeColumn(rows: srk.RanklistRow[]): boolean;
@@ -0,0 +1,32 @@
1
+ import type * as srk from '@algoux/standard-ranklist';
2
+ export interface RankValue {
3
+ /** Rank value initially. If the user is unofficial and rank value equals null, it will be rendered as unofficial mark such as '*'. */
4
+ rank: number | null;
5
+ /**
6
+ * Series segment index which this rank belongs to initially. `null` means this rank does not belong to any segment. `undefined` means it will be calculated automatically (only if the segment's count property exists).
7
+ * @defaultValue null
8
+ */
9
+ segmentIndex?: number | null;
10
+ }
11
+ export declare type StaticRanklist = Omit<srk.Ranklist, 'rows'> & {
12
+ rows: Array<srk.RanklistRow & {
13
+ rankValues: RankValue[];
14
+ }>;
15
+ };
16
+ export declare type StaticRanklistRow = StaticRanklist['rows'][number];
17
+ export interface UserClickPayload {
18
+ user: srk.User;
19
+ row: StaticRanklistRow;
20
+ rowIndex: number;
21
+ ranklist: StaticRanklist;
22
+ }
23
+ export interface SolutionClickPayload {
24
+ user: srk.User;
25
+ row: StaticRanklistRow;
26
+ rowIndex: number;
27
+ problemIndex: number;
28
+ problem: srk.Problem | undefined;
29
+ status: srk.RankProblemStatus;
30
+ solutions: srk.Solution[];
31
+ ranklist: StaticRanklist;
32
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@algoux/standard-ranklist-renderer-component-core",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Core helpers and shared types for Standard Ranklist Renderer Component",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/algoux/standard-ranklist-renderer-component",