@xcpcio/board-app 0.84.0 → 0.85.3

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 (101) hide show
  1. package/dist/404.html +1 -1
  2. package/dist/assets/Balloon-B3cPN2yD.js +1 -0
  3. package/dist/assets/Board-BpIhDrix.css +1 -0
  4. package/dist/assets/Board-CWwqX2IB.js +190 -0
  5. package/dist/assets/ContestStateBadge-CQOaPmT8.js +1 -0
  6. package/dist/assets/ContestStateBadge-DgwznQrX.css +1 -0
  7. package/dist/assets/Countdown-BvWlPnYq.js +1 -0
  8. package/dist/assets/DataSourceInput-DCNrUj45.js +1 -0
  9. package/dist/assets/NavBar-O1kqnfE4.css +1 -0
  10. package/dist/assets/NavBar-t1eXyPKg.js +1 -0
  11. package/dist/assets/Resolver-CRBstFE3.css +1 -0
  12. package/dist/assets/Resolver-D_GRj6HP.js +1 -0
  13. package/dist/assets/RightArrowIcon-F6HBDgDW.js +1 -0
  14. package/dist/assets/TheInput-Btyuor_6.js +1 -0
  15. package/dist/assets/Tooltip-C9jfn33Z.css +1 -0
  16. package/dist/assets/Tooltip-DPDTUI-s.js +27 -0
  17. package/dist/assets/_..-D9qurBBH.css +1 -0
  18. package/dist/assets/_...all_-Chjz45WV.js +1 -0
  19. package/dist/assets/_...all_-TFChZhkr.js +6 -0
  20. package/dist/assets/_intlify-i18n-0-BmghNkij.js +1 -0
  21. package/dist/assets/_intlify-i18n-1-DzpDgiG5.js +1 -0
  22. package/dist/assets/_plugin-vue_export-helper-BDNMzG2s.js +1 -0
  23. package/dist/assets/balloon-BlTynoB_.js +1 -0
  24. package/dist/assets/board-A69GsfUB.js +1 -0
  25. package/dist/assets/board-layout-mk2Y30Yp.js +1 -0
  26. package/dist/assets/constant-YlOY-yXZ.js +1 -0
  27. package/dist/assets/countdown-DQRGLfdE.js +1 -0
  28. package/dist/assets/default-BNqFZJwJ.js +1 -0
  29. package/dist/assets/headless-BSf0YrvV.js +1 -0
  30. package/dist/assets/index-C1jdVImx.css +1 -0
  31. package/dist/assets/index-D57KOcpK.js +12 -0
  32. package/dist/assets/index-layout-Cg_FpvvD.js +1 -0
  33. package/dist/assets/pages-DZG1eNR4.css +1 -0
  34. package/dist/assets/{index-D0V3pqsw.js → pages-kJjvENga.js} +2 -2
  35. package/dist/assets/pagination-EIqoVe6M.js +21 -0
  36. package/dist/assets/person-DrN1htE6.js +1 -0
  37. package/dist/assets/query-CoPl3fiI.js +20 -0
  38. package/dist/assets/rank-Bjo0QJlM.js +1 -0
  39. package/dist/assets/rating-B3sXjXNN.js +1 -0
  40. package/dist/assets/rating-DiCQYzia.css +1 -0
  41. package/dist/assets/resolver-Db22e0q8.js +1 -0
  42. package/dist/assets/route-block-ChiJXsfH.js +1 -0
  43. package/dist/assets/utils-Cfm-h8Z9.js +1 -0
  44. package/dist/assets/vue.runtime.esm-bundler-DTMOfGWR.js +4 -0
  45. package/dist/index.html +1 -1
  46. package/package.json +45 -45
  47. package/src/auto-imports.d.ts +4 -4
  48. package/src/components/board/Board.vue +3 -0
  49. package/src/components/board/Export.vue +5 -1
  50. package/src/components/board/Progress.vue +1 -0
  51. package/src/components/board/SeatMapView.vue +6 -3
  52. package/src/components/board/SubmissionsTable.vue +1 -2
  53. package/src/components/board/SubmitHeatMap.less +11 -3
  54. package/src/components/board/SubmitHeatMap.vue +56 -86
  55. package/src/components/board/TeamCard.vue +6 -6
  56. package/src/components/board/TeamOverview.vue +1 -0
  57. package/src/components/flowbite/Tooltip.vue +79 -13
  58. package/src/components.d.ts +0 -1
  59. package/dist/assets/Balloon.vue_vue_type_script_setup_true_lang-DHemO8cG.js +0 -1
  60. package/dist/assets/Board-C6kTRGEP.css +0 -1
  61. package/dist/assets/Board-DxiquVyO.js +0 -194
  62. package/dist/assets/ContestStateBadge-Bgdroopp.js +0 -1
  63. package/dist/assets/ContestStateBadge-D6uf8wH-.css +0 -1
  64. package/dist/assets/Countdown-ovUCuJ35.js +0 -1
  65. package/dist/assets/DataSourceInput.vue_vue_type_script_setup_true_lang-DgkCadgz.js +0 -1
  66. package/dist/assets/NavBar-DfKQ1ytp.js +0 -1
  67. package/dist/assets/NavBar-aT2xEvYz.css +0 -1
  68. package/dist/assets/Resolver-BQcsyOHn.js +0 -1
  69. package/dist/assets/Resolver-DV9FleNP.css +0 -1
  70. package/dist/assets/RightArrowIcon-BKvYPM1O.js +0 -1
  71. package/dist/assets/TheInput.vue_vue_type_script_setup_true_lang-OA6Dkh4w.js +0 -1
  72. package/dist/assets/Tooltip-DzyPDwbo.css +0 -1
  73. package/dist/assets/Tooltip.vue_vue_type_script_setup_true_lang-DWNdRDhE.js +0 -27
  74. package/dist/assets/_..-5I0UWqRG.css +0 -1
  75. package/dist/assets/_...all_-BtYYdFga.js +0 -1
  76. package/dist/assets/_...all_-XVDapPdm.js +0 -6
  77. package/dist/assets/_plugin-vue_export-helper-DlAUqK2U.js +0 -1
  78. package/dist/assets/board-layout-LZdDEkNj.js +0 -1
  79. package/dist/assets/constant-CYOTMbJW.js +0 -1
  80. package/dist/assets/dayjs-Ccpn6fIh.js +0 -1
  81. package/dist/assets/default-CRpkLq3n.js +0 -1
  82. package/dist/assets/en-DGp_RGnz.js +0 -1
  83. package/dist/assets/headless-l1XzJj3O.js +0 -1
  84. package/dist/assets/index-B34Y_Z57.js +0 -15
  85. package/dist/assets/index-B3NvUps3.js +0 -1
  86. package/dist/assets/index-B_303hcz.js +0 -1
  87. package/dist/assets/index-Bojw84ll.css +0 -1
  88. package/dist/assets/index-BuFplOHa.js +0 -1
  89. package/dist/assets/index-CeJCbVnH.js +0 -1
  90. package/dist/assets/index-CgXDtxXW.css +0 -1
  91. package/dist/assets/index-D-msPqye.css +0 -1
  92. package/dist/assets/index-DOf6OpNV.js +0 -1
  93. package/dist/assets/index-layout-BDulxfyN.js +0 -1
  94. package/dist/assets/pagination-DkTeb3KN.js +0 -3
  95. package/dist/assets/person-gKEre052.js +0 -1
  96. package/dist/assets/rank-DJDWVDFD.js +0 -1
  97. package/dist/assets/route-block-B_A1xBdJ.js +0 -1
  98. package/dist/assets/use-vmodel-Bem9C7yk.js +0 -20
  99. package/dist/assets/zh-CN-BUehjmhL.js +0 -1
  100. package/src/components/board/HeatMapTooltip.less +0 -115
  101. package/src/components/board/HeatMapTooltip.vue +0 -68
