암행부와 현재 도시 모바일 명령 폭을 줄이고 장수 배치 통일
This commit is contained in:
@@ -233,6 +233,8 @@ const measure = async (page: Page, root: string, table: string, name: string) =>
|
|||||||
rows: rows.map((row) => ({
|
rows: rows.map((row) => ({
|
||||||
rect: rect(row),
|
rect: rect(row),
|
||||||
cells: Array.from(row.querySelectorAll('td'), (cell) => ({
|
cells: Array.from(row.querySelectorAll('td'), (cell) => ({
|
||||||
|
field: cell.dataset.field,
|
||||||
|
gridArea: getComputedStyle(cell).gridArea,
|
||||||
rect: rect(cell),
|
rect: rect(cell),
|
||||||
text: cell.innerText.replace(/\s+/g, ''),
|
text: cell.innerText.replace(/\s+/g, ''),
|
||||||
scrollWidth: cell.scrollWidth,
|
scrollWidth: cell.scrollWidth,
|
||||||
@@ -287,6 +289,17 @@ for (const [route, root, table] of [
|
|||||||
expect(mobile.rows.map((row) => row.cells.map((cell) => cell.text))).toEqual(
|
expect(mobile.rows.map((row) => row.cells.map((cell) => cell.text))).toEqual(
|
||||||
desktop.rows.map((row) => row.cells.map((cell) => cell.text))
|
desktop.rows.map((row) => row.cells.map((cell) => cell.text))
|
||||||
);
|
);
|
||||||
|
// Every general shares the same columns, including NPCs and empty reservations.
|
||||||
|
for (const row of mobile.rows) {
|
||||||
|
const turns = row.cells.find((cell) => cell.field === 'turns')!;
|
||||||
|
expect(turns.rect.width).toBeCloseTo(166, 0);
|
||||||
|
for (const [index, cell] of row.cells.entries()) {
|
||||||
|
const reference = mobile.rows[0]!.cells[index]!;
|
||||||
|
expect(cell.gridArea).toBe(reference.gridArea);
|
||||||
|
expect(cell.rect.x).toBeCloseTo(reference.rect.x, 1);
|
||||||
|
expect(cell.rect.width).toBeCloseTo(reference.rect.width, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
for (const row of mobile.rows)
|
for (const row of mobile.rows)
|
||||||
for (const cell of row.cells) {
|
for (const cell of row.cells) {
|
||||||
expect(cell.rect.width).toBeGreaterThan(0);
|
expect(cell.rect.width).toBeGreaterThan(0);
|
||||||
@@ -378,7 +391,14 @@ test('idle and empty information lists stay compact; doubled text wraps without
|
|||||||
const desktop = await measure(page, root, table, `idle-${route.replace('/', '-')}-1000`);
|
const desktop = await measure(page, root, table, `idle-${route.replace('/', '-')}-1000`);
|
||||||
await page.setViewportSize({ width: 500, height: 900 });
|
await page.setViewportSize({ width: 500, height: 900 });
|
||||||
const mobile = await measure(page, root, table, `idle-${route.replace('/', '-')}-500`);
|
const mobile = await measure(page, root, table, `idle-${route.replace('/', '-')}-500`);
|
||||||
expect(mobile.table.height / desktop.table.height).toBeLessThan(1.8);
|
if (route === 'nation/secret') {
|
||||||
|
// The uniform four-line layout intentionally replaces the former NPC-only two-line layout.
|
||||||
|
// Bound the ordinary row directly; desktop idle rows have no command height to share.
|
||||||
|
for (const row of mobile.rows.slice(1)) expect(row.rect.height).toBeLessThan(76);
|
||||||
|
expect(mobile.table.height).toBeLessThan(7600);
|
||||||
|
} else {
|
||||||
|
expect(mobile.table.height / desktop.table.height).toBeLessThan(1.8);
|
||||||
|
}
|
||||||
expect(mobile.document.width).toBe(500);
|
expect(mobile.document.width).toBe(500);
|
||||||
await page.unroute(gameTrpcRoute);
|
await page.unroute(gameTrpcRoute);
|
||||||
await install(page);
|
await install(page);
|
||||||
|
|||||||
@@ -514,7 +514,7 @@ const commandBrief = (command: ReservedCommand): string =>
|
|||||||
}
|
}
|
||||||
.generals tbody tr {
|
.generals tbody tr {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 64px 62px 68px 40px 40px minmax(0, 1fr);
|
grid-template-columns: 64px 74px 90px 52px 52px 166px;
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
'icon name name name name turns'
|
'icon name name name name turns'
|
||||||
'icon lead str intel intel turns'
|
'icon lead str intel intel turns'
|
||||||
@@ -539,6 +539,7 @@ const commandBrief = (command: ReservedCommand): string =>
|
|||||||
content: attr(data-label);
|
content: attr(data-label);
|
||||||
margin-right: 3px;
|
margin-right: 3px;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
|
line-height: 1;
|
||||||
color: #bbb;
|
color: #bbb;
|
||||||
}
|
}
|
||||||
.generals [data-field='train'],
|
.generals [data-field='train'],
|
||||||
|
|||||||
@@ -241,7 +241,6 @@ onMounted(load);
|
|||||||
v-for="general in generals"
|
v-for="general in generals"
|
||||||
:key="general.id"
|
:key="general.id"
|
||||||
:data-general-id="general.id"
|
:data-general-id="general.id"
|
||||||
:class="{ 'has-commands': general.npcState < 2 && general.reservedCommands.length > 0 }"
|
|
||||||
:data-npc-state="general.npcState"
|
:data-npc-state="general.npcState"
|
||||||
>
|
>
|
||||||
<td data-field="name">
|
<td data-field="name">
|
||||||
@@ -525,8 +524,7 @@ th,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 500px에서는 명령을 숨기지 않고 그 옆 높이를 통계에 사용한다.
|
/* 장수 종류와 예약 유무에 관계없이 정보 2/3, 명령 1/3의 같은 배치를 사용한다. */
|
||||||
* 예약이 없는 장수는 전체 폭 2행으로 배치해 빈 명령 칸 때문에 길어지지 않는다. */
|
|
||||||
@media (max-width: 939.98px) {
|
@media (max-width: 939.98px) {
|
||||||
.secret-page {
|
.secret-page {
|
||||||
width: 500px;
|
width: 500px;
|
||||||
@@ -573,20 +571,16 @@ th,
|
|||||||
}
|
}
|
||||||
.list tbody tr {
|
.list tbody tr {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(100, minmax(0, 1fr));
|
grid-template-columns: repeat(12, minmax(0, 1fr)) 166px;
|
||||||
|
grid-template-areas:
|
||||||
|
'name name name name name name stats stats stats stats stats stats turns'
|
||||||
|
'troop troop troop troop troop troop city city city city defence defence turns'
|
||||||
|
'gold gold gold gold gold rice rice rice rice time time time turns'
|
||||||
|
'type type type crew crew crew train train atmos atmos kill kill turns';
|
||||||
height: auto;
|
height: auto;
|
||||||
border: 1px solid gray;
|
border: 1px solid gray;
|
||||||
border-top: 0;
|
border-top: 0;
|
||||||
}
|
}
|
||||||
.list tbody tr.has-commands {
|
|
||||||
grid-template-columns: 64px 70px 70px 70px minmax(0, 1fr);
|
|
||||||
grid-template-areas:
|
|
||||||
'name name stats stats turns'
|
|
||||||
'troop troop city city turns'
|
|
||||||
'gold gold rice rice turns'
|
|
||||||
'type crew train atmos turns'
|
|
||||||
'defence kill time time turns';
|
|
||||||
}
|
|
||||||
.list tbody td {
|
.list tbody td {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
padding: 0 2px;
|
padding: 0 2px;
|
||||||
@@ -600,6 +594,7 @@ th,
|
|||||||
content: attr(data-label);
|
content: attr(data-label);
|
||||||
margin-right: 3px;
|
margin-right: 3px;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
|
line-height: 1;
|
||||||
color: #bbb;
|
color: #bbb;
|
||||||
}
|
}
|
||||||
.list tbody td[data-field='name'] br {
|
.list tbody td[data-field='name'] br {
|
||||||
@@ -657,70 +652,5 @@ th,
|
|||||||
.list [data-field='time'] {
|
.list [data-field='time'] {
|
||||||
grid-area: time;
|
grid-area: time;
|
||||||
}
|
}
|
||||||
/* 예약이 없으면 서로 다른 열폭을 가진 2행을 100등분 grid 위에 놓는다.
|
|
||||||
* 첫 행: 이름/능력/부대/도시/NPC, 둘째 행: 자원/병력/훈사/턴. */
|
|
||||||
.list tbody tr:not(.has-commands) td {
|
|
||||||
grid-area: auto;
|
|
||||||
padding-inline: 1px;
|
|
||||||
}
|
|
||||||
.list tbody tr:not(.has-commands) td::before {
|
|
||||||
margin-right: 2px;
|
|
||||||
}
|
|
||||||
.list tbody tr:not(.has-commands) [data-field='name'] {
|
|
||||||
grid-row: 1;
|
|
||||||
grid-column: 1 / span 27;
|
|
||||||
}
|
|
||||||
.list tbody tr:not(.has-commands) [data-field='stats'] {
|
|
||||||
grid-row: 1;
|
|
||||||
grid-column: 28 / span 28;
|
|
||||||
}
|
|
||||||
.list tbody tr:not(.has-commands) [data-field='troop'] {
|
|
||||||
grid-row: 1;
|
|
||||||
grid-column: 56 / span 18;
|
|
||||||
}
|
|
||||||
.list tbody tr:not(.has-commands) [data-field='city'] {
|
|
||||||
grid-row: 1;
|
|
||||||
grid-column: 74 / span 12;
|
|
||||||
}
|
|
||||||
.list tbody tr:not(.has-commands) [data-field='turns'] {
|
|
||||||
grid-row: 1;
|
|
||||||
grid-column: 86 / span 15;
|
|
||||||
}
|
|
||||||
.list tbody tr:not(.has-commands) [data-field='gold'] {
|
|
||||||
grid-row: 2;
|
|
||||||
grid-column: 1 / span 19;
|
|
||||||
}
|
|
||||||
.list tbody tr:not(.has-commands) [data-field='rice'] {
|
|
||||||
grid-row: 2;
|
|
||||||
grid-column: 20 / span 16;
|
|
||||||
}
|
|
||||||
.list tbody tr:not(.has-commands) [data-field='defence'] {
|
|
||||||
grid-row: 2;
|
|
||||||
grid-column: 36 / span 7;
|
|
||||||
}
|
|
||||||
.list tbody tr:not(.has-commands) [data-field='type'] {
|
|
||||||
grid-row: 2;
|
|
||||||
grid-column: 43 / span 11;
|
|
||||||
}
|
|
||||||
.list tbody tr:not(.has-commands) [data-field='crew'] {
|
|
||||||
grid-row: 2;
|
|
||||||
grid-column: 54 / span 14;
|
|
||||||
}
|
|
||||||
.list tbody tr:not(.has-commands) [data-field='train'] {
|
|
||||||
grid-row: 2;
|
|
||||||
grid-column: 68 / span 8;
|
|
||||||
}
|
|
||||||
.list tbody tr:not(.has-commands) [data-field='atmos'] {
|
|
||||||
grid-row: 2;
|
|
||||||
grid-column: 76 / span 8;
|
|
||||||
}
|
|
||||||
.list tbody tr:not(.has-commands) [data-field='kill'] {
|
|
||||||
grid-row: 2;
|
|
||||||
grid-column: 84 / span 6;
|
|
||||||
}
|
|
||||||
.list tbody tr:not(.has-commands) [data-field='time'] {
|
|
||||||
grid-row: 2;
|
|
||||||
grid-column: 90 / span 11;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -125,12 +125,17 @@ the selected radio option so the user can retry.
|
|||||||
`app/game-frontend/e2e/infoMobileLayout.spec.ts` verifies the intentional Core
|
`app/game-frontend/e2e/infoMobileLayout.spec.ts` verifies the intentional Core
|
||||||
500px layouts for `/nation/secret` and `/current-city`. Both keep every visible
|
500px layouts for `/nation/secret` and `/current-city`. Both keep every visible
|
||||||
cell, command and image from the desktop dataset. Statistics share vertical space
|
cell, command and image from the desktop dataset. Statistics share vertical space
|
||||||
with reserved commands; current-city portraits remain 64px. Empty reservation
|
with reserved commands; current-city portraits remain 64px. Both lists reserve
|
||||||
cells in the secret list use a shorter full-width two-row layout.
|
166px of the 500px table for commands and 332px for information (plus borders).
|
||||||
|
Secret rows use the same four-line field positions for players, NPCs and empty
|
||||||
|
reservations. Long content wraps without truncation.
|
||||||
|
|
||||||
The fixture measures 100 generals with five-command and NPC rows at CSS viewport
|
The fixture measures 100 generals with five-command and NPC rows at CSS viewport
|
||||||
widths 1000, 500, 501 and 800. The mixed-list height must stay below 1.5 times the
|
widths 1000, 500, 501 and 800. The mixed-list height must stay below 1.5 times the
|
||||||
same desktop list, and page height below 1.55 times; idle lists stay below 1.8 times.
|
same desktop list, and page height below 1.55 times. Idle current-city lists stay
|
||||||
|
below 1.8 times; ordinary idle secret rows stay below 76px and the 100-row fixture
|
||||||
|
below 7600px. The secret idle limit replaces the old NPC-specific two-row limit
|
||||||
|
to preserve consistent field positions, as requested in the follow-up review.
|
||||||
These limits apply to the deterministic fixture, not arbitrary-length user text.
|
These limits apply to the deterministic fixture, not arbitrary-length user text.
|
||||||
It also verifies exact cell text preservation, no horizontal overflow, sorting,
|
It also verifies exact cell text preservation, no horizontal overflow, sorting,
|
||||||
injury tooltip interaction, city selection, empty lists, doubled text, and actual
|
injury tooltip interaction, city selection, empty lists, doubled text, and actual
|
||||||
|
|||||||
Reference in New Issue
Block a user