@xcpcio/board-app 0.53.2 → 0.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/README.md +3 -3
  2. package/dist/404.html +1 -1
  3. package/dist/assets/{Balloon.vue_vue_type_script_setup_true_lang-CNaNh1cd.js → Balloon.vue_vue_type_script_setup_true_lang-4AVGJCMI.js} +1 -1
  4. package/dist/assets/Board-DI2_Xyh3.css +1 -0
  5. package/dist/assets/Board-DpMNDnwb.js +1 -0
  6. package/dist/assets/{ContestStateBadge-fTJw-IjF.js → ContestStateBadge-Dhbid94g.js} +1 -1
  7. package/dist/assets/Countdown-BvdJWWuk.js +1 -0
  8. package/dist/assets/DataSourceInput.vue_vue_type_script_setup_true_lang-BdYOS7Pt.js +1 -0
  9. package/dist/assets/{Footer.vue_vue_type_script_setup_true_lang-Bk0nFBfB.js → Footer.vue_vue_type_script_setup_true_lang-DoBaeAhT.js} +1 -1
  10. package/dist/assets/{NavBar-C6sQ5txU.js → NavBar-B9KggQU-.js} +1 -1
  11. package/dist/assets/Resolver-B6m5f_LD.css +1 -0
  12. package/dist/assets/Resolver-CxzATvRW.js +25 -0
  13. package/dist/assets/{RightArrowIcon-ClOVVo1B.js → RightArrowIcon-UrcrWsjs.js} +1 -1
  14. package/dist/assets/{TheInput.vue_vue_type_script_setup_true_lang-D5spkHqx.js → TheInput.vue_vue_type_script_setup_true_lang-Fq7AIk4r.js} +1 -1
  15. package/dist/assets/Tooltip.vue_vue_type_script_setup_true_lang-C9T_K5RC.js +27 -0
  16. package/dist/assets/_...all_-CpbyYPnu.js +1 -0
  17. package/dist/assets/{_...all_-BSvAEkM-.js → _...all_-DizdgePm.js} +2 -2
  18. package/dist/assets/_name_-CyHYBF6H.js +1 -0
  19. package/dist/assets/{about-tPNAra1j.js → about-CfqrcwJT.js} +1 -1
  20. package/dist/assets/board-layout-DDF2wubP.js +1 -0
  21. package/dist/assets/board-vgh1WXMi.js +1 -0
  22. package/dist/assets/{constant-b2ZZlr_Y.js → constant-CKMKwY_M.js} +1 -1
  23. package/dist/assets/default-REGvX7tC.js +1 -0
  24. package/dist/assets/{en-DDlXf_8q.js → en-Bjc9Zdq9.js} +1 -1
  25. package/dist/assets/headless-DedxMF3x.js +1 -0
  26. package/dist/assets/home-DSppcc6C.js +1 -0
  27. package/dist/assets/index-B8vw3ErJ.js +198 -0
  28. package/dist/assets/{index-DQYGiW5A.js → index-BZdyM9Qh.js} +1 -1
  29. package/dist/assets/index-Bx4d8xqO.js +1 -0
  30. package/dist/assets/{index-XjAdtRAz.js → index-CaB6yvAO.js} +1 -1
  31. package/dist/assets/index-Cc2LPl6E.js +50 -0
  32. package/dist/assets/{index-BP90wE2A.css → index-CkWanoDO.css} +1 -1
  33. package/dist/assets/index-DLur0B0J.css +5 -0
  34. package/dist/assets/index-LvyoMonD.js +1 -0
  35. package/dist/assets/index-Y1YIGHJj.js +1 -0
  36. package/dist/assets/index-Yf5aNxDp.js +1 -0
  37. package/dist/assets/{index-BnOiKE-V.js → index-_3n9Ahv3.js} +2 -2
  38. package/dist/assets/index-layout-qLKsk6Zm.js +1 -0
  39. package/dist/assets/pagination-zmuISHg-.js +3 -0
  40. package/dist/assets/query-DymhAP9l.js +1 -0
  41. package/dist/assets/{test-Bw2mUsPy.js → test-C8r9tN4s.js} +1 -1
  42. package/dist/assets/{use-vmodel-BKVjZ-1P.js → use-vmodel-BusQKjfF.js} +1 -1
  43. package/dist/assets/{user-o9hs8HNN.js → user-B1iInd_U.js} +1 -1
  44. package/dist/assets/{virtual_pwa-register-8shrxAmh.js → virtual_pwa-register-Bw1WWEdI.js} +1 -1
  45. package/dist/assets/{workbox-window.prod.es5-D5gOYdM7.js → workbox-window.prod.es5-B9K5rw8f.js} +2 -2
  46. package/dist/assets/{zh-CN-CNJkghp4.js → zh-CN-BgAN9IOl.js} +1 -1
  47. package/dist/index.html +1 -1
  48. package/dist/manifest.webmanifest +1 -1
  49. package/dist/sw.js +1 -1
  50. package/dist/{workbox-86deb690.js → workbox-7ee6f867.js} +1 -1
  51. package/package.json +49 -51
  52. package/src/auto-imports.d.ts +21 -2
  53. package/src/components/Resolver.vue +1 -1
  54. package/src/components/ResolverTeamRow.vue +6 -14
  55. package/src/components/battle-of-giants/GiantsOptions.vue +2 -1
  56. package/src/components/board/AnimatedSubmissionBlock.vue +2 -2
  57. package/src/components/board/AnimatedSubmissionsModal.vue +2 -2
  58. package/src/components/board/Board.vue +4 -3
  59. package/src/components/board/HeatMapTooltip.less +115 -0
  60. package/src/components/board/HeatMapTooltip.vue +68 -0
  61. package/src/components/board/Progress.less +15 -9
  62. package/src/components/board/Statistics.vue +6 -0
  63. package/src/components/board/SubmitHeatMap.less +13 -0
  64. package/src/components/board/SubmitHeatMap.vue +226 -0
  65. package/src/components/flowbite/Tooltip.css +25 -23
  66. package/src/components/rating/rating.less +5 -1
  67. package/src/components.d.ts +3 -0
  68. package/src/composables/constant.ts +1 -0
  69. package/src/composables/statistics.ts +2 -1
  70. package/src/modules/i18n.ts +1 -1
  71. package/src/modules/nprogress.ts +1 -1
  72. package/src/modules/pinia.ts +1 -1
  73. package/src/modules/toast.ts +1 -1
  74. package/src/styles/color.css +5 -5
  75. package/src/styles/main.css +4 -4
  76. package/src/styles/markdown.css +1 -1
  77. package/src/styles/submission-status-background.css +0 -4
  78. package/src/styles/submission-status-filter.css +0 -4
  79. package/src/styles/submission-status.css +31 -34
  80. package/.eslintrc.json +0 -6
  81. package/dist/assets/Board-D7W2MZ4t.js +0 -1
  82. package/dist/assets/Board-_AOcbImO.css +0 -1
  83. package/dist/assets/Countdown-1A-2QJ7m.js +0 -1
  84. package/dist/assets/DataSourceInput.vue_vue_type_script_setup_true_lang-CXts-riZ.js +0 -1
  85. package/dist/assets/Resolver-A6qSPE_R.js +0 -25
  86. package/dist/assets/Resolver-DRiWpQTB.css +0 -1
  87. package/dist/assets/Tooltip.vue_vue_type_script_setup_true_lang-FgOX36Lq.js +0 -27
  88. package/dist/assets/_...all_-DmfbP4Ao.js +0 -1
  89. package/dist/assets/_name_-AmLA_ZW6.js +0 -1
  90. package/dist/assets/board-D0VTebOf.js +0 -1
  91. package/dist/assets/board-layout-C2v-fo1W.js +0 -1
  92. package/dist/assets/default-D4907N0j.js +0 -1
  93. package/dist/assets/headless-C_cFBCtG.js +0 -1
  94. package/dist/assets/home-CRZ6rGwZ.js +0 -1
  95. package/dist/assets/index-BjCWriHl.css +0 -5
  96. package/dist/assets/index-Bvki346r.js +0 -1
  97. package/dist/assets/index-CVZtA3I-.js +0 -50
  98. package/dist/assets/index-ClcpF-3G.js +0 -1
  99. package/dist/assets/index-D5Eq7tNN.js +0 -1
  100. package/dist/assets/index-Dar9HUtt.js +0 -198
  101. package/dist/assets/index-cu4LZbd6.js +0 -1
  102. package/dist/assets/index-layout-Bp7ERlAB.js +0 -1
  103. package/dist/assets/pagination-CHOfWwrE.js +0 -3
  104. package/dist/assets/query-C-pLpLZb.js +0 -1
  105. /package/dist/assets/{ContestStateBadge-yOXRkC0c.css → ContestStateBadge-DawlTsHs.css} +0 -0
  106. /package/dist/assets/{Countdown-De6dfmWB.css → Countdown-CGwMuG9S.css} +0 -0
  107. /package/dist/assets/{NavBar-K1-1c5jR.css → NavBar-C0cqpBnK.css} +0 -0
  108. /package/dist/assets/{Tooltip-VVqtpO6L.css → Tooltip-DzyPDwbo.css} +0 -0
  109. /package/dist/assets/{_..-8Wh-Y8sB.css → _..-5I0UWqRG.css} +0 -0
  110. /package/dist/assets/{index-BerApwlM.css → index-D0wLFbYO.css} +0 -0
