베팅 장수 정보 글자를 키우고 왼쪽 아이콘 표시

This commit is contained in:
2026-09-12 07:23:46 +00:00
parent e55c653e40
commit 3ad230beb3
2 changed files with 99 additions and 83 deletions
@@ -994,6 +994,7 @@ for (const width of [1365, 1101, 800, 390, 320]) {
elements.map((element) => { elements.map((element) => {
const rect = element.getBoundingClientRect(); const rect = element.getBoundingClientRect();
const selectors = [ const selectors = [
'.general-identity-icon',
'.general-identity-name', '.general-identity-name',
'.bracket-core-stat', '.bracket-core-stat',
'.bracket-odds', '.bracket-odds',
@@ -1010,6 +1011,7 @@ for (const width of [1365, 1101, 800, 390, 320]) {
selector, selector,
rect: el.getBoundingClientRect().toJSON(), rect: el.getBoundingClientRect().toJSON(),
font: style.font, font: style.font,
fontSize: Number.parseFloat(style.fontSize),
color: style.color, color: style.color,
scrollWidth: el.scrollWidth, scrollWidth: el.scrollWidth,
clientWidth: el.clientWidth, clientWidth: el.clientWidth,
@@ -1019,7 +1021,22 @@ for (const width of [1365, 1101, 800, 390, 320]) {
}) })
); );
for (const card of geometry) { for (const card of geometry) {
const iconRect = card.fields.find((field) => field.selector === '.general-identity-icon')!.rect;
expect(iconRect.width).toBe(width > 1100 ? 64 : 40);
expect(iconRect.height).toBe(iconRect.width);
for (const field of card.fields) { for (const field of card.fields) {
if (
[
'.general-identity-name',
'.bracket-core-stat',
'.bracket-odds',
'.bracket-my-bet',
'.bracket-return',
].includes(field.selector)
) {
expect(field.rect.left).toBeGreaterThanOrEqual(iconRect.right + 5);
expect(field.fontSize).toBeGreaterThanOrEqual(width > 1100 ? 14 : 13);
}
expect(field.rect.left).toBeGreaterThanOrEqual(card.rect.left - 1); expect(field.rect.left).toBeGreaterThanOrEqual(card.rect.left - 1);
expect(field.rect.right).toBeLessThanOrEqual(card.rect.right + 1); expect(field.rect.right).toBeLessThanOrEqual(card.rect.right + 1);
expect(field.rect.top).toBeGreaterThanOrEqual(card.rect.top - 1); expect(field.rect.top).toBeGreaterThanOrEqual(card.rect.top - 1);
@@ -1060,11 +1077,9 @@ for (const width of [1365, 1101, 800, 390, 320]) {
); );
await page.screenshot({ path: testInfo.outputPath('inline-before.png'), fullPage: true }); await page.screenshot({ path: testInfo.outputPath('inline-before.png'), fullPage: true });
await page.screenshot({ path: testInfo.outputPath('inline-viewport.png') }); await page.screenshot({ path: testInfo.outputPath('inline-viewport.png') });
const iconTrigger = cards.first().locator('[data-rich-tooltip]'); const candidateImage = cards.first().locator('.general-identity-icon');
await iconTrigger.hover(); await expect(candidateImage).toBeVisible();
const tooltipImage = page.locator('.tippy-box[data-state="visible"] img'); const icon = await candidateImage.evaluate((image: HTMLImageElement) => ({
await expect(tooltipImage).toBeVisible();
const icon = await tooltipImage.evaluate((image: HTMLImageElement) => ({
rect: image.getBoundingClientRect().toJSON(), rect: image.getBoundingClientRect().toJSON(),
naturalWidth: image.naturalWidth, naturalWidth: image.naturalWidth,
naturalHeight: image.naturalHeight, naturalHeight: image.naturalHeight,
@@ -1072,8 +1087,8 @@ for (const width of [1365, 1101, 800, 390, 320]) {
})); }));
expect(icon.naturalWidth).toBeGreaterThan(0); expect(icon.naturalWidth).toBeGreaterThan(0);
expect(icon.naturalHeight).toBeGreaterThan(0); expect(icon.naturalHeight).toBeGreaterThan(0);
expect(icon.objectFit).toBe('cover');
await writeFile(testInfo.outputPath('inline-icon.json'), JSON.stringify(icon, null, 2)); await writeFile(testInfo.outputPath('inline-icon.json'), JSON.stringify(icon, null, 2));
await page.keyboard.press('Escape');
const buttons = bracket.locator('.bracket-bet-button'); const buttons = bracket.locator('.bracket-bet-button');
const firstButton = buttons.first(); const firstButton = buttons.first();
await firstButton.hover(); await firstButton.hover();
@@ -1,7 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
import GeneralIdentity from '../ui/GeneralIdentity.vue'; import GeneralIdentity from '../ui/GeneralIdentity.vue';
import RichTooltip from '../ui/RichTooltip.vue';
import { import {
buildTournamentBracket, buildTournamentBracket,
resolveTournamentCoreStat, resolveTournamentCoreStat,
@@ -49,7 +48,7 @@ const roundColumns = computed(() => [
]); ]);
const desktopX = computed(() => (props.bettingMode ? [180, 480, 690, 895, 1100] : [110, 355, 600, 845, 1090])); const desktopX = computed(() => (props.bettingMode ? [180, 480, 690, 895, 1100] : [110, 355, 600, 845, 1090]));
const cardWidth = 190; const cardWidth = 190;
const desktopSlotHeight = computed(() => (props.bettingMode ? 154 : 88)); const desktopSlotHeight = computed(() => (props.bettingMode ? 184 : 88));
const desktopCanvasHeight = computed(() => desktopSlotHeight.value * 16); const desktopCanvasHeight = computed(() => desktopSlotHeight.value * 16);
const slotY = (columnIndex: number, slotIndex: number) => { const slotY = (columnIndex: number, slotIndex: number) => {
const slotHeight = desktopSlotHeight.value * 2 ** columnIndex; const slotHeight = desktopSlotHeight.value * 2 ** columnIndex;
@@ -159,39 +158,30 @@ const mobilePairs = computed(() => {
top: `${slotY(columnIndex, slotIndex)}px`, top: `${slotY(columnIndex, slotIndex)}px`,
}" }"
> >
<span
v-if="columnIndex === 0 && bettingMode && slot.id !== null"
class="bracket-candidate-identity"
>
<RichTooltip placement="right" :max-width="180" :test-id="`candidate-icon-${slot.id}`">
<GeneralIdentity :name="slot.name" :hide-icon="true" :npc-state="slot.npcState" />
<template #content>
<GeneralIdentity
:name="slot.name"
:picture="slot.picture"
:image-server="slot.imageServer"
:npc-state="slot.npcState"
/>
</template>
</RichTooltip>
</span>
<GeneralIdentity <GeneralIdentity
v-else :class="{ 'bracket-candidate-identity': columnIndex === 0 && bettingMode }"
:name="slot.name" :name="slot.name"
:picture="slot.picture" :picture="slot.picture"
:image-server="slot.imageServer" :image-server="slot.imageServer"
:npc-state="slot.npcState" :npc-state="slot.npcState"
/> >
<div v-if="columnIndex === 0 && bettingMode" class="bracket-bet-summary"> <template v-if="columnIndex === 0 && bettingMode" #details>
<small v-if="coreStat(slot)" class="bracket-core-stat"> <span class="bracket-bet-summary">
{{ coreStat(slot)?.label }} {{ coreStat(slot)?.value }} <small v-if="coreStat(slot)" class="bracket-core-stat">
</small> {{ coreStat(slot)?.label }} {{ coreStat(slot)?.value }}
<small class="bracket-odds">배당 {{ odds(slot.id) }}</small> </small>
<small class="bracket-my-bet"> 투자 {{ myBet(slot.id).toLocaleString('ko-KR') }}</small> <small class="bracket-odds">배당 {{ odds(slot.id) }}</small>
<small class="bracket-return" <small class="bracket-my-bet"
>예상 환수 {{ (myExpectedReturn(slot.id) ?? 0).toLocaleString('ko-KR') }}</small > 투자 {{ myBet(slot.id).toLocaleString('ko-KR') }}</small
> >
</div> <small class="bracket-return"
>예상 환수 {{
(myExpectedReturn(slot.id) ?? 0).toLocaleString('ko-KR')
}}</small
>
</span>
</template>
</GeneralIdentity>
<slot <slot
v-if="columnIndex === 0 && bettingOpen && slot.id !== null" v-if="columnIndex === 0 && bettingOpen && slot.id !== null"
name="bet-controls" name="bet-controls"
@@ -238,43 +228,30 @@ const mobilePairs = computed(() => {
}" }"
:data-general-id="slot.id ?? undefined" :data-general-id="slot.id ?? undefined"
> >
<span
v-if="activeMobileRound === 0 && bettingMode && slot.id !== null"
class="bracket-candidate-identity"
>
<RichTooltip
placement="bottom"
:max-width="180"
:test-id="`mobile-candidate-icon-${slot.id}`"
>
<GeneralIdentity :name="slot.name" :hide-icon="true" :npc-state="slot.npcState" />
<template #content>
<GeneralIdentity
:name="slot.name"
:picture="slot.picture"
:image-server="slot.imageServer"
:npc-state="slot.npcState"
/>
</template>
</RichTooltip>
</span>
<GeneralIdentity <GeneralIdentity
v-else :class="{ 'bracket-candidate-identity': activeMobileRound === 0 && bettingMode }"
:name="slot.name" :name="slot.name"
:picture="slot.picture" :picture="slot.picture"
:image-server="slot.imageServer" :image-server="slot.imageServer"
:npc-state="slot.npcState" :npc-state="slot.npcState"
/> >
<div v-if="activeMobileRound === 0 && bettingMode" class="bracket-bet-summary"> <template v-if="activeMobileRound === 0 && bettingMode" #details>
<small v-if="coreStat(slot)" class="bracket-core-stat"> <span class="bracket-bet-summary">
{{ coreStat(slot)?.label }} {{ coreStat(slot)?.value }} <small v-if="coreStat(slot)" class="bracket-core-stat">
</small> {{ coreStat(slot)?.label }} {{ coreStat(slot)?.value }}
<small class="bracket-odds">배당 {{ odds(slot.id) }}</small> </small>
<small class="bracket-my-bet"> 투자 {{ myBet(slot.id).toLocaleString('ko-KR') }}</small> <small class="bracket-odds">배당 {{ odds(slot.id) }}</small>
<small class="bracket-return" <small class="bracket-my-bet"
>예상 환수 {{ (myExpectedReturn(slot.id) ?? 0).toLocaleString('ko-KR') }}</small > 투자 {{ myBet(slot.id).toLocaleString('ko-KR') }}</small
> >
</div> <small class="bracket-return"
>예상 환수 {{
(myExpectedReturn(slot.id) ?? 0).toLocaleString('ko-KR')
}}</small
>
</span>
</template>
</GeneralIdentity>
<slot <slot
v-if="activeMobileRound === 0 && bettingOpen && slot.id !== null" v-if="activeMobileRound === 0 && bettingOpen && slot.id !== null"
name="bet-controls" name="bet-controls"
@@ -354,7 +331,7 @@ const mobilePairs = computed(() => {
} }
.desktop-bracket-name.betting-candidate { .desktop-bracket-name.betting-candidate {
width: 28.3333%; width: 28.3333%;
min-height: 140px; min-height: 176px;
align-content: center; align-content: center;
gap: 5px; gap: 5px;
padding: 5px 6px; padding: 5px 6px;
@@ -400,30 +377,34 @@ const mobilePairs = computed(() => {
justify-content: flex-start; justify-content: flex-start;
} }
.bracket-candidate-identity { .bracket-candidate-identity {
display: flex; --sammo-general-icon-size: 64px;
display: grid;
grid-template-columns: 64px minmax(0, 1fr);
min-width: 0; min-width: 0;
min-height: 24px; gap: 8px;
align-items: center; align-items: start;
padding-right: 0;
text-align: left; text-align: left;
} }
.bracket-candidate-identity :deep(.rich-tooltip-trigger) { .bracket-candidate-identity :deep(.general-identity-copy) {
display: block; gap: 3px;
min-width: 0;
} }
.betting-candidate .bracket-candidate-identity :deep(.general-identity-name) { .bracket-candidate-identity :deep(.general-identity-name) {
overflow: visible; overflow: visible;
text-overflow: clip; text-overflow: clip;
white-space: normal; white-space: normal;
overflow-wrap: anywhere; overflow-wrap: anywhere;
font-size: 16px;
line-height: 20px;
font-weight: 700;
} }
.bracket-bet-summary { .bracket-bet-summary {
display: grid; display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
min-width: 0; min-width: 0;
align-items: center; align-items: center;
gap: 3px 7px; gap: 2px 7px;
white-space: nowrap; white-space: normal;
overflow-wrap: anywhere;
} }
.bracket-core-stat, .bracket-core-stat,
.bracket-odds, .bracket-odds,
@@ -431,8 +412,8 @@ const mobilePairs = computed(() => {
display: block; display: block;
min-width: 0; min-width: 0;
color: skyblue; color: skyblue;
font-size: 11px; font-size: 14px;
line-height: 12px; line-height: 18px;
} }
.bracket-core-stat { .bracket-core-stat {
color: #fff; color: #fff;
@@ -451,8 +432,8 @@ const mobilePairs = computed(() => {
grid-column: 1 / -1; grid-column: 1 / -1;
text-align: left; text-align: left;
color: cyan; color: cyan;
font-size: 11px; font-size: 14px;
line-height: 14px; line-height: 18px;
white-space: normal; white-space: normal;
overflow-wrap: anywhere; overflow-wrap: anywhere;
} }
@@ -468,6 +449,26 @@ const mobilePairs = computed(() => {
margin-top: auto; margin-top: auto;
} }
@media (max-width: 1100px) { @media (max-width: 1100px) {
.bracket-candidate-identity {
--sammo-general-icon-size: 40px;
grid-template-columns: 40px minmax(0, 1fr);
gap: 6px;
}
.bracket-candidate-identity :deep(.general-identity-name) {
font-size: 14px;
line-height: 18px;
}
.bracket-bet-summary {
grid-template-columns: minmax(0, 1fr);
}
.bracket-core-stat,
.bracket-odds,
.bracket-my-bet,
.bracket-return {
font-size: 13px;
line-height: 18px;
text-align: left;
}
.inline-betting-bracket .desktop-bracket { .inline-betting-bracket .desktop-bracket {
display: none; display: none;
} }