@algoux/standard-ranklist-renderer-component-core 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +26 -0
- package/dist/caniuse.d.ts +2 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +454 -0
- package/dist/modal.d.ts +47 -0
- package/dist/progress.d.ts +16 -0
- package/dist/ranklist.d.ts +21 -0
- package/dist/types.d.ts +32 -0
- package/package.json +36 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 algoUX
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# @algoux/standard-ranklist-renderer-component-core
|
|
2
|
+
|
|
3
|
+
Shared helpers, compatibility checks, and payload types for Standard Ranklist Renderer Component packages.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm i -D @algoux/standard-ranklist
|
|
9
|
+
npm i -S @algoux/standard-ranklist-renderer-component-core
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Main Exports
|
|
13
|
+
|
|
14
|
+
- `convertToStaticRanklist`
|
|
15
|
+
- `caniuse`
|
|
16
|
+
- `srkSupportedVersions`
|
|
17
|
+
- `StaticRanklist`
|
|
18
|
+
- `UserClickPayload`
|
|
19
|
+
- `SolutionClickPayload`
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { caniuse } from '@algoux/standard-ranklist-renderer-component-core';
|
|
25
|
+
import { convertToStaticRanklist } from '@algoux/standard-ranklist-utils';
|
|
26
|
+
```
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});var J=require("color"),s=require("@algoux/standard-ranklist-utils"),V=require("semver");function B(e){return e&&typeof e=="object"&&"default"in e?e:{default:e}}var O=B(J),Z=B(V);const N={[s.EnumTheme.light]:"#ffffff",[s.EnumTheme.dark]:"#191919"};function G(e,t,o){if(typeof o=="function")return o(e,t);if(e.startsWith("//"))return e;const n=e.match(/^([a-zA-Z][a-zA-Z0-9+.-]*):/);if(n){const r=n[1].toLowerCase();return r==="http"||r==="https"||r==="data"?e:(console.warn(`unsupported protocol "${r}" in srk asset url:`,e),"")}return console.warn("unsupported srk asset url:",e),""}function Q(e,t){const{backgroundColor:o}=s.resolveStyle(e||{}),n=o[t]||N[t],r=O.default(n).string(),i=O.default(n).alpha(.27).string();return`linear-gradient(180deg, ${r} 0%, ${r} 10%, ${i} 10%, transparent 100%)`}function ee(e,t){return typeof e.style=="string"?{className:`srk-preset-marker-${e.style}`}:e.style?{style:{backgroundColor:s.resolveStyle(e.style).backgroundColor[t]}}:{}}function te(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 oe(e){return`${e.tries}/${e.time?s.formatTimeDuration(e.time,"min",Math.floor):"-"}`}function ne(e,t){return`Solutions of ${s.numberToAlphabet(e)} (${s.resolveText(t.name)})`}function re(e){return s.secToTimeStr(s.formatTimeDuration(e.time,"s"))}function se(e){return e.some(t=>{var o;return Boolean((o=t.score)==null?void 0:o.time)})}function F(e){return s.formatTimeDuration(e.duration,"min")}function ie(e,t,o=0){const n=new Date(e.startAt).getTime(),r=t-o;return Math.min(Math.max(Math.floor((r-n)/6e4),0),F(e))}function le(e,t,o=0){const r=new Date(e.startAt).getTime()+s.formatTimeDuration(e.duration,"ms");return t-o>=r}function ae(e){const t={};for(let o=0;o<=e;++o)t[o]="";return t}function ue(e,t,o,n,r){const{contest:i}=e,u=new Date(i.startAt).getTime(),g=u+s.formatTimeDuration(i.duration,"ms"),L=i.frozenDuration?s.formatTimeDuration(i.frozenDuration,"ms"):0,R=t-o,c=g-u,f=c?1-L/c:1,h=Math.min(Math.max(R-u,0),c),X=c-h,K=g-L,U=c?h/c*100:0,I=c?Math.max(Math.min(R,K)-u,0)/c*100:0,W=Math.min(100,f?I/f:0),H=U-I,q=Math.min(100,1-f?H/(1-f):0),M=n*60*1e3,D=c-M;return{elapsed:r?M:h,remaining:r?D:X,frozenBreakpoint:f,normalInnerPercent:W,frozenInnerPercent:q,timeTravelElapsed:M,timeTravelRemaining:D,supportRegen:s.canRegenerateRanklist(e)}}const j=">=0.3.0 <=0.3.12";function ce(e){return Z.default.satisfies(e,j)}const de=280,fe="srk-animated-modal-root",b="srk-scrolling-effect";let m=null,a=null,_=!1,d=0,S="",v="",w="",k="",A="",x="",P="",E="",p=0,y=0,T=1,l=[],C=!1;function me(e){var o,n;if(e.key!=="Escape")return;const t=l[l.length-1];!t||((o=t.onEscape)==null||o.call(t,e),(n=e.stopImmediatePropagation)==null||n.call(e),e.stopPropagation())}function ge(){typeof document=="undefined"||C||(document.addEventListener("keydown",me),C=!0)}function pe(){typeof document=="undefined"||_||(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}),_=!0)}function Y(e,t,o){m={x:e,y:t,timestamp:Date.now(),source:o==null?void 0:o.source,context:o==null?void 0:o.context}}function ye(e,t){Y(e.clientX,e.clientY,t)}function z(e=500){return!m||Date.now()-m.timestamp>e?null:m}function be(){return a}function he(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(),o={x:t.width/2,y:Math.min(t.height*.22,96)},n=z();return n?{origin:{x:n.x-t.left,y:n.y-t.top},source:"explicit-trigger",explicitTriggerPoint:n,globalInteractionPoint:a,dialogRect:{left:t.left,top:t.top,width:t.width,height:t.height},fallbackPoint:o}: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:o}:{origin:o,source:"fallback",explicitTriggerPoint:null,globalInteractionPoint:null,dialogRect:{left:t.left,top:t.top,width:t.width,height:t.height},fallbackPoint:o}}function Me(){return typeof document=="undefined"?null:document.activeElement instanceof HTMLElement?document.activeElement:null}function $(e){try{e.focus({preventScroll:!0})}catch{e.focus()}}function Te(e,t={}){if(typeof document=="undefined"||!e)return null;ge();const o=l.find(u=>u.dialogElement===e);if(o)return o.onEscape=t.onEscape,o.id;const n=e.hasAttribute("tabindex"),r=e.getAttribute("tabindex");n||e.setAttribute("tabindex","-1");const i={id:T,dialogElement:e,restoreElement:Me(),hadTabIndex:n,previousTabIndex:r,onEscape:t.onEscape};return T+=1,l.push(i),$(e),i.id}function Se(e){var u;if(typeof document=="undefined"||e==null)return;const t=l.findIndex(g=>g.id===e);if(t===-1)return;const[o]=l.splice(t,1);if(o.hadTabIndex?o.previousTabIndex===null?o.dialogElement.removeAttribute("tabindex"):o.dialogElement.setAttribute("tabindex",o.previousTabIndex):o.dialogElement.removeAttribute("tabindex"),!(t===l.length))return;const r=l[l.length-1],i=(u=o.restoreElement)!=null&&u.isConnected?o.restoreElement:r==null?void 0:r.dialogElement;i&&$(i)}function ve(e){var t;return e==null?!1:((t=l[l.length-1])==null?void 0:t.id)===e}function we(){typeof document!="undefined"&&(d===0&&(p=window.scrollX,y=window.scrollY,S=document.body.style.overflow,v=document.body.style.overflowX,w=document.body.style.overflowY,k=document.body.style.position,A=document.body.style.top,x=document.body.style.left,P=document.body.style.right,E=document.body.style.width,document.body.classList.add(b),document.body.style.overflow="hidden",document.body.style.overflowX="hidden",document.body.style.overflowY="hidden",document.body.style.position="fixed",document.body.style.top=`-${y}px`,document.body.style.left=`-${p}px`,document.body.style.right="0",document.body.style.width="100%"),d+=1)}function ke(){if(!(typeof document=="undefined"||d===0)&&(d-=1,d===0&&(document.body.classList.remove(b),document.body.style.overflow=S,document.body.style.overflowX=v,document.body.style.overflowY=w,document.body.style.position=k,document.body.style.top=A,document.body.style.left=x,document.body.style.right=P,document.body.style.width=E,!(typeof navigator!="undefined"&&/jsdom/i.test(navigator.userAgent))&&typeof window.scrollTo=="function")))try{window.scrollTo(p,y)}catch{}}function Ae(){m=null,a=null,d=0,T=1,l=[],S="",v="",w="",k="",A="",x="",P="",E="",p=0,y=0,typeof document!="undefined"&&(document.body.classList.remove(b),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=de;exports.SRK_ANIMATED_MODAL_ROOT_CLASS=fe;exports.SRK_SCROLL_LOCK_BODY_CLASS=b;exports.buildSliderMarks=ae;exports.caniuse=ce;exports.captureModalTriggerPointFromMouseEvent=ye;exports.defaultBackgroundColor=N;exports.ensureModalInteractionTracker=pe;exports.formatSolutionTimestamp=re;exports.getAcceptedStatusDetails=oe;exports.getLastModalInteractionPoint=be;exports.getMarkerPresentation=ee;exports.getProblemHeaderBackgroundImage=Q;exports.getProgressDurationMinutes=F;exports.getProgressMaxAvailableMinutes=ie;exports.getProgressMetrics=ue;exports.getRecentModalTriggerPoint=z;exports.getSolutionModalTitle=ne;exports.getSolutionResultMeta=te;exports.isProgressEnded=le;exports.isTopModalFocusScope=ve;exports.lockModalBodyScroll=we;exports.registerModalFocusScope=Te;exports.resetModalInteractionStateForTests=Ae;exports.resolveModalTransformOrigin=he;exports.resolveSrkAssetUrl=G;exports.setModalTriggerPoint=Y;exports.shouldShowTimeColumn=se;exports.srkSupportedVersions=j;exports.unlockModalBodyScroll=ke;exports.unregisterModalFocusScope=Se;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,454 @@
|
|
|
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
|
+
function resolveSrkAssetUrl(url, field, formatter) {
|
|
10
|
+
if (typeof formatter === "function") {
|
|
11
|
+
return formatter(url, field);
|
|
12
|
+
}
|
|
13
|
+
if (url.startsWith("//")) {
|
|
14
|
+
return url;
|
|
15
|
+
}
|
|
16
|
+
const protocolMatch = url.match(/^([a-zA-Z][a-zA-Z0-9+.-]*):/);
|
|
17
|
+
if (protocolMatch) {
|
|
18
|
+
const protocol = protocolMatch[1].toLowerCase();
|
|
19
|
+
if (protocol === "http" || protocol === "https" || protocol === "data") {
|
|
20
|
+
return url;
|
|
21
|
+
}
|
|
22
|
+
console.warn(`unsupported protocol "${protocol}" in srk asset url:`, url);
|
|
23
|
+
return "";
|
|
24
|
+
}
|
|
25
|
+
console.warn("unsupported srk asset url:", url);
|
|
26
|
+
return "";
|
|
27
|
+
}
|
|
28
|
+
function getProblemHeaderBackgroundImage(style, theme) {
|
|
29
|
+
const { backgroundColor } = resolveStyle(style || {});
|
|
30
|
+
const resolvedColor = backgroundColor[theme] || defaultBackgroundColor[theme];
|
|
31
|
+
const bgColorStr = Color(resolvedColor).string();
|
|
32
|
+
const bgColorAlphaStr = Color(resolvedColor).alpha(0.27).string();
|
|
33
|
+
return `linear-gradient(180deg, ${bgColorStr} 0%, ${bgColorStr} 10%, ${bgColorAlphaStr} 10%, transparent 100%)`;
|
|
34
|
+
}
|
|
35
|
+
function getMarkerPresentation(marker, theme) {
|
|
36
|
+
if (typeof marker.style === "string") {
|
|
37
|
+
return { className: `srk-preset-marker-${marker.style}` };
|
|
38
|
+
}
|
|
39
|
+
if (marker.style) {
|
|
40
|
+
return {
|
|
41
|
+
style: {
|
|
42
|
+
backgroundColor: resolveStyle(marker.style).backgroundColor[theme]
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return {};
|
|
47
|
+
}
|
|
48
|
+
function getSolutionResultMeta(result) {
|
|
49
|
+
switch (result) {
|
|
50
|
+
case "FB":
|
|
51
|
+
return { label: "First Blood", className: "srk-preset-result-fb" };
|
|
52
|
+
case "AC":
|
|
53
|
+
return { label: "Accepted", className: "srk-preset-result-ac" };
|
|
54
|
+
case "RJ":
|
|
55
|
+
return { label: "Rejected", className: "srk-preset-result-rj" };
|
|
56
|
+
case "?":
|
|
57
|
+
return { label: "Frozen", className: "srk-preset-result-fz" };
|
|
58
|
+
case "WA":
|
|
59
|
+
return { label: "Wrong Answer", className: "srk-preset-result-rj" };
|
|
60
|
+
case "PE":
|
|
61
|
+
return { label: "Presentation Error", className: "srk-preset-result-rj" };
|
|
62
|
+
case "TLE":
|
|
63
|
+
return { label: "Time Limit Exceeded", className: "srk-preset-result-rj" };
|
|
64
|
+
case "MLE":
|
|
65
|
+
return { label: "Memory Limit Exceeded", className: "srk-preset-result-rj" };
|
|
66
|
+
case "OLE":
|
|
67
|
+
return { label: "Output Limit Exceeded", className: "srk-preset-result-rj" };
|
|
68
|
+
case "RTE":
|
|
69
|
+
return { label: "Runtime Error", className: "srk-preset-result-rj" };
|
|
70
|
+
case "NOUT":
|
|
71
|
+
return { label: "No Output" };
|
|
72
|
+
case "CE":
|
|
73
|
+
return { label: "Compile Error" };
|
|
74
|
+
case "UKE":
|
|
75
|
+
return { label: "Unknown Error" };
|
|
76
|
+
case null:
|
|
77
|
+
return { label: "--" };
|
|
78
|
+
default:
|
|
79
|
+
return { label: result };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function getAcceptedStatusDetails(status) {
|
|
83
|
+
return `${status.tries}/${status.time ? formatTimeDuration(status.time, "min", Math.floor) : "-"}`;
|
|
84
|
+
}
|
|
85
|
+
function getSolutionModalTitle(problemIndex, user) {
|
|
86
|
+
return `Solutions of ${numberToAlphabet(problemIndex)} (${resolveText(user.name)})`;
|
|
87
|
+
}
|
|
88
|
+
function formatSolutionTimestamp(solution) {
|
|
89
|
+
return secToTimeStr(formatTimeDuration(solution.time, "s"));
|
|
90
|
+
}
|
|
91
|
+
function shouldShowTimeColumn(rows) {
|
|
92
|
+
return rows.some((row) => {
|
|
93
|
+
var _a;
|
|
94
|
+
return Boolean((_a = row.score) == null ? void 0 : _a.time);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function getProgressDurationMinutes(contest) {
|
|
98
|
+
return formatTimeDuration(contest.duration, "min");
|
|
99
|
+
}
|
|
100
|
+
function getProgressMaxAvailableMinutes(contest, localTime, td = 0) {
|
|
101
|
+
const startAt = new Date(contest.startAt).getTime();
|
|
102
|
+
const currentTime = localTime - td;
|
|
103
|
+
return Math.min(Math.max(Math.floor((currentTime - startAt) / 6e4), 0), getProgressDurationMinutes(contest));
|
|
104
|
+
}
|
|
105
|
+
function isProgressEnded(contest, localTime, td = 0) {
|
|
106
|
+
const startAt = new Date(contest.startAt).getTime();
|
|
107
|
+
const endAt = startAt + formatTimeDuration(contest.duration, "ms");
|
|
108
|
+
const currentTime = localTime - td;
|
|
109
|
+
return currentTime >= endAt;
|
|
110
|
+
}
|
|
111
|
+
function buildSliderMarks(maxAvailableMinutes) {
|
|
112
|
+
const sliderMarks = {};
|
|
113
|
+
for (let i = 0; i <= maxAvailableMinutes; ++i) {
|
|
114
|
+
sliderMarks[i] = "";
|
|
115
|
+
}
|
|
116
|
+
return sliderMarks;
|
|
117
|
+
}
|
|
118
|
+
function getProgressMetrics(data, localTime, td, timeTravelCurrentValue, inTimeMachine) {
|
|
119
|
+
const { contest } = data;
|
|
120
|
+
const startAt = new Date(contest.startAt).getTime();
|
|
121
|
+
const endAt = startAt + formatTimeDuration(contest.duration, "ms");
|
|
122
|
+
const frozenLength = contest.frozenDuration ? formatTimeDuration(contest.frozenDuration, "ms") : 0;
|
|
123
|
+
const currentTime = localTime - td;
|
|
124
|
+
const length = endAt - startAt;
|
|
125
|
+
const frozenBreakpoint = length ? 1 - frozenLength / length : 1;
|
|
126
|
+
const elapsed = Math.min(Math.max(currentTime - startAt, 0), length);
|
|
127
|
+
const remaining = length - elapsed;
|
|
128
|
+
const frozenAt = endAt - frozenLength;
|
|
129
|
+
const percent = length ? elapsed / length * 100 : 0;
|
|
130
|
+
const normalPercent = length ? Math.max(Math.min(currentTime, frozenAt) - startAt, 0) / length * 100 : 0;
|
|
131
|
+
const normalInnerPercent = Math.min(100, frozenBreakpoint ? normalPercent / frozenBreakpoint : 0);
|
|
132
|
+
const frozenPercent = percent - normalPercent;
|
|
133
|
+
const frozenInnerPercent = Math.min(100, 1 - frozenBreakpoint ? frozenPercent / (1 - frozenBreakpoint) : 0);
|
|
134
|
+
const timeTravelElapsed = timeTravelCurrentValue * 60 * 1e3;
|
|
135
|
+
const timeTravelRemaining = length - timeTravelElapsed;
|
|
136
|
+
return {
|
|
137
|
+
elapsed: inTimeMachine ? timeTravelElapsed : elapsed,
|
|
138
|
+
remaining: inTimeMachine ? timeTravelRemaining : remaining,
|
|
139
|
+
frozenBreakpoint,
|
|
140
|
+
normalInnerPercent,
|
|
141
|
+
frozenInnerPercent,
|
|
142
|
+
timeTravelElapsed,
|
|
143
|
+
timeTravelRemaining,
|
|
144
|
+
supportRegen: canRegenerateRanklist(data)
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
const srkSupportedVersions = ">=0.3.0 <=0.3.12";
|
|
148
|
+
function caniuse(version) {
|
|
149
|
+
return semver.satisfies(version, srkSupportedVersions);
|
|
150
|
+
}
|
|
151
|
+
const MODAL_ANIMATION_DURATION_MS = 280;
|
|
152
|
+
const SRK_ANIMATED_MODAL_ROOT_CLASS = "srk-animated-modal-root";
|
|
153
|
+
const SRK_SCROLL_LOCK_BODY_CLASS = "srk-scrolling-effect";
|
|
154
|
+
let lastExplicitTriggerPoint = null;
|
|
155
|
+
let lastInteractionPoint = null;
|
|
156
|
+
let hasInteractionTracker = false;
|
|
157
|
+
let lockedModalCount = 0;
|
|
158
|
+
let previousBodyOverflow = "";
|
|
159
|
+
let previousBodyOverflowX = "";
|
|
160
|
+
let previousBodyOverflowY = "";
|
|
161
|
+
let previousBodyPosition = "";
|
|
162
|
+
let previousBodyTop = "";
|
|
163
|
+
let previousBodyLeft = "";
|
|
164
|
+
let previousBodyRight = "";
|
|
165
|
+
let previousBodyWidth = "";
|
|
166
|
+
let previousScrollX = 0;
|
|
167
|
+
let previousScrollY = 0;
|
|
168
|
+
let nextModalFocusScopeId = 1;
|
|
169
|
+
let modalFocusStack = [];
|
|
170
|
+
let hasModalFocusEscapeListener = false;
|
|
171
|
+
function handleModalFocusKeyDown(event) {
|
|
172
|
+
var _a, _b;
|
|
173
|
+
if (event.key !== "Escape") {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const topScope = modalFocusStack[modalFocusStack.length - 1];
|
|
177
|
+
if (!topScope) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
(_a = topScope.onEscape) == null ? void 0 : _a.call(topScope, event);
|
|
181
|
+
(_b = event.stopImmediatePropagation) == null ? void 0 : _b.call(event);
|
|
182
|
+
event.stopPropagation();
|
|
183
|
+
}
|
|
184
|
+
function ensureModalFocusEscapeListener() {
|
|
185
|
+
if (typeof document === "undefined" || hasModalFocusEscapeListener) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
document.addEventListener("keydown", handleModalFocusKeyDown);
|
|
189
|
+
hasModalFocusEscapeListener = true;
|
|
190
|
+
}
|
|
191
|
+
function ensureModalInteractionTracker() {
|
|
192
|
+
if (typeof document === "undefined" || hasInteractionTracker) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
document.addEventListener("mousedown", (event) => {
|
|
196
|
+
lastInteractionPoint = { x: event.clientX, y: event.clientY };
|
|
197
|
+
}, true);
|
|
198
|
+
document.addEventListener("touchstart", (event) => {
|
|
199
|
+
const touch = event.touches[0] || event.changedTouches[0];
|
|
200
|
+
if (!touch) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
lastInteractionPoint = { x: touch.clientX, y: touch.clientY };
|
|
204
|
+
}, { capture: true, passive: true });
|
|
205
|
+
hasInteractionTracker = true;
|
|
206
|
+
}
|
|
207
|
+
function setModalTriggerPoint(x, y, options) {
|
|
208
|
+
lastExplicitTriggerPoint = {
|
|
209
|
+
x,
|
|
210
|
+
y,
|
|
211
|
+
timestamp: Date.now(),
|
|
212
|
+
source: options == null ? void 0 : options.source,
|
|
213
|
+
context: options == null ? void 0 : options.context
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
function captureModalTriggerPointFromMouseEvent(event, options) {
|
|
217
|
+
setModalTriggerPoint(event.clientX, event.clientY, options);
|
|
218
|
+
}
|
|
219
|
+
function getRecentModalTriggerPoint(maxAgeMs = 500) {
|
|
220
|
+
if (!lastExplicitTriggerPoint) {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
if (Date.now() - lastExplicitTriggerPoint.timestamp > maxAgeMs) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
return lastExplicitTriggerPoint;
|
|
227
|
+
}
|
|
228
|
+
function getLastModalInteractionPoint() {
|
|
229
|
+
return lastInteractionPoint;
|
|
230
|
+
}
|
|
231
|
+
function resolveModalTransformOrigin(dialogElement) {
|
|
232
|
+
if (typeof window === "undefined" || !dialogElement) {
|
|
233
|
+
return {
|
|
234
|
+
origin: { x: 0, y: 0 },
|
|
235
|
+
source: "fallback",
|
|
236
|
+
explicitTriggerPoint: null,
|
|
237
|
+
globalInteractionPoint: lastInteractionPoint,
|
|
238
|
+
dialogRect: null,
|
|
239
|
+
fallbackPoint: { x: 0, y: 0 }
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
const rect = dialogElement.getBoundingClientRect();
|
|
243
|
+
const fallbackPoint = {
|
|
244
|
+
x: rect.width / 2,
|
|
245
|
+
y: Math.min(rect.height * 0.22, 96)
|
|
246
|
+
};
|
|
247
|
+
const explicitTriggerPoint = getRecentModalTriggerPoint();
|
|
248
|
+
if (explicitTriggerPoint) {
|
|
249
|
+
return {
|
|
250
|
+
origin: {
|
|
251
|
+
x: explicitTriggerPoint.x - rect.left,
|
|
252
|
+
y: explicitTriggerPoint.y - rect.top
|
|
253
|
+
},
|
|
254
|
+
source: "explicit-trigger",
|
|
255
|
+
explicitTriggerPoint,
|
|
256
|
+
globalInteractionPoint: lastInteractionPoint,
|
|
257
|
+
dialogRect: {
|
|
258
|
+
left: rect.left,
|
|
259
|
+
top: rect.top,
|
|
260
|
+
width: rect.width,
|
|
261
|
+
height: rect.height
|
|
262
|
+
},
|
|
263
|
+
fallbackPoint
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
if (!lastInteractionPoint) {
|
|
267
|
+
return {
|
|
268
|
+
origin: fallbackPoint,
|
|
269
|
+
source: "fallback",
|
|
270
|
+
explicitTriggerPoint: null,
|
|
271
|
+
globalInteractionPoint: null,
|
|
272
|
+
dialogRect: {
|
|
273
|
+
left: rect.left,
|
|
274
|
+
top: rect.top,
|
|
275
|
+
width: rect.width,
|
|
276
|
+
height: rect.height
|
|
277
|
+
},
|
|
278
|
+
fallbackPoint
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
return {
|
|
282
|
+
origin: {
|
|
283
|
+
x: lastInteractionPoint.x - rect.left,
|
|
284
|
+
y: lastInteractionPoint.y - rect.top
|
|
285
|
+
},
|
|
286
|
+
source: "global-pointer",
|
|
287
|
+
explicitTriggerPoint: null,
|
|
288
|
+
globalInteractionPoint: lastInteractionPoint,
|
|
289
|
+
dialogRect: {
|
|
290
|
+
left: rect.left,
|
|
291
|
+
top: rect.top,
|
|
292
|
+
width: rect.width,
|
|
293
|
+
height: rect.height
|
|
294
|
+
},
|
|
295
|
+
fallbackPoint
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
function activeHTMLElement() {
|
|
299
|
+
if (typeof document === "undefined") {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
return document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
303
|
+
}
|
|
304
|
+
function focusWithoutScroll(element) {
|
|
305
|
+
try {
|
|
306
|
+
element.focus({ preventScroll: true });
|
|
307
|
+
} catch {
|
|
308
|
+
element.focus();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
function registerModalFocusScope(dialogElement, options = {}) {
|
|
312
|
+
if (typeof document === "undefined" || !dialogElement) {
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
ensureModalFocusEscapeListener();
|
|
316
|
+
const existing = modalFocusStack.find((entry) => entry.dialogElement === dialogElement);
|
|
317
|
+
if (existing) {
|
|
318
|
+
existing.onEscape = options.onEscape;
|
|
319
|
+
return existing.id;
|
|
320
|
+
}
|
|
321
|
+
const hadTabIndex = dialogElement.hasAttribute("tabindex");
|
|
322
|
+
const previousTabIndex = dialogElement.getAttribute("tabindex");
|
|
323
|
+
if (!hadTabIndex) {
|
|
324
|
+
dialogElement.setAttribute("tabindex", "-1");
|
|
325
|
+
}
|
|
326
|
+
const scope = {
|
|
327
|
+
id: nextModalFocusScopeId,
|
|
328
|
+
dialogElement,
|
|
329
|
+
restoreElement: activeHTMLElement(),
|
|
330
|
+
hadTabIndex,
|
|
331
|
+
previousTabIndex,
|
|
332
|
+
onEscape: options.onEscape
|
|
333
|
+
};
|
|
334
|
+
nextModalFocusScopeId += 1;
|
|
335
|
+
modalFocusStack.push(scope);
|
|
336
|
+
focusWithoutScroll(dialogElement);
|
|
337
|
+
return scope.id;
|
|
338
|
+
}
|
|
339
|
+
function unregisterModalFocusScope(scopeId) {
|
|
340
|
+
var _a;
|
|
341
|
+
if (typeof document === "undefined" || scopeId == null) {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
const index = modalFocusStack.findIndex((entry) => entry.id === scopeId);
|
|
345
|
+
if (index === -1) {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
const [scope] = modalFocusStack.splice(index, 1);
|
|
349
|
+
if (scope.hadTabIndex) {
|
|
350
|
+
if (scope.previousTabIndex === null) {
|
|
351
|
+
scope.dialogElement.removeAttribute("tabindex");
|
|
352
|
+
} else {
|
|
353
|
+
scope.dialogElement.setAttribute("tabindex", scope.previousTabIndex);
|
|
354
|
+
}
|
|
355
|
+
} else {
|
|
356
|
+
scope.dialogElement.removeAttribute("tabindex");
|
|
357
|
+
}
|
|
358
|
+
const wasTopScope = index === modalFocusStack.length;
|
|
359
|
+
if (!wasTopScope) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const nextTopScope = modalFocusStack[modalFocusStack.length - 1];
|
|
363
|
+
const restoreElement = ((_a = scope.restoreElement) == null ? void 0 : _a.isConnected) ? scope.restoreElement : nextTopScope == null ? void 0 : nextTopScope.dialogElement;
|
|
364
|
+
if (restoreElement) {
|
|
365
|
+
focusWithoutScroll(restoreElement);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
function isTopModalFocusScope(scopeId) {
|
|
369
|
+
var _a;
|
|
370
|
+
if (scopeId == null) {
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
return ((_a = modalFocusStack[modalFocusStack.length - 1]) == null ? void 0 : _a.id) === scopeId;
|
|
374
|
+
}
|
|
375
|
+
function lockModalBodyScroll() {
|
|
376
|
+
if (typeof document === "undefined") {
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
if (lockedModalCount === 0) {
|
|
380
|
+
previousScrollX = window.scrollX;
|
|
381
|
+
previousScrollY = window.scrollY;
|
|
382
|
+
previousBodyOverflow = document.body.style.overflow;
|
|
383
|
+
previousBodyOverflowX = document.body.style.overflowX;
|
|
384
|
+
previousBodyOverflowY = document.body.style.overflowY;
|
|
385
|
+
previousBodyPosition = document.body.style.position;
|
|
386
|
+
previousBodyTop = document.body.style.top;
|
|
387
|
+
previousBodyLeft = document.body.style.left;
|
|
388
|
+
previousBodyRight = document.body.style.right;
|
|
389
|
+
previousBodyWidth = document.body.style.width;
|
|
390
|
+
document.body.classList.add(SRK_SCROLL_LOCK_BODY_CLASS);
|
|
391
|
+
document.body.style.overflow = "hidden";
|
|
392
|
+
document.body.style.overflowX = "hidden";
|
|
393
|
+
document.body.style.overflowY = "hidden";
|
|
394
|
+
document.body.style.position = "fixed";
|
|
395
|
+
document.body.style.top = `-${previousScrollY}px`;
|
|
396
|
+
document.body.style.left = `-${previousScrollX}px`;
|
|
397
|
+
document.body.style.right = "0";
|
|
398
|
+
document.body.style.width = "100%";
|
|
399
|
+
}
|
|
400
|
+
lockedModalCount += 1;
|
|
401
|
+
}
|
|
402
|
+
function unlockModalBodyScroll() {
|
|
403
|
+
if (typeof document === "undefined" || lockedModalCount === 0) {
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
lockedModalCount -= 1;
|
|
407
|
+
if (lockedModalCount === 0) {
|
|
408
|
+
document.body.classList.remove(SRK_SCROLL_LOCK_BODY_CLASS);
|
|
409
|
+
document.body.style.overflow = previousBodyOverflow;
|
|
410
|
+
document.body.style.overflowX = previousBodyOverflowX;
|
|
411
|
+
document.body.style.overflowY = previousBodyOverflowY;
|
|
412
|
+
document.body.style.position = previousBodyPosition;
|
|
413
|
+
document.body.style.top = previousBodyTop;
|
|
414
|
+
document.body.style.left = previousBodyLeft;
|
|
415
|
+
document.body.style.right = previousBodyRight;
|
|
416
|
+
document.body.style.width = previousBodyWidth;
|
|
417
|
+
const isJsdom = typeof navigator !== "undefined" && /jsdom/i.test(navigator.userAgent);
|
|
418
|
+
if (!isJsdom && typeof window.scrollTo === "function") {
|
|
419
|
+
try {
|
|
420
|
+
window.scrollTo(previousScrollX, previousScrollY);
|
|
421
|
+
} catch {
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
function resetModalInteractionStateForTests() {
|
|
427
|
+
lastExplicitTriggerPoint = null;
|
|
428
|
+
lastInteractionPoint = null;
|
|
429
|
+
lockedModalCount = 0;
|
|
430
|
+
nextModalFocusScopeId = 1;
|
|
431
|
+
modalFocusStack = [];
|
|
432
|
+
previousBodyOverflow = "";
|
|
433
|
+
previousBodyOverflowX = "";
|
|
434
|
+
previousBodyOverflowY = "";
|
|
435
|
+
previousBodyPosition = "";
|
|
436
|
+
previousBodyTop = "";
|
|
437
|
+
previousBodyLeft = "";
|
|
438
|
+
previousBodyRight = "";
|
|
439
|
+
previousBodyWidth = "";
|
|
440
|
+
previousScrollX = 0;
|
|
441
|
+
previousScrollY = 0;
|
|
442
|
+
if (typeof document !== "undefined") {
|
|
443
|
+
document.body.classList.remove(SRK_SCROLL_LOCK_BODY_CLASS);
|
|
444
|
+
document.body.style.overflow = "";
|
|
445
|
+
document.body.style.overflowX = "";
|
|
446
|
+
document.body.style.overflowY = "";
|
|
447
|
+
document.body.style.position = "";
|
|
448
|
+
document.body.style.top = "";
|
|
449
|
+
document.body.style.left = "";
|
|
450
|
+
document.body.style.right = "";
|
|
451
|
+
document.body.style.width = "";
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
export { MODAL_ANIMATION_DURATION_MS, SRK_ANIMATED_MODAL_ROOT_CLASS, SRK_SCROLL_LOCK_BODY_CLASS, buildSliderMarks, caniuse, captureModalTriggerPointFromMouseEvent, defaultBackgroundColor, ensureModalInteractionTracker, formatSolutionTimestamp, getAcceptedStatusDetails, getLastModalInteractionPoint, getMarkerPresentation, getProblemHeaderBackgroundImage, getProgressDurationMinutes, getProgressMaxAvailableMinutes, getProgressMetrics, getRecentModalTriggerPoint, getSolutionModalTitle, getSolutionResultMeta, isProgressEnded, isTopModalFocusScope, lockModalBodyScroll, registerModalFocusScope, resetModalInteractionStateForTests, resolveModalTransformOrigin, resolveSrkAssetUrl, setModalTriggerPoint, shouldShowTimeColumn, srkSupportedVersions, unlockModalBodyScroll, unregisterModalFocusScope };
|
package/dist/modal.d.ts
ADDED
|
@@ -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,21 @@
|
|
|
1
|
+
import type * as srk from '@algoux/standard-ranklist';
|
|
2
|
+
import { EnumTheme } from '@algoux/standard-ranklist-utils';
|
|
3
|
+
export declare const defaultBackgroundColor: Record<EnumTheme, string>;
|
|
4
|
+
export interface MarkerPresentation {
|
|
5
|
+
className?: string;
|
|
6
|
+
style?: {
|
|
7
|
+
backgroundColor?: string;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export interface SolutionResultMeta {
|
|
11
|
+
label: string;
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function resolveSrkAssetUrl(url: string, field: string, formatter?: (url: string, field: string) => string): string;
|
|
15
|
+
export declare function getProblemHeaderBackgroundImage(style: srk.Style | undefined, theme: EnumTheme): string;
|
|
16
|
+
export declare function getMarkerPresentation(marker: srk.Marker, theme: EnumTheme): MarkerPresentation;
|
|
17
|
+
export declare function getSolutionResultMeta(result: srk.Solution['result']): SolutionResultMeta;
|
|
18
|
+
export declare function getAcceptedStatusDetails(status: srk.RankProblemStatus): string;
|
|
19
|
+
export declare function getSolutionModalTitle(problemIndex: number, user: srk.User): string;
|
|
20
|
+
export declare function formatSolutionTimestamp(solution: srk.Solution): string;
|
|
21
|
+
export declare function shouldShowTimeColumn(rows: srk.RanklistRow[]): boolean;
|
package/dist/types.d.ts
ADDED
|
@@ -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
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@algoux/standard-ranklist-renderer-component-core",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Core helpers and shared types for Standard Ranklist Renderer Component",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"main": "./dist/index.cjs",
|
|
10
|
+
"module": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"require": "./dist/index.cjs",
|
|
17
|
+
"default": "./dist/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public",
|
|
23
|
+
"registry": "https://registry.npmjs.org/"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@algoux/standard-ranklist-utils": "^0.2.11",
|
|
27
|
+
"color": "^4.2.3",
|
|
28
|
+
"semver": "^7.3.7"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"@algoux/standard-ranklist": "*"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "vite build --config vite.config.ts"
|
|
35
|
+
}
|
|
36
|
+
}
|