@@ -0,0 +1,115 @@
1
+ .tooltip-container {
2
+ position: relative;
3
+ display: inline-block;
4
+ }
5
+
6
+ .tooltip {
7
+ position: absolute;
8
+ padding: 8px 12px;
9
+ border-radius: 4px;
10
+ font-size: 14px;
11
+ line-height: 1.4;
12
+ text-align: center;
13
+ opacity: 0;
14
+ visibility: hidden;
15
+ transition:
16
+ opacity 0.3s,
17
+ visibility 0.3s;
18
+ white-space: nowrap;
19
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
20
+ pointer-events: none;
21
+ background-color: #1f2937;
22
+ }
23
+
24
+ .dark.tooltip {
25
+ background-color: #374151;
26
+ }
27
+
28
+ .tooltip.visible {
29
+ opacity: 1;
30
+ visibility: visible;
31
+ }
32
+
33
+ .tooltip::after {
34
+ content: "";
35
+ position: absolute;
36
+ width: 0;
37
+ height: 0;
38
+ border: 6px solid transparent;
39
+ }
40
+
41
+ .tooltip.top {
42
+ bottom: 100%;
43
+ left: 50%;
44
+ transform: translateX(-50%) translateY(-5px);
45
+ margin-bottom: 5px;
46
+ }
47
+
48
+ .tooltip.top::after {
49
+ top: 100%;
50
+ left: 50%;
51
+ transform: translateX(-50%);
52
+ border-top-color: #1f2937;
53
+ border-bottom: 0;
54
+ }
55
+
56
+ .dark.tooltip.top::after {
57
+ border-top-color: #374151;
58
+ }
59
+
60
+ .tooltip.bottom {
61
+ top: 100%;
62
+ left: 50%;
63
+ transform: translateX(-50%) translateY(5px);
64
+ margin-top: 5px;
65
+ }
66
+
67
+ .tooltip.bottom::after {
68
+ bottom: 100%;
69
+ left: 50%;
70
+ transform: translateX(-50%);
71
+ border-bottom-color: #1f2937;
72
+ border-top: 0;
73
+ }
74
+
75
+ .dark.tooltip.bottom::after {
76
+ border-bottom-color: #374151;
77
+ }
78
+
79
+ .tooltip.left {
80
+ right: 100%;
81
+ top: 50%;
82
+ transform: translateY(-50%) translateX(-5px);
83
+ margin-right: 5px;
84
+ }
85
+
86
+ .tooltip.left::after {
87
+ left: 100%;
88
+ top: 50%;
89
+ transform: translateY(-50%);
90
+ border-left-color: #1f2937;
91
+ border-right: 0;
92
+ }
93
+
94
+ .dark.tooltip.left::after {
95
+ border-left-color: #374151;
96
+ }
97
+
98
+ .tooltip.right {
99
+ left: 100%;
100
+ top: 50%;
101
+ transform: translateY(-50%) translateX(5px);
102
+ margin-left: 5px;
103
+ }
104
+
105
+ .tooltip.right::after {
106
+ right: 100%;
107
+ top: 50%;
108
+ transform: translateY(-50%);
109
+ border-right-color: #1f2937;
110
+ border-left: 0;
111
+ }
112
+
113
+ .dark.tooltip.right::after {
114
+ border-right-color: #374151;
115
+ }
@@ -0,0 +1,68 @@
1
+ <script setup lang="ts">
2
+ const props = defineProps({
3
+ content: {
4
+ type: String,
5
+ default: "",
6
+ },
7
+ position: {
8
+ type: String,
9
+ default: "top",
10
+ validator: (value: string) => ["top", "right", "bottom", "left"].includes(value),
11
+ },
12
+ delay: {
13
+ type: Number,
14
+ default: 100,
15
+ },
16
+ textColor: {
17
+ type: String,
18
+ default: "#fff",
19
+ },
20
+ width: {
21
+ type: String,
22
+ default: "auto",
23
+ },
24
+ zIndex: {
25
+ type: Number,
26
+ default: 1000,
27
+ },
28
+ });
29
+
30
+ const isVisible = ref(false);
31
+ let timeoutId: ReturnType<typeof setTimeout>;
32
+
33
+ const tooltipStyle = computed(() => ({
34
+ color: props.textColor,
35
+ width: props.width,
36
+ zIndex: props.zIndex,
37
+ }));
38
+
39
+ function showTooltip() {
40
+ clearTimeout(timeoutId);
41
+ timeoutId = setTimeout(() => {
42
+ isVisible.value = true;
43
+ }, props.delay);
44
+ }
45
+
46
+ function hideTooltip() {
47
+ clearTimeout(timeoutId);
48
+ isVisible.value = false;
49
+ }
50
+ </script>
51
+
52
+ <template>
53
+ <div class="tooltip-container" @mouseenter="showTooltip" @mouseleave="hideTooltip">
54
+ <slot />
55
+ <div
56
+ class="tooltip"
57
+ :class="[position, { visible: isVisible }]"
58
+ :style="tooltipStyle"
59
+ >
60
+ {{ content }}
61
+ <slot name="tooltip-content" />
62
+ </div>
63
+ </div>
64
+ </template>
65
+
66
+ <style scoped lang="less">
67
+ @import "./HeatMapTooltip.less";
68
+ </style>
@@ -91,12 +91,12 @@
91
91
  animation: progress-bar-stripes 2s linear infinite;
