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

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