사령부 명령 목록을 자기 수뇌 열과 클릭한 턴에 맞춘다

1000px 사령부 desktop에서 compact 명령 목록이 `position: fixed`로 첫째 줄 첫째 열에
고정되어, 다른 수뇌 자리에서 입력해도 군주 열 위에 떠 있던 문제를 고친다.
Ref ChiefReservedCommand처럼 목록을 자기 수뇌 열 안에 두고, 턴을 누르면 그 행
바로 아래에, 고급 모드의 턴 미지정 목록은 턴 표 하단 기준으로 위로 펼친다.
수뇌 12·6·9·5 위치별 Chromium geometry 회귀 테스트를 추가한다.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-23 16:49:45 +00:00
co-authored by Claude Opus 5.5
parent 08be92b6fa
commit 3c75de16e9
2 changed files with 150 additions and 34 deletions
@@ -135,7 +135,7 @@ const labelMap = computed(() => {
const displayRows = computed(() =>
props.rows.slice(0, expanded.value || props.compact ? props.rows.length : collapsedRowCount)
);
const quickPickerTop = ref('38px');
const quickPickerStyle = ref<Record<string, string> | undefined>();
const isRecruitmentCommand = computed(
() => selectedCommand.value?.key === 'che_징병' || selectedCommand.value?.key === 'che_모병'
);
@@ -183,20 +183,23 @@ const finishDrag = (next: Set<number>) => {
dragKind.value = null;
};
const openPicker = (turnIndex?: number) => {
if (!props.compact && editorElement.value) {
const editorRect = editorElement.value.getBoundingClientRect();
const anchor =
turnIndex === undefined
? editorElement.value.querySelector<HTMLElement>('.control-pad')
: editorElement.value.querySelector<HTMLElement>(`[data-turn-index="${turnIndex}"]`);
if (anchor) {
const anchorRect = anchor.getBoundingClientRect();
quickPickerTop.value = `${
turnIndex === undefined ? anchorRect.bottom - editorRect.top : anchorRect.top - editorRect.top + 30
}px`;
}
// 명령 목록 popup은 편집기(사령부에서는 자기 수뇌 열) 안에서 위치를 잡는다. 턴을 누르면 Ref
// commandQuickReserveFormAnchor처럼 그 행 바로 아래에 붙이고, 사령부 고급 모드의 턴 미지정 목록은 Ref 편집 모드
// CommandSelectForm(`bottom: 0`)처럼 턴 표 하단에 맞춰 위로 펼친다. 모바일 사령부와 장수 턴 미지정은 CSS 기본 위치다.
const anchoredPickerStyle = (turnIndex?: number): Record<string, string> | undefined => {
const editor = editorElement.value;
if (!editor || (props.compact && props.mobile)) return undefined;
const editorRect = editor.getBoundingClientRect();
if (turnIndex !== undefined) {
const row = editor.querySelector<HTMLElement>(`[data-turn-index="${turnIndex}"]`);
return row ? { top: `${row.getBoundingClientRect().top - editorRect.top + 30}px` } : undefined;
}
if (!props.compact) return undefined;
const queue = editor.querySelector<HTMLElement>('.queue-area');
return queue ? { top: 'auto', bottom: `${editorRect.bottom - queue.getBoundingClientRect().bottom}px` } : undefined;
};
const openPicker = (turnIndex?: number) => {
quickPickerStyle.value = anchoredPickerStyle(turnIndex);
quickTarget.value = turnIndex ?? null;
pickerOpen.value = true;
selectedCommand.value = null;
@@ -805,9 +808,7 @@ const clickOutsideMenu = (event: Event) => {
class="command-picker"
:class="{ 'argument-overlay': isArgumentOverlay, 'recruitment-picker': isRecruitmentCommand }"
data-testid="command-picker"
:style="
isArgumentOverlay || quickTarget === null || props.compact ? undefined : { top: quickPickerTop }
"
:style="isArgumentOverlay ? undefined : quickPickerStyle"
:role="isArgumentOverlay ? 'dialog' : undefined"
:aria-modal="isArgumentOverlay ? 'true' : undefined"
:aria-label="
@@ -1460,17 +1461,6 @@ small {
}
}
@media (min-width: 1025px) {
.compact:not(.mobile) .command-picker {
position: fixed;
z-index: 1000;
top: 86px;
right: auto;
left: calc(50% - 476px);
width: 238px;
}
}
.mobile.compact .editor-layout {
min-height: 370px;
display: grid;