92
92
  }
93
93
 
94
- .am-progress-bar[aria-valuenow='1'],
95
- .am-progress-bar[aria-valuenow='2'] {
94
+ .am-progress-bar[aria-valuenow="1"],
95
+ .am-progress-bar[aria-valuenow="2"] {
96
96
  min-width: 30px;
97
97
  }
98
98
 
99
- .am-progress-bar[aria-valuenow='0'] {
99
+ .am-progress-bar[aria-valuenow="0"] {
100
100
  color: #999999;
101
101
  min-width: 30px;
102
102
  background: none;
@@ -336,8 +336,14 @@
336
336
  position: absolute;
337
337
  z-index: 99;
338
338
  display: block;
339
- font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
340
- Helvetica Neue, Arial, sans-serif;
339
+ font-family:
340
+ -apple-system,
341
+ BlinkMacSystemFont,
342
+ Segoe UI,
343
+ Roboto,
344
+ Helvetica Neue,
345
+ Arial,
346
+ sans-serif;
341
347
  font-style: normal;
342
348
  font-weight: 400;
343
349
  letter-spacing: normal;
@@ -371,7 +377,7 @@
371
377
  bottom: 0;
372
378
  left: 50%;
373
379
  margin-left: -5px;
374
- content: '';
380
+ content: "";
375
381
  border-width: 5px 5px 0;
376
382
  border-top-color: #000;
377
383
  }
@@ -387,7 +393,7 @@
387
393
  top: 50%;
388
394
  left: 0;
389
395
  margin-top: -5px;
390
- content: '';
396
+ content: "";
391
397
  border-width: 5px 5px 5px 0;
392
398
  border-right-color: #000;
393
399
  }
@@ -403,7 +409,7 @@
403
409
  top: 0;
404
410
  left: 50%;
405
411
  margin-left: -5px;
406
- content: '';
412
+ content: "";
407
413
  border-width: 0 5px 5px;
408
414
  border-bottom-color: #000;
409
415
  }
