ui: measure editor sidebar height

This commit is contained in:
2026-04-06 13:44:16 +09:00
parent 360ec5ac3d
commit 08ec6f42d1
3 changed files with 41 additions and 3 deletions

View File

@@ -1,5 +1,8 @@
# 의사결정 이력
## 2026-04-06 v1.4.97
- 티어표 편집기의 오른쪽 아이템 패널은 페이지 내부 위치가 헤더, 제목, 스크롤 상태에 따라 달라지므로 `100dvh - 고정값` 방식으로는 왼쪽 레일처럼 하단이 자연스럽게 맞지 않을 수 있다. 실제 패널의 화면 내 시작 위치를 측정해 남은 높이를 계산하는 편이 더 안정적이라고 정리했다.
## 2026-04-06 v1.4.96
- 템플릿 제목을 버튼화하면 접근성은 좋아지지만, 포커스가 남은 상태의 `Space` 입력이 브라우저 스크롤과 섞이면 작업 화면을 갑자기 밀어낼 수 있다. 따라서 제목 버튼에서는 `Space` 기본 스크롤을 막고 의도한 본문 이동만 실행하는 편이 맞다고 정리했다.

View File

@@ -1,5 +1,9 @@
# 업데이트 로그
## 2026-04-06 v1.4.97
- 티어표 편집 화면의 오른쪽 아이템 패널 높이를 고정 숫자 대신 실제 화면 내 시작 위치 기준으로 계산하도록 바꿨다. 공통 헤더/제목 영역/스크롤 위치가 달라져도 아이템 풀의 하단이 viewport 안에 더 자연스럽게 맞도록 보정했다.
- 확인: `npm run build`
## 2026-04-06 v1.4.96
- 티어표 편집 화면의 템플릿 제목에 포커스가 남은 상태에서 `Space`를 누르면 브라우저 기본 스크롤이 섞일 수 있어, 제목 버튼의 `Space` 기본 동작을 막고 본문 이동만 실행되도록 보정했다.
- 확인: `npm run build`

View File

@@ -90,6 +90,7 @@ let editorLoadToken = 0
const boardEl = ref(null)
const exportBoardEl = ref(null)
const groupListEl = ref(null)
const sidebarEl = ref(null)
const poolEl = ref(null)
const groupDropEls = ref({})
const fileEl = ref(null)
@@ -97,6 +98,8 @@ const thumbnailFileEl = ref(null)
const groupSortable = ref(null)
const poolSortable = ref(null)
const dropSortables = ref([])
const editorSidebarMaxHeight = ref('')
let editorSidebarMeasureFrame = 0
const isNewTierList = computed(() => tierListId.value === 'new')
const isOwnTierList = computed(() => !!auth.user && !!ownerId.value && ownerId.value === auth.user.id)
@@ -364,6 +367,25 @@ function scrollWorkspaceBodyToTop() {
workspaceBody?.scrollIntoView({ behavior: 'smooth', block: 'start' })
}
function updateEditorSidebarMaxHeight() {
if (typeof window === 'undefined' || !sidebarEl.value) return
const bottomGap = 14
const stickyTop = 14
const minHeight = 260
const sidebarTop = Math.max(sidebarEl.value.getBoundingClientRect().top, stickyTop)
const nextHeight = Math.max(minHeight, Math.floor(window.innerHeight - sidebarTop - bottomGap))
editorSidebarMaxHeight.value = `${nextHeight}px`
}
function scheduleEditorSidebarMeasure() {
if (typeof window === 'undefined') return
if (editorSidebarMeasureFrame) return
editorSidebarMeasureFrame = window.requestAnimationFrame(() => {
editorSidebarMeasureFrame = 0
updateEditorSidebarMaxHeight()
})
}
function openItemContextMenu(itemId, event) {
if (!canEdit.value || !itemId || !itemsById.value[itemId] || shouldIgnoreItemClick()) return
selectedItemId.value = itemId
@@ -1355,6 +1377,7 @@ async function loadEditorState() {
if (loadToken !== editorLoadToken) return
syncSavedEditorSnapshot()
scheduleEditorSidebarMeasure()
if (canEdit.value) {
await initSortables()
}
@@ -1374,6 +1397,9 @@ onMounted(() => {
window.addEventListener('contextmenu', handleGlobalContextMenu, true)
window.addEventListener('blur', closeItemContextMenu)
window.addEventListener('scroll', closeItemContextMenu, true)
window.addEventListener('resize', scheduleEditorSidebarMeasure)
window.addEventListener('scroll', scheduleEditorSidebarMeasure, true)
nextTick(() => scheduleEditorSidebarMeasure())
})
onUnmounted(() => {
@@ -1382,6 +1408,12 @@ onUnmounted(() => {
window.removeEventListener('contextmenu', handleGlobalContextMenu, true)
window.removeEventListener('blur', closeItemContextMenu)
window.removeEventListener('scroll', closeItemContextMenu, true)
window.removeEventListener('resize', scheduleEditorSidebarMeasure)
window.removeEventListener('scroll', scheduleEditorSidebarMeasure, true)
if (editorSidebarMeasureFrame) {
window.cancelAnimationFrame(editorSidebarMeasureFrame)
editorSidebarMeasureFrame = 0
}
}
if (thumbnailPreviewUrl.value) URL.revokeObjectURL(thumbnailPreviewUrl.value)
destroySortables()
@@ -1769,7 +1801,7 @@ onUnmounted(() => {
</div>
</div>
<div class="sidebar">
<div ref="sidebarEl" class="sidebar" :style="{ '--editor-sidebar-max-height': editorSidebarMaxHeight || undefined }">
<div class="sidebar__titleRow">
<div class="sidebar__title">아이템</div>
<div class="sidebar__count">{{ visiblePoolCount }} / {{ pool.length }}</div>
@@ -2774,7 +2806,6 @@ onUnmounted(() => {
object-fit: cover;
}
.sidebar {
--editor-sidebar-visible-offset: 136px;
min-width: 0;
display: flex;
flex-direction: column;
@@ -2785,7 +2816,7 @@ onUnmounted(() => {
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
position: sticky;
top: 14px;
max-height: calc(100dvh - var(--editor-sidebar-visible-offset));
max-height: var(--editor-sidebar-max-height, calc(100dvh - 136px));
overflow: hidden;
overscroll-behavior: contain;
}