@@ -150,8 +150,7 @@ const submissions = computed(() => {
150
150
  if (o.orgIds.length === 0
151
151
  && o.teamIds.length === 0
152
152
  && o.languages.length === 0
153
- && o.statuses.length === 0
154
- ) {
153
+ && o.statuses.length === 0) {
155
154
  return true;
156
155
  }
157
156
 
@@ -1,13 +1,21 @@
1
1
  @correct-colors: #9be9a8, #40c463, #30a14e, #216e39;
2
2
  @incorrect-colors: #ff8c8c, #ff4d4f, #ff1a1a, #d40d0d;
3
+ @correct-colors-dark: #216e39, #30a14e, #40c463, #9be9a8;
4
+ @incorrect-colors-dark: #d40d0d, #ff1a1a, #ff4d4f, #ff8c8c;
3
5
 
4
- .generate-heat-rules(@class, @colors) {
6
+ .generate-heat-rules(@class, @colors, @dark-colors) {
5
7
  each(@colors, {
6
8
  .@{class}[data-level="@{index}"] {
7
9
  background-color: @value;
8
10
  }
9
11
  });
12
+
13
+ each(@dark-colors, {
14
+ .dark .@{class}[data-level="@{index}"] {
15
+ background-color: @value;
16
+ }
17
+ });
10
18
  }
11
19
 
12
- .generate-heat-rules(correct-heat, @correct-colors);
13
- .generate-heat-rules(incorrect-heat, @incorrect-colors);
20
+ .generate-heat-rules(correct-heat, @correct-colors, @correct-colors-dark);
21
+ .generate-heat-rules(incorrect-heat, @incorrect-colors, @incorrect-colors-dark);
@@ -1,6 +1,7 @@
1
1
  <script setup lang="ts">
2
- import type { Rank } from "@xcpcio/core";
2
+ import type { Rank, Submission } from "@xcpcio/core";
3
3
  import { isAccepted, isRejected } from "@xcpcio/core";
4
+ import "./SubmitHeatMap.less";
4
5
 
5
6
  const props = defineProps<{
6
7
  rank: Rank;
@@ -9,23 +10,32 @@ const props = defineProps<{
9
10
  const { t } = useI18n();
10
11
 
11
12
  const rank = computed(() => props.rank);
13
+ const submissions = computed(() => rank.value.getSubmissions());
12
14
 
13
15
  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
16
 
19
- const submissions = computed(() => rank.value.getSubmissions());
17
+ const intervalSize = computed(() => {
18
+ const startTime = rank.value.contest.getStartTime();
19
+ const endTime = rank.value.contest.getEndTime();
20
+ const totalDuration = endTime.diff(startTime, "minute");
21
+
22
+ if (!Number.isFinite(totalDuration) || totalDuration <= 0) {
23
+ return 1;
24
+ }
20
25
 
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: `;
26
+ return totalDuration / mapTimeDiffLength;
25
27
  });
26
28
 
29
+ const intervalDescriptions = computed(() =>
30
+ Array.from({ length: mapTimeDiffLength }, (_, index) => {
31
+ const start = index * intervalSize.value;
32
+ const end = (index + 1) * intervalSize.value;
33
+ return `Between ${start} and ${end} minutes: `;
34
+ }),
35
+ );
36
+
27
37
  const submissionsByProblemId = computed(() => {
28
- const result = new Map();
38
+ const result = new Map<string, { correct: Submission[]; incorrect: Submission[] }>();
29
39
 
30
40
  rank.value.contest.problems.forEach((p) => {
31
41
  result.set(p.id, { correct: [], incorrect: [] });
@@ -47,64 +57,30 @@ const submissionsByProblemId = computed(() => {
47
57
  return result;
48
58
  });
49
59
 
50
- function generateHeatMap(submissions: Map<any, any>, type: "correct" | "incorrect") {
60
+ function generateHeatMap(submissions: Submission[], type: "correct" | "incorrect") {
51
61
  const counts = Array.from<number>({ length: mapTimeDiffLength }).fill(0);
62
+ const size = intervalSize.value > 0 ? intervalSize.value : 1;
63
+ const descriptions = intervalDescriptions.value;
52
64
 
53
65
  submissions.forEach((s) => {
54
- const index = Math.min(Math.floor(s.timestampToMinute / intervalSize), mapTimeDiffLength - 1);
66
+ const index = Math.min(Math.floor(s.timestampToMinute / size), mapTimeDiffLength - 1);
55
67
  counts[index]++;
56
68
  });
57
69
 
58
70
  return counts.map((count, i) => ({
59
71
  count,
60
72
  level: 0,
61
- description: `${intervalDescriptions[i]}${count} ${type} submissions`,
73
+ description: `${descriptions[i]}${count} ${type} submissions`,
62
74
  }));
63
75
  }
64
76
 
65
- function calcPreciseQuan(sortedValues: number[], p: number): number {
66
- if (sortedValues.length === 0) {
77
+ function getHeatLevel(value: number, maxValue: number): number {
78
+ if (value <= 0 || maxValue <= 0) {
67
79
  return 0;
68
80
  }
69
81
 
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;
82
+ const level = Math.ceil((value / maxValue) * 4);
83
+ return Math.min(Math.max(level, 1), 4);
108
84
  }
109
85
 
110
86
  const heatMapData = computed(() =>
@@ -115,13 +91,13 @@ const heatMapData = computed(() =>
115
91
  const incorrectHeatMap = generateHeatMap(incorrect, "incorrect");
116
92
 
117
93
  const correctCounts = correctHeatMap.map(i => i.count);
118
- const correctThresholds = calcStaticThresholds(correctCounts);
94
+ const correctMax = Math.max(0, ...correctCounts);
119
95
 
120
96
  const incorrectCounts = incorrectHeatMap.map(i => i.count);
121
- const incorrectThresholds = calcStaticThresholds(incorrectCounts);
97
+ const incorrectMax = Math.max(0, ...incorrectCounts);
122
98
 
123
- correctHeatMap.forEach(i => i.level = getDynamicHeatLevel(i.count, correctThresholds));
124
- incorrectHeatMap.forEach(i => i.level = getDynamicHeatLevel(i.count, incorrectThresholds));
99
+ correctHeatMap.forEach(i => i.level = getHeatLevel(i.count, correctMax));
100
+ incorrectHeatMap.forEach(i => i.level = getHeatLevel(i.count, incorrectMax));
125
101
 
126
102
  return {
127
103
  label: p.label,
@@ -145,14 +121,14 @@ const heatMapData = computed(() =>
145
121
  </span>
146
122
 
147
123
  <div
148
- grid grid-cols-3
149
- gap-10 space-y-0
124
+ grid grid-cols-4
125
+ gap-6 space-y-0
150
126
  place-items-center
151
127
  >
152
128
  <div
153
129
  v-for="heatMap in heatMapData" :key="heatMap.label"
154
- class="w-350px h-100px"
155
- flex flex-col
130
+ class="w-280px h-100px"
131
+ flex flex-col gap-2
156
132
  items-center justify-center
157
133
  border border-gray-100 dark:border-gray-600
158
134
  rounded-md
@@ -173,54 +149,48 @@ const heatMapData = computed(() =>
173
149
 
174
150
  <div
175
151
  flex flex-row
176
- gap-1
152
+ gap-0.5
177
153
  >
178
154
  <div
179
155
  v-for="(correctItem, index) in heatMap.correctHeatMap" :key="`correct-${index}`"
180
156
  >
181
- <HeatMapTooltip
182
- :content="correctItem.description"
183
- position="top"
184
- >
157
+ <Tooltip>
185
158
  <div
186
159
  :data-level="correctItem.level"
187
160
  class="correct-heat"
188
- size-12px
189
- rounded-1
190
- shadow
191
- border border-gray-100 dark:border-gray-600
161
+ size-10px
162
+ rounded-0.7
163
+ bg-gray-200 dark:bg-gray-700
192
164
  />
193
- </HeatMapTooltip>
165
+ <template #popper>
166
+ {{ correctItem.description }}
167
+ </template>
168
+ </Tooltip>
194
169
  </div>
195
170
  </div>
196
171
 
197
172
  <div
198
173
  flex flex-row
199
- gap-1
174
+ gap-0.5
200
175
  >
201
176
  <div
202
177
  v-for="(incorrectItem, index) in heatMap.incorrectHeatMap" :key="`incorrect-${index}`"
203
178
  >
204
- <HeatMapTooltip
205
- :content="incorrectItem.description"
206
- position="top"
207
- >
179
+ <Tooltip>
208
180
  <div
209
181
  :data-level="incorrectItem.level"
210
182
  class="incorrect-heat"
211
- size-12px
212
- rounded-1
213
- shadow
214
- border border-gray-100 dark:border-gray-600
183
+ size-10px
184
+ rounded-0.7
185
+ bg-gray-200 dark:bg-gray-700
215
186
  />
216
- </HeatMapTooltip>
187
+ <template #popper>
188
+ {{ incorrectItem.description }}
189
+ </template>
190
+ </Tooltip>
217
191
  </div>
218
192
  </div>
219
193
  </div>
220
194
  </div>
221
195
  </div>
222
196
  </template>
223
-
224
- <style scoped lang="less">
225
- @import "./SubmitHeatMap.less";
226
- </style>
@@ -61,7 +61,7 @@ const isGirl = computed(() => team.value.isGirl);
61
61
  class="h-12 w-12"
62
62
  >
63
63
  <div flex="1 ~ col" min-w-0>
64
- <HeatMapTooltip position="top">
64
+ <Tooltip placement="top">
65
65
  <div
66
66
  text="lg gray-900 dark:white"
67
67
  font="semibold"
@@ -71,23 +71,23 @@ const isGirl = computed(() => team.value.isGirl);
71
71
  <span v-if="isUnofficial" class="i-line-md:star-alt-filled" text="sm" />
72
72
  <span v-if="isGirl" class="i-tabler:gender-female" text="sm pink-500" />
73
73
  </div>
74
- <template #tooltip-content>
74
+ <template #popper>
75
75
  <div>{{ t("team_info.team_id") }}{{ t("common.colon") }}{{ team.id }}</div>
76
76
  <div>{{ t("team_info.team_name") }}{{ t("common.colon") }}{{ teamName }}</div>
77
77
  </template>
78
- </HeatMapTooltip>
79
- <HeatMapTooltip v-if="organizationName" position="top">
78
+ </Tooltip>
79
+ <Tooltip v-if="organizationName" placement="top">
80
80
  <div
81
81
  text="sm gray-500 dark:gray-400"
82
82
  class="line-clamp-1"
83
83
  >
84
84
  {{ organizationName }}
85
85
  </div>
86
- <template #tooltip-content>
86
+ <template #popper>
87
87
  <div>{{ t("team_info.org_id") }}{{ t("common.colon") }}{{ team.organization?.id }}</div>
88
88
  <div>{{ t("team_info.org_name") }}{{ t("common.colon") }}{{ organizationName }}</div>
89
89
  </template>
90
- </HeatMapTooltip>
90
+ </Tooltip>
91
91
  </div>
92
92
  </div>
93
93
 
@@ -92,6 +92,7 @@ const tableRows = computed(() => {
92
92
  const rows = [
93
93
  { label: t("team_info.rank"), value: team.value.rank, show: true },
94
94
  { label: t("team_info.team_name"), value: team.value.name.getOrDefault(lang.value), show: true },
95
+ { label: t("team_info.description"), value: team.value.description?.getOrDefault(lang.value), show: !!team.value.description },
95
96
  { label: t("team_info.organization"), value: team.value.organization?.name.getOrDefault(lang.value), show: !!team.value.organization },
96
97
  { label: t("team_info.group"), value: groupNames.value, show: team.value.group.length > 0 },
97
98
  { label: t("team_info.members"), value: team.value.membersToString(lang.value), show: team.value.members.length > 0 },
@@ -1,32 +1,98 @@
1
1
  <script setup lang="ts">
2
2
  import type { TooltipInterface, TooltipOptions } from "flowbite";
3
3
  import { Tooltip } from "flowbite";
4
-
5
4
  import "./Tooltip.css";
6
5
 
7
6
  const props = defineProps<{
8
7
  placement?: "left" | "right" | "top" | "bottom" | "auto";
9
8
  }>();
10
9
 
11
- const tooltipTargetEl = ref(null);
12
- const tooltipTriggerEl = ref(null);
10
+ const instanceId = `vue-tooltip-${getCurrentInstance()?.uid ?? Math.random().toString(36).slice(2)}`;
11
+ const tooltipTargetEl = ref<HTMLElement | null>(null);
12
+ const tooltipTriggerEl = ref<HTMLElement | null>(null);
13
+ const isTriggerVisible = useElementVisibility(tooltipTriggerEl);
14
+ const placement = computed(() => {
15
+ return props.placement ?? "auto";
16
+ });
13
17
 
14
- // eslint-disable-next-line unused-imports/no-unused-vars
15
18
  let tooltip: TooltipInterface | null = null;
19
+ let cleanup: (() => void) | null = null;
16
20
 
17
- const placement = computed(() => {
18
- return props.placement ?? "auto";
21
+ function createTooltip() {
22
+ if (tooltip) {
23
+ return;
24
+ }
25
+
26
+ if (!tooltipTargetEl.value || !tooltipTriggerEl.value) {
27
+ return;
28
+ }
29
+
30
+ const options: TooltipOptions = {
31
+ placement: placement.value as unknown as undefined,
32
+ triggerType: "none",
33
+ };
34
+
35
+ tooltip = new Tooltip(tooltipTargetEl.value, tooltipTriggerEl.value, options, {
36
+ id: instanceId,
37
+ override: true,
38
+ });
39
+ }
40
+
41
+ function destroyTooltip() {
42
+ tooltip?.destroyAndRemoveInstance();
43
+ tooltip = null;
44
+ }
45
+
46
+ function showTooltip() {
47
+ createTooltip();
48
+ tooltip?.show();
49
+ }
50
+
51
+ function hideTooltip() {
52
+ tooltip?.hide();
53
+ }
54
+
55
+ watch(isTriggerVisible, (visible) => {
56
+ if (visible) {
57
+ return;
58
+ }
59
+
60
+ if (!tooltip) {
61
+ return;
62
+ }
63
+
64
+ tooltip.hide();
65
+ destroyTooltip();
19
66
  });
20
67
 
21
68
  onMounted(() => {
22
- if (tooltipTargetEl.value && tooltipTriggerEl.value) {
23
- const options: TooltipOptions = {
24
- placement: placement.value as unknown as undefined,
25
- triggerType: "hover",
26
- };
27
-
28
- tooltip = new Tooltip(tooltipTargetEl.value, tooltipTriggerEl.value, options);
69
+ const triggerEl = tooltipTriggerEl.value;
70
+ if (!triggerEl) {
71
+ return;
29
72
  }
73
+
74
+ const onMouseEnter = () => showTooltip();
75
+ const onMouseLeave = () => hideTooltip();
76
+ const onFocus = () => showTooltip();
77
+ const onBlur = () => hideTooltip();
78
+
79
+ triggerEl.addEventListener("mouseenter", onMouseEnter);
80
+ triggerEl.addEventListener("mouseleave", onMouseLeave);
81
+ triggerEl.addEventListener("focus", onFocus, true);
82
+ triggerEl.addEventListener("blur", onBlur, true);
83
+
84
+ cleanup = () => {
85
+ triggerEl.removeEventListener("mouseenter", onMouseEnter);
86
+ triggerEl.removeEventListener("mouseleave", onMouseLeave);
87
+ triggerEl.removeEventListener("focus", onFocus, true);
88
+ triggerEl.removeEventListener("blur", onBlur, true);
89
+ };
90
+ });
91
+
92
+ onBeforeUnmount(() => {
93
+ cleanup?.();
94
+ cleanup = null;
95
+ destroyTooltip();
30
96
  });
31
97
  </script>
32
98
 
@@ -34,7 +34,6 @@ declare module 'vue' {
34
34
  GiantsOptions: typeof import('./components/battle-of-giants/GiantsOptions.vue')['default']
35
35
  GiantsScoreBoard: typeof import('./components/battle-of-giants/GiantsScoreBoard.vue')['default']
36
36
  GoBack: typeof import('./components/GoBack.vue')['default']
37
- HeatMapTooltip: typeof import('./components/board/HeatMapTooltip.vue')['default']
38
37
  InfoModal: typeof import('./components/board/InfoModal.vue')['default']
39
38
  Modal: typeof import('./components/board/Modal.vue')['default']
40
39
  ModalMenu: typeof import('./components/board/ModalMenu.vue')['default']
@@ -1 +0,0 @@
1
- import{e as w,m as L,f as C,c as n,o as s,p as i,q as D,u as e,n as O,h as c,b as o,I as k,t as d,A as F,C as I,F as N,E as V,G as z}from"./index-B34Y_Z57.js";import{B}from"./constant-CYOTMbJW.js";import{c as R,a as U,R as A,B as q}from"./rank-DJDWVDFD.js";import{c as G}from"./use-vmodel-Bem9C7yk.js";import{u as M}from"./TheInput.vue_vue_type_script_setup_true_lang-OA6Dkh4w.js";const Q={flex:"","flex-1":"","flex-col":"","justify-center":"","items-start":""},X={class:"resolver-team-name",truncate:"","overflow-hidden":""},H={"w-32":"",flex:"","flex-shrink-0":"","flex-row":"","justify-start":"","items-center":""},J=w({__name:"BalloonBlock",props:{index:null,balloon:null},setup(p){const f=p,b=c(()=>f.index),r=c(()=>f.balloon),v=i(null),y=L(v),{locale:_}=C(),l=c(()=>_.value);function x(a){return[a.location,a.organization?.name.getOrDefault(l.value),a.name.getOrDefault(l.value)].filter(t=>t).join(" - ")}const u=c(()=>r.value.problem.balloonColor);return(a,m)=>(s(),n("div",{ref_key:"el",ref:v,"h-24":""},[e(y)?(s(),n("div",{key:0,"h-24":"",flex:"","flex-row":"","gap-x-4":"","font-mono":"","text-4xl":"",class:O([e(b)%2===0?"bg-resolver-bg-0":"bg-resolver-bg-1"])},[o("div",{"w-20":"",flex:"","flex-shrink-0":"","justify-center":"","items-center":"",style:k({backgroundColor:e(u).background_color})},[o("div",{style:k({color:e(u).color})},d(e(r).problem.label),5)],4),o("div",Q,[o("div",X,d(x(e(r).team)),1),m[0]||(m[0]=o("div",{flex:"","flex-row":"","text-sm":"","items-start":"","gap-x-2":""},null,-1))]),o("div",H,d(e(r).submission.timestampToMinute),1)],2)):D("",!0)],512))}}),K={class:"bg-[#323443]","text-gray-200":"","w-screen":"","h-screen":""},P={key:0},W={flex:"","flex-col":"","justify-center":"","items-center":"","w-screen":"","h-screen":"","text-xl":"",italic:""},Y={key:0},Z={key:1},ee={flex:"","flex-col":"","justify-between":""},re=w({__name:"Balloon",props:{dataSourceUrl:null},setup(p){const f=p,b=F(B),{t:r,locale:v}=C(),y=c(()=>v.value),_=i(!1),l=i({}),x=i([]),u=i([]),a=i({});function m(){const g=new A(l.value,x.value,u.value);g.buildBalloons(),a.value=g}const{data:t,isError:j,error:S}=M(f.dataSourceUrl);I(t,async()=>{t.value===null||t.value===void 0||(l.value=G(t.value?.contest),b.value=`${l.value.name.getOrDefault(y.value)} | ${B}`,x.value=R(t.value?.teams),u.value=U(t.value?.submissions,l.value),m(),_.value=!0)},{immediate:!0});const T=c(()=>a.value.balloons.sort(q.compare).reverse().slice(0,256));return(g,oe)=>{const $=J;return s(),n("div",K,[e(_)?(s(),n("div",Z,[o("div",ee,[(s(!0),n(N,null,V(e(T),(h,E)=>(s(),z($,{key:h.key,index:E,balloon:h},null,8,["index","balloon"]))),128))])])):(s(),n("div",P,[o("div",W,[o("div",null,d(e(r)("common.loading"))+"... ",1),e(j)?(s(),n("div",Y,d(e(S)),1)):D("",!0)])]))])}}});export{re as _};