@@ -419,7 +425,7 @@
419
425
  top: 50%;
420
426
  right: 0;
421
427
  margin-top: -5px;
422
- content: '';
428
+ content: "";
423
429
  border-width: 5px 0 5px 5px;
424
430
  border-left-color: #000;
425
431
  }
@@ -76,6 +76,12 @@ function getHeadData() {
76
76
  mt-8 gap-8
77
77
  flex flex-col
78
78
  >
79
+ <div>
80
+ <SubmitHeatMap
81
+ :rank="rank"
82
+ />
83
+ </div>
84
+
79
85
  <div>
80
86
  <Chart
81
87
  :options="getSubmitChart(rank)"
@@ -0,0 +1,13 @@
1
+ @correct-colors: #9be9a8, #40c463, #30a14e, #216e39;
2
+ @incorrect-colors: #ff8c8c, #ff4d4f, #ff1a1a, #d40d0d;
3
+
4
+ .generate-heat-rules(@class, @colors) {
5
+ each(@colors, {
6
+ .@{class}[data-level="@{index}"] {
7
+ background-color: @value;
8
+ }
9
+ });
10
+ }
11
+
12
+ .generate-heat-rules(correct-heat, @correct-colors);
13
+ .generate-heat-rules(incorrect-heat, @incorrect-colors);
@@ -0,0 +1,226 @@
1
+ <script setup lang="ts">
2
+ import type { Rank } from "@xcpcio/core";
3
+ import { isAccepted, isRejected } from "@xcpcio/core";
4
+
5
+ const props = defineProps<{
6
+ rank: Rank;
7
+ }>();
8
+
9
+ const { t } = useI18n();
10
+
11
+ const rank = computed(() => props.rank);
12
+
13
+ const mapTimeDiffLength = 20;
14
+ const startTime = rank.value.contest.getStartTime();
15
+ const endTime = rank.value.contest.getEndTime();
16
+ const totalDuration = endTime.diff(startTime, "minute");
17
+ const intervalSize = totalDuration / mapTimeDiffLength;
18
+
19
+ const submissions = computed(() => rank.value.getSubmissions());
20
+
21
+ const intervalDescriptions = Array.from({ length: mapTimeDiffLength }, (_, index) => {
22
+ const start = index * intervalSize;
23
+ const end = (index + 1) * intervalSize;
24
+ return `Between ${start} and ${end} minutes: `;
25
+ });
26
+
27
+ const submissionsByProblemId = computed(() => {
28
+ const result = new Map();
29
+
30
+ rank.value.contest.problems.forEach((p) => {
31
+ result.set(p.id, { correct: [], incorrect: [] });
32
+ });
33
+
34
+ submissions.value.forEach((s) => {
35
+ const entry = result.get(s.problemId);
36
+ if (!entry) {
37
+ return;
38
+ }
39
+
40
+ if (isAccepted(s.status)) {
41
+ entry.correct.push(s);
42
+ } else if (isRejected(s.status)) {
43
+ entry.incorrect.push(s);
44
+ }
45
+ });
46
+
47
+ return result;
48
+ });
49
+
50
+ function generateHeatMap(submissions: Map<any, any>, type: "correct" | "incorrect") {
51
+ const counts = Array.from<number>({ length: mapTimeDiffLength }).fill(0);
52
+
53
+ submissions.forEach((s) => {
54
+ const index = Math.min(Math.floor(s.timestampToMinute / intervalSize), mapTimeDiffLength - 1);
55
+ counts[index]++;
56
+ });
57
+
58
+ return counts.map((count, i) => ({
59
+ count,
60
+ level: 0,
61
+ description: `${intervalDescriptions[i]}${count} ${type} submissions`,
62
+ }));
63
+ }
64
+
65
+ function calcPreciseQuan(sortedValues: number[], p: number): number {
66
+ if (sortedValues.length === 0) {
67
+ return 0;
68
+ }
69
+
70
+ const position = (sortedValues.length - 1) * p;
71
+ const index = Math.floor(position);
72
+ const fraction = position - index;
73
+
74
+ if (sortedValues[index + 1] !== undefined) {
75
+ return sortedValues[index] + fraction * (sortedValues[index + 1] - sortedValues[index]);
76
+ }
77
+
78
+ return sortedValues[index];
79
+ }
80
+
81
+ function calcStaticThresholds(counts: number[]) {
82
+ const nonZeroCounts = counts.filter(c => c > 0);
83
+
84
+ if (nonZeroCounts.length === 0) {
85
+ return [0, 0, 0, 0, 0];
86
+ }
87
+
88
+ const sorted = [...nonZeroCounts].sort((a, b) => a - b);
89
+
90
+ return [
91
+ 0,
92
+ calcPreciseQuan(sorted, 0.2),
93
+ calcPreciseQuan(sorted, 0.4),
94
+ calcPreciseQuan(sorted, 0.6),
95
+ calcPreciseQuan(sorted, 0.8),
96
+ ];
97
+ }
98
+
99
+ function getDynamicHeatLevel(value: number, thresholds: number[]): number {
100
+ for (let i = thresholds.length - 1; i >= 0; i--) {
101
+ if (value === 0) {
102
+ return 0;
103
+ } else if (value >= thresholds[i]) {
104
+ return i;
105
+ }
106
+ }
107
+ return 0;
108
+ }
109
+
110
+ const heatMapData = computed(() =>
111
+ rank.value.contest.problems.map((p) => {
112
+ const { correct, incorrect } = submissionsByProblemId.value.get(p.id)!;
113
+
114
+ const correctHeatMap = generateHeatMap(correct, "correct");
115
+ const incorrectHeatMap = generateHeatMap(incorrect, "incorrect");
116
+
117
+ const correctCounts = correctHeatMap.map(i => i.count);
118
+ const correctThresholds = calcStaticThresholds(correctCounts);
119
+
120
+ const incorrectCounts = incorrectHeatMap.map(i => i.count);
121
+ const incorrectThresholds = calcStaticThresholds(incorrectCounts);
122
+
123
+ correctHeatMap.forEach(i => i.level = getDynamicHeatLevel(i.count, correctThresholds));
124
+ incorrectHeatMap.forEach(i => i.level = getDynamicHeatLevel(i.count, incorrectThresholds));
125
+
126
+ return {
127
+ label: p.label,
128
+ balloonColor: p.balloonColor,
129
+ correctHeatMap,
130
+ incorrectHeatMap,
131
+ };
132
+ }),
133
+ );
134
+ </script>
135
+
136
+ <template>
137
+ <div flex flex-col mb-8>
138
+ <span
139
+ text-align-center
140
+ text-size-lg
141
+ font-semibold
142
+ mb-8
143
+ >
144
+ {{ t("standings.statistics.submit_heatmap") }}
145
+ </span>
146
+
147
+ <div
148
+ grid grid-cols-3
149
+ gap-10 space-y-0
150
+ place-items-center
151
+ >
152
+ <div
153
+ v-for="heatMap in heatMapData" :key="heatMap.label"
154
+ class="w-350px h-100px"
155
+ flex flex-col
156
+ items-center justify-center
157
+ border border-gray-100 dark:border-gray-600
158
+ rounded-md
159
+ shadow
160
+ >
161
+ <div
162
+ mb-1
163
+ size-30px
164
+ flex
165
+ justify-center items-center
166
+ rounded-md
167
+ :style="{ backgroundColor: heatMap.balloonColor.background_color, color: heatMap.balloonColor.color }"
168
+ >
169
+ <span text-size-lg font-semibold>
170
+ {{ heatMap.label }}
171
+ </span>
172
+ </div>
173
+
174
+ <div
175
+ flex flex-row
176
+ gap-1
177
+ >
178
+ <div
179
+ v-for="(correctItem, index) in heatMap.correctHeatMap" :key="`correct-${index}`"
180
+ >
181
+ <HeatMapTooltip
182
+ :content="correctItem.description"
183
+ position="top"
184
+ >
185
+ <div
186
+ :data-level="correctItem.level"
187
+ class="correct-heat"
188
+ size-12px
189
+ rounded-1
190
+ shadow
191
+ border border-gray-100 dark:border-gray-600
192
+ />
193
+ </HeatMapTooltip>
194
+ </div>
195
+ </div>
196
+
197
+ <div
198
+ flex flex-row
199
+ gap-1
200
+ >
201
+ <div
202
+ v-for="(incorrectItem, index) in heatMap.incorrectHeatMap" :key="`incorrect-${index}`"
203
+ >
204
+ <HeatMapTooltip
205
+ :content="incorrectItem.description"
206
+ position="top"
207
+ >
208
+ <div
209
+ :data-level="incorrectItem.level"
210
+ class="incorrect-heat"
211
+ size-12px
212
+ rounded-1
213
+ shadow
214
+ border border-gray-100 dark:border-gray-600
215
+ />
216
+ </HeatMapTooltip>
217
+ </div>
218
+ </div>
219
+ </div>
220
+ </div>
221
+ </div>
222
+ </template>
223
+
224
+ <style scoped lang="less">
225
+ @import "./SubmitHeatMap.less";
226
+ </style>
@@ -1,4 +1,5 @@
1
- .tooltip-arrow,.tooltip-arrow:before {
1
+ .tooltip-arrow,
2
+ .tooltip-arrow:before {
2
3
  position: absolute;
3
4
  width: 8px;
4
5
  height: 8px;
@@ -15,44 +16,44 @@
15
16
  transform: rotate(45deg);
16
17
  }
17
18
 
18
- [data-tooltip-style^='light'] + .tooltip > .tooltip-arrow:before {
19
+ [data-tooltip-style^="light"] + .tooltip > .tooltip-arrow:before {
19
20
  border-style: solid;
20
21
  border-color: #e5e7eb;
21
22
  }
22
23
 
23
- [data-tooltip-style^='light'] + .tooltip[data-popper-placement^='top'] > .tooltip-arrow:before {
24
+ [data-tooltip-style^="light"] + .tooltip[data-popper-placement^="top"] > .tooltip-arrow:before {
24
25
  border-bottom-width: 1px;
25
26
  border-right-width: 1px;
26
27
  }
27
28
 
28
- [data-tooltip-style^='light'] + .tooltip[data-popper-placement^='right'] > .tooltip-arrow:before {
29
+ [data-tooltip-style^="light"] + .tooltip[data-popper-placement^="right"] > .tooltip-arrow:before {
29
30
  border-bottom-width: 1px;
30
31
  border-left-width: 1px;
31
32
  }
32
33
 
33
- [data-tooltip-style^='light'] + .tooltip[data-popper-placement^='bottom'] > .tooltip-arrow:before {
34
+ [data-tooltip-style^="light"] + .tooltip[data-popper-placement^="bottom"] > .tooltip-arrow:before {
34
35
  border-top-width: 1px;
35
36
  border-left-width: 1px;
36
37
  }
37
38
 
38
- [data-tooltip-style^='light'] + .tooltip[data-popper-placement^='left'] > .tooltip-arrow:before {
39
+ [data-tooltip-style^="light"] + .tooltip[data-popper-placement^="left"] > .tooltip-arrow:before {
39
40
  border-top-width: 1px;
40
41
  border-right-width: 1px;
41
42
  }
42
43
 
43
- .tooltip[data-popper-placement^='top'] > .tooltip-arrow {
44
+ .tooltip[data-popper-placement^="top"] > .tooltip-arrow {
44
45
  bottom: -4px;
45
46
  }
46
47
 
47
- .tooltip[data-popper-placement^='bottom'] > .tooltip-arrow {
48
+ .tooltip[data-popper-placement^="bottom"] > .tooltip-arrow {
48
49
  top: -4px;
49
50
  }
50
51
 
51
- .tooltip[data-popper-placement^='left'] > .tooltip-arrow {
52
+ .tooltip[data-popper-placement^="left"] > .tooltip-arrow {
52
53
  right: -4px;
53
54
  }
54
55
 
55
- .tooltip[data-popper-placement^='right'] > .tooltip-arrow {
56
+ .tooltip[data-popper-placement^="right"] > .tooltip-arrow {
56
57
  left: -4px;
57
58
  }
58
59
 
@@ -60,7 +61,8 @@
60
61
  visibility: hidden;
61
62
  }
62
63
 
63
- [data-popper-arrow],[data-popper-arrow]:before {
64
+ [data-popper-arrow],
65
+ [data-popper-arrow]:before {
64
66
  position: absolute;
65
67
  width: 8px;
66
68
  height: 8px;
@@ -107,59 +109,59 @@
107
109
  border-color: #4b5563;
108
110
  }
109
111
 
110
- [data-popover][role="tooltip"][data-popper-placement^='top'] > [data-popper-arrow]:before {
112
+ [data-popover][role="tooltip"][data-popper-placement^="top"] > [data-popper-arrow]:before {
111
113
  border-bottom-width: 1px;
112
114
  border-right-width: 1px;
113
115
  }
114
116
 
115
- [data-popover][role="tooltip"][data-popper-placement^='top'] > [data-popper-arrow]:after {
117
+ [data-popover][role="tooltip"][data-popper-placement^="top"] > [data-popper-arrow]:after {
116
118
  border-bottom-width: 1px;
117
119
  border-right-width: 1px;
118
120
  }
119
121
 
120
- [data-popover][role="tooltip"][data-popper-placement^='right'] > [data-popper-arrow]:before {
122
+ [data-popover][role="tooltip"][data-popper-placement^="right"] > [data-popper-arrow]:before {
121
123
  border-bottom-width: 1px;
122
124
  border-left-width: 1px;
123
125
  }
124
126
 
125
- [data-popover][role="tooltip"][data-popper-placement^='right'] > [data-popper-arrow]:after {
127
+ [data-popover][role="tooltip"][data-popper-placement^="right"] > [data-popper-arrow]:after {
126
128
  border-bottom-width: 1px;
127
129
  border-left-width: 1px;
128
130
  }
129
131
 
130
- [data-popover][role="tooltip"][data-popper-placement^='bottom'] > [data-popper-arrow]:before {
132
+ [data-popover][role="tooltip"][data-popper-placement^="bottom"] > [data-popper-arrow]:before {
131
133
  border-top-width: 1px;
132
134
  border-left-width: 1px;
133
135
  }
134
136
 
135
- [data-popover][role="tooltip"][data-popper-placement^='bottom'] > [data-popper-arrow]:after {
137
+ [data-popover][role="tooltip"][data-popper-placement^="bottom"] > [data-popper-arrow]:after {
136
138
  border-top-width: 1px;
137
139
  border-left-width: 1px;
138
140
  }
139
141
 
140
- [data-popover][role="tooltip"][data-popper-placement^='left'] > [data-popper-arrow]:before {
142
+ [data-popover][role="tooltip"][data-popper-placement^="left"] > [data-popper-arrow]:before {
141
143
  border-top-width: 1px;
142
144
  border-right-width: 1px;
143
145
  }
144
146
 
145
- [data-popover][role="tooltip"][data-popper-placement^='left'] > [data-popper-arrow]:after {
147
+ [data-popover][role="tooltip"][data-popper-placement^="left"] > [data-popper-arrow]:after {
146
148
  border-top-width: 1px;
147
149
  border-right-width: 1px;
148
150
  }
149
151
 
150
- [data-popover][role="tooltip"][data-popper-placement^='top'] > [data-popper-arrow] {
152
+ [data-popover][role="tooltip"][data-popper-placement^="top"] > [data-popper-arrow] {
151
153
  bottom: -5px;
152
154
  }
153
155
 
154
- [data-popover][role="tooltip"][data-popper-placement^='bottom'] > [data-popper-arrow] {
156
+ [data-popover][role="tooltip"][data-popper-placement^="bottom"] > [data-popper-arrow] {
155
157
  top: -5px;
156
158
  }
157
159
 
158
- [data-popover][role="tooltip"][data-popper-placement^='left'] > [data-popper-arrow] {
160
+ [data-popover][role="tooltip"][data-popper-placement^="left"] > [data-popper-arrow] {
159
161
  right: -5px;
160
162
  }
161
163
 
162
- [data-popover][role="tooltip"][data-popper-placement^='right'] > [data-popper-arrow] {
164
+ [data-popover][role="tooltip"][data-popper-placement^="right"] > [data-popper-arrow] {
163
165
  left: -5px;
164
166
  }
165
167
 
@@ -4,7 +4,11 @@
4
4
  }
5
5
 
6
6
  .rated-user {
7
- font-family: helvetica neue, Helvetica, Arial, sans-serif;
7
+ font-family:
8
+ helvetica neue,
9
+ Helvetica,
10
+ Arial,
11
+ sans-serif;
8
12
  text-decoration: none;
9
13
  font-weight: 700;
10
14
  display: inline-block;
@@ -2,6 +2,7 @@
2
2
  // @ts-nocheck
3
3
  // Generated by unplugin-vue-components
4
4
  // Read more: https://github.com/vuejs/core/pull/3399
5
+ // biome-ignore lint: disable
5
6
  export {}
6
7
 
7
8
  /* prettier-ignore */
@@ -29,6 +30,7 @@ declare module 'vue' {
29
30
  GiantsScoreBoard: typeof import('./components/battle-of-giants/GiantsScoreBoard.vue')['default']
30
31
  GirlIcon: typeof import('./components/icon/GirlIcon.vue')['default']
31
32
  GoBack: typeof import('./components/GoBack.vue')['default']
33
+ HeatMapTooltip: typeof import('./components/board/HeatMapTooltip.vue')['default']
32
34
  Modal: typeof import('./components/board/Modal.vue')['default']
33
35
  ModalMenu: typeof import('./components/board/ModalMenu.vue')['default']
34
36
  NavBar: typeof import('./components/NavBar.vue')['default']
@@ -56,6 +58,7 @@ declare module 'vue' {
56
58
  Statistics: typeof import('./components/board/Statistics.vue')['default']
57
59
  SubmissionsTable: typeof import('./components/board/SubmissionsTable.vue')['default']
58
60
  SubmissionsTableModal: typeof import('./components/board/SubmissionsTableModal.vue')['default']
61
+ SubmitHeatMap: typeof import('./components/board/SubmitHeatMap.vue')['default']
59
62
  TablePagination: typeof import('./components/table/TablePagination.vue')['default']
60
63
  TeamAwards: typeof import('./components/board/TeamAwards.vue')['default']
61
64
  TeamInfoModal: typeof import('./components/board/TeamInfoModal.vue')['default']
@@ -3,6 +3,7 @@ export const RATING_TITLE_SUFFIX = "Rating - XCPCIO";
3
3
  export const BALLOON_TITLE_SUFFIX = "Balloon - XCPCIO";
4
4
  export const RESOLVER_TITLE_SUFFIX = "Resolver - XCPCIO";
5
5
  export const COUNTDOWN_TITLE_SUFFIX = "Countdown - XCPCIO";
6
+ export const SUBMISSION_TITLE_SUFFIX = "Submission - XCPCIO";
6
7
 
7
8
  export const CDN_HOST = computed(() => {
8
9
  if (!window) {