merge: align main UI grid with ref
This commit is contained in:
@@ -31,6 +31,7 @@ type NavigationFixture = {
|
||||
forceSnapshotCalls?: number;
|
||||
refreshDelayMs?: number;
|
||||
largeCommandTable?: boolean;
|
||||
reservedTurns?: Array<{ index: number; action: string; args: Record<string, unknown> }>;
|
||||
dashboardResponses?: Array<{
|
||||
bytes: number;
|
||||
contextKind: string | null;
|
||||
@@ -116,12 +117,13 @@ const emitReadModelChanges = (page: Page, changes: ReturnType<typeof readModelCh
|
||||
|
||||
const commandTableFixture = (large: boolean, blockedCount = 0) => ({
|
||||
general: large
|
||||
? [
|
||||
{
|
||||
category: '일반',
|
||||
values: Array.from({ length: 48 }, (_, index) => ({
|
||||
? ['내정', '군사', '계략'].map((category, categoryIndex) => ({
|
||||
category,
|
||||
values: Array.from({ length: 16 }, (_, localIndex) => {
|
||||
const index = categoryIndex * 16 + localIndex;
|
||||
return {
|
||||
key: `command-${index}`,
|
||||
name: `명령 ${index}`,
|
||||
name: index === 0 ? '주민 선정과 장기 도시 개발' : `명령 ${index}`,
|
||||
reqArg: index % 2 === 0,
|
||||
possible: index >= blockedCount,
|
||||
status: index >= blockedCount ? 'available' : 'blocked',
|
||||
@@ -135,9 +137,9 @@ const commandTableFixture = (large: boolean, blockedCount = 0) => ({
|
||||
max: 10_000,
|
||||
},
|
||||
],
|
||||
})),
|
||||
},
|
||||
]
|
||||
};
|
||||
}),
|
||||
}))
|
||||
: [],
|
||||
nation: [],
|
||||
inputOptions: {
|
||||
@@ -229,6 +231,7 @@ const generalContext = (state: NavigationFixture) => ({
|
||||
dex: [350, 100_000, 500_000, 1_000_000, 1_275_975],
|
||||
},
|
||||
items: { horse: null, weapon: null, book: null, item: null },
|
||||
turnTime: '0185-01-01T00:00:00.000Z',
|
||||
},
|
||||
city: {
|
||||
id: 1,
|
||||
@@ -400,7 +403,7 @@ const installFixture = async (page: Page, state: NavigationFixture) => {
|
||||
}
|
||||
if (operation === 'turns.getCommandTable') return response({ general: [], nation: [] });
|
||||
if (operation === 'turns.reserved.getGeneral' || operation === 'turns.reserved.getNation') {
|
||||
return response({ turns: [], revision: 0 });
|
||||
return response({ turns: state.reservedTurns ?? [], revision: 0 });
|
||||
}
|
||||
if (operation === 'messages.getRecent') return response(emptyMessages(state.permission));
|
||||
if (operation === 'messages.getContacts') return response({ nation: [] });
|
||||
@@ -628,9 +631,7 @@ test('desktop menus preserve ref columns, prefix-safe routes, and controlled dro
|
||||
await persistArtifact(page, `${basePath.slice(1)}-desktop-1200`);
|
||||
});
|
||||
|
||||
test('main city and general cards render every ref bar plus dual dexterity progress at ref heights', async ({
|
||||
page,
|
||||
}) => {
|
||||
test('main cards and command input stay inside their Ref-sized grid slots', async ({ page }) => {
|
||||
const state: NavigationFixture = {
|
||||
officerLevel: 1,
|
||||
permission: 0,
|
||||
@@ -639,6 +640,12 @@ test('main city and general cards render every ref bar plus dual dexterity progr
|
||||
npcMode: 1,
|
||||
generalMeCalls: 0,
|
||||
operations: [],
|
||||
largeCommandTable: true,
|
||||
reservedTurns: Array.from({ length: 14 }, (_, index) => ({
|
||||
index,
|
||||
action: `command-${index}`,
|
||||
args: {},
|
||||
})),
|
||||
};
|
||||
await installFixture(page, state);
|
||||
await page.setViewportSize({ width: 1200, height: 900 });
|
||||
@@ -647,28 +654,15 @@ test('main city and general cards render every ref bar plus dual dexterity progr
|
||||
const cityBars = page.locator('[data-main-target="city"] [role="progressbar"]');
|
||||
const statBars = page.locator('[data-stat-progress] [role="progressbar"]');
|
||||
const experienceBar = page.locator('[data-experience-progress] [role="progressbar"]');
|
||||
const dexRows = page.locator('[data-dex-progress]');
|
||||
await expect(cityBars).toHaveCount(8);
|
||||
await expect(statBars).toHaveCount(3);
|
||||
await expect(experienceBar).toHaveCount(1);
|
||||
await expect(dexRows).toHaveCount(5);
|
||||
await expect(dexRows.locator('[role="progressbar"]')).toHaveCount(10);
|
||||
await expect(page.locator('[data-main-target="general"] [data-dex-progress]')).toHaveCount(0);
|
||||
await expect(page.locator('[data-main-target="general"] [role="progressbar"]')).toHaveCount(4);
|
||||
|
||||
expect(await cityBars.first().evaluate((element) => element.getBoundingClientRect().height)).toBe(9);
|
||||
expect(await statBars.first().evaluate((element) => element.getBoundingClientRect().height)).toBe(12);
|
||||
expect(await experienceBar.evaluate((element) => element.getBoundingClientRect().height)).toBe(12);
|
||||
const firstDexBars = dexRows.first().locator('[role="progressbar"]');
|
||||
expect(await firstDexBars.nth(0).evaluate((element) => element.getBoundingClientRect().height)).toBe(12);
|
||||
expect(await firstDexBars.nth(1).evaluate((element) => element.getBoundingClientRect().height)).toBe(9);
|
||||
|
||||
await expect(dexRows.nth(3).locator('[role="progressbar"]').nth(0)).toHaveAttribute('aria-valuemax', '100');
|
||||
await expect(dexRows.nth(3).locator('[role="progressbar"]').nth(0)).toHaveAttribute(
|
||||
'aria-label',
|
||||
/1,000,000 \/ 1,275,975 \(EX\+\)/
|
||||
);
|
||||
await expect(dexRows.nth(3).locator('[role="progressbar"]').nth(1)).toHaveAttribute('aria-label', /까지 .* 남음/);
|
||||
await expect(dexRows.nth(4).locator('[role="progressbar"]').nth(1)).toHaveAttribute('aria-label', /EX\+ 달성/);
|
||||
|
||||
const texture = await cityBars.first().evaluate((element) => getComputedStyle(element).backgroundImage);
|
||||
const fillTexture = await cityBars
|
||||
.first()
|
||||
@@ -677,6 +671,100 @@ test('main city and general cards render every ref bar plus dual dexterity progr
|
||||
expect(texture).toContain('/game/pr5.gif');
|
||||
expect(fillTexture).toContain('/game/pb5.gif');
|
||||
|
||||
const desktopGeometry = await page.evaluate(() => {
|
||||
const box = (element: Element) => {
|
||||
const rect = element.getBoundingClientRect();
|
||||
return {
|
||||
x: rect.x,
|
||||
y: rect.y,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
right: rect.right,
|
||||
bottom: rect.bottom,
|
||||
};
|
||||
};
|
||||
const target = (name: string) => {
|
||||
const panel = document.querySelector<HTMLElement>(`[data-main-target="${name}"]`);
|
||||
if (!panel) throw new Error(`${name} panel missing`);
|
||||
return panel;
|
||||
};
|
||||
const general = target('general');
|
||||
const nation = target('nation');
|
||||
const city = target('city');
|
||||
const commands = target('commands');
|
||||
const generalBody = general.querySelector<HTMLElement>('.general-body');
|
||||
const editor = commands.querySelector<HTMLElement>('.reserved-command-editor');
|
||||
const controlPad = commands.querySelector<HTMLElement>('.control-pad');
|
||||
const bottomActions = commands.querySelector<HTMLElement>('.bottom-actions');
|
||||
if (!generalBody || !editor || !controlPad || !bottomActions) throw new Error('main layout probe missing');
|
||||
const visibleControlBoxes = [...controlPad.children]
|
||||
.filter((element) => getComputedStyle(element).display !== 'none')
|
||||
.map(box);
|
||||
const bottomActionBoxes = [...bottomActions.children].map(box);
|
||||
return {
|
||||
general: box(general),
|
||||
generalBody: box(generalBody),
|
||||
nation: box(nation),
|
||||
city: box(city),
|
||||
commands: box(commands),
|
||||
editor: box(editor),
|
||||
controlPad: box(controlPad),
|
||||
controlColumns: getComputedStyle(controlPad).gridTemplateColumns,
|
||||
visibleControlBoxes,
|
||||
bottomActionBoxes,
|
||||
commandHorizontalOverflow: commands.scrollWidth - commands.clientWidth,
|
||||
commandVerticalOverflow: commands.scrollHeight - commands.clientHeight,
|
||||
};
|
||||
});
|
||||
expect(desktopGeometry.general.y).toBe(desktopGeometry.nation.y);
|
||||
expect(desktopGeometry.general.bottom).toBe(desktopGeometry.nation.bottom);
|
||||
expect(desktopGeometry.commands.bottom).toBe(desktopGeometry.city.bottom);
|
||||
expect(desktopGeometry.generalBody.bottom).toBeLessThanOrEqual(desktopGeometry.general.bottom);
|
||||
expect(desktopGeometry.editor.right).toBeLessThanOrEqual(desktopGeometry.commands.right);
|
||||
expect(desktopGeometry.commandHorizontalOverflow).toBeLessThanOrEqual(0);
|
||||
expect(desktopGeometry.commandVerticalOverflow).toBeLessThanOrEqual(0);
|
||||
expect(desktopGeometry.controlColumns.split(' ')).toHaveLength(3);
|
||||
expect(desktopGeometry.visibleControlBoxes).toHaveLength(3);
|
||||
expect(new Set(desktopGeometry.visibleControlBoxes.map(({ y }) => y)).size).toBe(1);
|
||||
expect(desktopGeometry.bottomActionBoxes).toHaveLength(3);
|
||||
expect(new Set(desktopGeometry.bottomActionBoxes.map(({ y }) => y)).size).toBe(1);
|
||||
|
||||
const modeButton = page.locator('[data-main-target="commands"] .control-pad').getByRole('button', {
|
||||
name: '고급 모드',
|
||||
});
|
||||
await modeButton.hover();
|
||||
await modeButton.focus();
|
||||
await expect(modeButton).toBeFocused();
|
||||
await modeButton.click();
|
||||
await page.locator('[data-main-target="commands"] .select-command').click();
|
||||
const picker = page.getByTestId('command-picker');
|
||||
await expect(picker).toBeVisible();
|
||||
const pickerGeometry = await picker.evaluate((element) => {
|
||||
const rect = element.getBoundingClientRect();
|
||||
const editor = element.closest('.reserved-command-editor')?.getBoundingClientRect();
|
||||
const categories = element.querySelector<HTMLElement>('.category-list');
|
||||
if (!editor || !categories) throw new Error('command picker geometry missing');
|
||||
return {
|
||||
left: rect.left,
|
||||
right: rect.right,
|
||||
editorLeft: editor.left,
|
||||
editorRight: editor.right,
|
||||
categoryColumns: getComputedStyle(categories).gridTemplateColumns,
|
||||
categoryBoxes: [...categories.children].map((child) => {
|
||||
const childRect = child.getBoundingClientRect();
|
||||
return { x: childRect.x, y: childRect.y, width: childRect.width, height: childRect.height };
|
||||
}),
|
||||
horizontalOverflow: element.scrollWidth - element.clientWidth,
|
||||
};
|
||||
});
|
||||
expect(pickerGeometry.left).toBeGreaterThanOrEqual(pickerGeometry.editorLeft);
|
||||
expect(pickerGeometry.right).toBeLessThanOrEqual(pickerGeometry.editorRight);
|
||||
expect(pickerGeometry.categoryColumns.split(' ')).toHaveLength(3);
|
||||
expect(pickerGeometry.categoryBoxes).toHaveLength(3);
|
||||
expect(new Set(pickerGeometry.categoryBoxes.map(({ y }) => y)).size).toBe(1);
|
||||
expect(pickerGeometry.horizontalOverflow).toBeLessThanOrEqual(0);
|
||||
await picker.getByRole('button', { name: '명령 입력 닫기' }).click();
|
||||
|
||||
const captureProgress = async (name: string) => {
|
||||
if (!artifactRoot) return;
|
||||
await mkdir(artifactRoot, { recursive: true });
|
||||
@@ -696,27 +784,123 @@ test('main city and general cards render every ref bar plus dual dexterity progr
|
||||
};
|
||||
})
|
||||
);
|
||||
const layout = await page.evaluate(() => {
|
||||
const describe = (selector: string) => {
|
||||
const element = document.querySelector<HTMLElement>(selector);
|
||||
if (!element) return null;
|
||||
const rect = element.getBoundingClientRect();
|
||||
return {
|
||||
rect: {
|
||||
x: rect.x,
|
||||
y: rect.y,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
right: rect.right,
|
||||
bottom: rect.bottom,
|
||||
},
|
||||
clientWidth: element.clientWidth,
|
||||
clientHeight: element.clientHeight,
|
||||
scrollWidth: element.scrollWidth,
|
||||
scrollHeight: element.scrollHeight,
|
||||
overflowX: getComputedStyle(element).overflowX,
|
||||
overflowY: getComputedStyle(element).overflowY,
|
||||
boxSizing: getComputedStyle(element).boxSizing,
|
||||
cssWidth: getComputedStyle(element).width,
|
||||
minWidth: getComputedStyle(element).minWidth,
|
||||
paddingInline: getComputedStyle(element).paddingInline,
|
||||
borderInline: `${getComputedStyle(element).borderLeftWidth} ${getComputedStyle(element).borderRightWidth}`,
|
||||
};
|
||||
};
|
||||
return {
|
||||
viewport: { width: innerWidth, height: innerHeight },
|
||||
documentWidth: document.documentElement.scrollWidth,
|
||||
roots: {
|
||||
html: describe('html'),
|
||||
body: describe('body'),
|
||||
app: describe('#app'),
|
||||
main: describe('main.main-page'),
|
||||
},
|
||||
overflowingElements: [...document.body.querySelectorAll<HTMLElement>('*')]
|
||||
.map((element) => {
|
||||
const rect = element.getBoundingClientRect();
|
||||
return {
|
||||
tag: element.tagName.toLowerCase(),
|
||||
className: element.className,
|
||||
testId: element.dataset.testid ?? null,
|
||||
left: rect.left,
|
||||
right: rect.right,
|
||||
width: rect.width,
|
||||
};
|
||||
})
|
||||
.filter(({ left, right }) => left < 0 || right > innerWidth)
|
||||
.slice(0, 20),
|
||||
city: describe('[data-main-target="city"]'),
|
||||
nation: describe('[data-main-target="nation"]'),
|
||||
general: describe('[data-main-target="general"]'),
|
||||
commands: describe('[data-main-target="commands"]'),
|
||||
commandEditor: describe('[data-main-target="commands"] .reserved-command-editor'),
|
||||
commandPicker: describe('[data-testid="command-picker"]'),
|
||||
};
|
||||
});
|
||||
await Promise.all([
|
||||
page.screenshot({ path: resolve(artifactRoot, `progress-bars-${name}.png`), fullPage: true }),
|
||||
writeFile(resolve(artifactRoot, `progress-bars-${name}.json`), `${JSON.stringify(measurement, null, 2)}\n`),
|
||||
writeFile(
|
||||
resolve(artifactRoot, `progress-bars-${name}.json`),
|
||||
`${JSON.stringify({ layout, progress: measurement }, null, 2)}\n`
|
||||
),
|
||||
]);
|
||||
};
|
||||
await captureProgress('desktop-1200');
|
||||
|
||||
await page.locator('[data-main-target="commands"] .control-pad').getByRole('button', { name: '일반 모드' }).click();
|
||||
await page.setViewportSize({ width: 500, height: 900 });
|
||||
await expect(page.locator('.layout-mobile')).toBeVisible();
|
||||
expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(500);
|
||||
await expect(page.locator('[data-main-target="city"] [role="progressbar"]')).toHaveCount(8);
|
||||
await expect(page.locator('[data-main-target="general"] [role="progressbar"]')).toHaveCount(14);
|
||||
await expect(page.locator('[data-main-target="general"] [role="progressbar"]')).toHaveCount(4);
|
||||
expect(
|
||||
await page
|
||||
.locator('[data-main-target="city"] [role="progressbar"]')
|
||||
.first()
|
||||
.evaluate((element) => element.getBoundingClientRect().height)
|
||||
).toBe(9);
|
||||
const mobileGeometry = await page.locator('[data-main-target="commands"]').evaluate((commands) => {
|
||||
const editor = commands.querySelector<HTMLElement>('.reserved-command-editor');
|
||||
const controls = commands.querySelector<HTMLElement>('.control-pad');
|
||||
if (!editor || !controls) throw new Error('mobile command layout probe missing');
|
||||
const panelRect = commands.getBoundingClientRect();
|
||||
const editorRect = editor.getBoundingClientRect();
|
||||
const controlBoxes = [...controls.children]
|
||||
.filter((element) => getComputedStyle(element).display !== 'none')
|
||||
.map((element) => {
|
||||
const rect = element.getBoundingClientRect();
|
||||
return { y: rect.y, width: rect.width };
|
||||
});
|
||||
return {
|
||||
panelWidth: panelRect.width,
|
||||
panelHeight: panelRect.height,
|
||||
editorLeft: editorRect.left,
|
||||
editorRight: editorRect.right,
|
||||
panelLeft: panelRect.left,
|
||||
panelRight: panelRect.right,
|
||||
horizontalOverflow: commands.scrollWidth - commands.clientWidth,
|
||||
verticalOverflow: commands.scrollHeight - commands.clientHeight,
|
||||
controlColumns: getComputedStyle(controls).gridTemplateColumns,
|
||||
controlBoxes,
|
||||
};
|
||||
});
|
||||
expect(mobileGeometry.panelHeight).toBe(645);
|
||||
expect(mobileGeometry.editorLeft).toBeGreaterThanOrEqual(mobileGeometry.panelLeft);
|
||||
expect(mobileGeometry.editorRight).toBeLessThanOrEqual(mobileGeometry.panelRight);
|
||||
expect(mobileGeometry.horizontalOverflow).toBeLessThanOrEqual(0);
|
||||
expect(mobileGeometry.verticalOverflow).toBeLessThanOrEqual(0);
|
||||
expect(mobileGeometry.controlColumns.split(' ')).toHaveLength(3);
|
||||
expect(mobileGeometry.controlBoxes).toHaveLength(3);
|
||||
expect(new Set(mobileGeometry.controlBoxes.map(({ y }) => y)).size).toBe(1);
|
||||
await captureProgress('mobile-500');
|
||||
});
|
||||
|
||||
test('the 939/940 boundary switches to the Ref-style 502px single document', async ({ page }) => {
|
||||
test('the 939/940 boundary switches to the Ref-style 500px single document', async ({ page }) => {
|
||||
const state: NavigationFixture = {
|
||||
officerLevel: 5,
|
||||
permission: 2,
|
||||
@@ -738,7 +922,7 @@ test('the 939/940 boundary switches to the Ref-style 502px single document', asy
|
||||
await expect(page.locator('.layout-mobile')).toBeVisible();
|
||||
expect(await gridColumnCount(page, '.main-global-menu')).toBe(4);
|
||||
expect(await gridColumnCount(page, '.main-nation-menu')).toBe(5);
|
||||
await expect(page.locator('.main-mobile-bottom')).toHaveCount(0);
|
||||
await expect(page.locator('.main-mobile-bottom')).toBeVisible();
|
||||
|
||||
await page.setViewportSize({ width: 500, height: 900 });
|
||||
const documentGeometry = await page.locator('.main-page').evaluate((element) => {
|
||||
@@ -752,8 +936,8 @@ test('the 939/940 boundary switches to the Ref-style 502px single document', asy
|
||||
});
|
||||
expect(documentGeometry).toMatchObject({
|
||||
x: 0,
|
||||
width: 502,
|
||||
scrollWidth: 502,
|
||||
width: 500,
|
||||
scrollWidth: 500,
|
||||
});
|
||||
expect(documentGeometry.height).toBeGreaterThan(900);
|
||||
for (const selector of [
|
||||
|
||||
@@ -246,8 +246,8 @@ const clickOutsideMenu = (event: Event) => {
|
||||
<strong>{{ props.name ?? '-' }}</strong
|
||||
><span>{{ props.title }}</span>
|
||||
</div>
|
||||
<div class="clock">{{ props.currentTime }}</div>
|
||||
<button type="button" @click="editMode = !editMode">{{ editMode ? '일반 모드' : '고급 모드' }}</button>
|
||||
<div class="clock">{{ props.currentTime }}</div>
|
||||
<details class="legacy-menu">
|
||||
<summary>반복</summary>
|
||||
<div class="menu-items">
|
||||
@@ -364,7 +364,7 @@ const clickOutsideMenu = (event: Event) => {
|
||||
</details>
|
||||
</template>
|
||||
|
||||
<details class="legacy-menu">
|
||||
<details v-if="props.compact" class="legacy-menu">
|
||||
<summary>당기기</summary>
|
||||
<div class="menu-items">
|
||||
<button
|
||||
@@ -379,7 +379,7 @@ const clickOutsideMenu = (event: Event) => {
|
||||
</button>
|
||||
</div>
|
||||
</details>
|
||||
<details class="legacy-menu">
|
||||
<details v-if="props.compact" class="legacy-menu">
|
||||
<summary>미루기</summary>
|
||||
<div class="menu-items">
|
||||
<button
|
||||
|
||||
@@ -169,7 +169,7 @@ const commandTitle = (command: CommandAvailability) =>
|
||||
|
||||
.category-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { computed } from 'vue';
|
||||
import SkeletonLines from '../ui/SkeletonLines.vue';
|
||||
import LegacyProgressBar from '../ui/LegacyProgressBar.vue';
|
||||
import { dexProgress, legacyExperiencePercent, ratioPercent } from '../../utils/legacyProgress';
|
||||
import { legacyExperiencePercent, ratioPercent } from '../../utils/legacyProgress';
|
||||
|
||||
interface GeneralStats {
|
||||
leadership: number;
|
||||
@@ -15,7 +15,6 @@ interface GeneralProgression {
|
||||
dedicationLevel: number;
|
||||
statExperience?: { leadership: number; strength: number; intelligence: number };
|
||||
statUpgradeLimit?: number;
|
||||
dex: number[];
|
||||
}
|
||||
|
||||
interface GeneralInfo {
|
||||
@@ -66,14 +65,6 @@ const statRows = computed(() => {
|
||||
].map((entry) => ({ ...entry, limit, percent: ratioPercent(entry.accumulated, limit) }));
|
||||
});
|
||||
|
||||
const dexRows = computed(() => {
|
||||
const dex = props.general?.progression?.dex ?? [];
|
||||
return ['보병', '궁병', '기병', '귀병', '차병'].map((label, index) => {
|
||||
const value = dex[index] ?? 0;
|
||||
return { label, value, progress: dexProgress(value) };
|
||||
});
|
||||
});
|
||||
|
||||
const experiencePercent = computed(() =>
|
||||
legacyExperiencePercent(props.general?.experience ?? 0, props.general?.progression?.experienceLevel ?? 0)
|
||||
);
|
||||
@@ -130,35 +121,14 @@ const experiencePercent = computed(() =>
|
||||
</div>
|
||||
<span class="experience-total">명성 {{ props.general.experience.toLocaleString() }}</span>
|
||||
</div>
|
||||
|
||||
<div class="dex-title">숙 련 도</div>
|
||||
<div v-for="row of dexRows" :key="row.label" class="dex-row" :data-dex-progress="row.label">
|
||||
<span class="cell-label">{{ row.label }}</span>
|
||||
<strong :style="{ color: row.progress.color }">{{ row.progress.name }}</strong>
|
||||
<span class="dex-value">{{ (row.value / 1_000).toFixed(1) }}K</span>
|
||||
<div class="dex-bars">
|
||||
<LegacyProgressBar
|
||||
:percent="row.progress.overallPercent"
|
||||
:label="`${row.label} 전체 ${row.value.toLocaleString()} / 1,275,975 (EX+)`"
|
||||
/>
|
||||
<LegacyProgressBar
|
||||
:height="7"
|
||||
variant="grade"
|
||||
:percent="row.progress.gradePercent"
|
||||
:label="
|
||||
row.progress.nextName
|
||||
? `${row.label} ${row.progress.name}에서 ${row.progress.nextName}까지 ${row.progress.remaining.toLocaleString()} 남음`
|
||||
: `${row.label} EX+ 달성`
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.general-title {
|
||||
box-sizing: border-box;
|
||||
height: 20px;
|
||||
min-height: 20px;
|
||||
padding: 1px 6px;
|
||||
border-bottom: 1px solid #777;
|
||||
@@ -171,13 +141,15 @@ const experiencePercent = computed(() =>
|
||||
.stat-progress-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(30px, 1fr) minmax(34px, 1fr) 45px);
|
||||
grid-auto-rows: 21px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.stat-progress-grid > *,
|
||||
.legacy-grid > * {
|
||||
box-sizing: border-box;
|
||||
min-height: 22px;
|
||||
height: 21px;
|
||||
min-height: 0;
|
||||
border-right: 1px solid #666;
|
||||
border-bottom: 1px solid #666;
|
||||
padding: 2px 4px;
|
||||
@@ -186,8 +158,7 @@ const experiencePercent = computed(() =>
|
||||
}
|
||||
|
||||
.cell-label,
|
||||
.legacy-grid > span,
|
||||
.dex-title {
|
||||
.legacy-grid > span {
|
||||
background: rgb(20 75 42 / 70%);
|
||||
text-align: center;
|
||||
}
|
||||
@@ -207,13 +178,16 @@ const experiencePercent = computed(() =>
|
||||
.legacy-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||
grid-auto-rows: 21px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.experience-row {
|
||||
display: grid;
|
||||
box-sizing: border-box;
|
||||
grid-template-columns: 32px 38px minmax(120px, 1fr) 112px;
|
||||
min-height: 22px;
|
||||
height: 20px;
|
||||
min-height: 20px;
|
||||
border-bottom: 1px solid #666;
|
||||
font-size: 12px;
|
||||
}
|
||||
@@ -227,44 +201,6 @@ const experiencePercent = computed(() =>
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dex-title {
|
||||
min-height: 20px;
|
||||
border-bottom: 1px solid #666;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.dex-row {
|
||||
display: grid;
|
||||
grid-template-columns: 64px 40px 60px minmax(0, 1fr);
|
||||
min-height: 26px;
|
||||
border-bottom: 1px solid #666;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.dex-row > * {
|
||||
display: grid;
|
||||
align-content: center;
|
||||
box-sizing: border-box;
|
||||
border-right: 1px solid #666;
|
||||
padding: 0 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dex-row > strong {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.dex-value {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.dex-bars {
|
||||
gap: 1px;
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
.empty {
|
||||
color: rgba(232, 221, 196, 0.6);
|
||||
}
|
||||
|
||||
@@ -585,7 +585,7 @@ button {
|
||||
.layout-desktop > [data-main-target='commands'] {
|
||||
grid-column: 8 / 11;
|
||||
grid-row: 1;
|
||||
height: 589px;
|
||||
height: 645px;
|
||||
width: 290px;
|
||||
margin-left: 10px;
|
||||
overflow-y: auto;
|
||||
@@ -719,7 +719,7 @@ button {
|
||||
}
|
||||
|
||||
.layout-mobile [data-main-target='commands'] {
|
||||
height: 586px;
|
||||
height: 645px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@@ -780,7 +780,7 @@ button {
|
||||
}
|
||||
|
||||
.main-page {
|
||||
width: 502px;
|
||||
width: 500px;
|
||||
min-height: 3688px;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user