이미지 목록 화면 오른쪽에 스크롤바 추가
This commit is contained in:
@@ -7,6 +7,8 @@ import { buildSequentialName, getNameFormat } from './utils/file-names.js';
|
||||
const grid = document.getElementById('grid');
|
||||
const emptyState = document.getElementById('empty-state');
|
||||
const gridContainer = document.getElementById('grid-container');
|
||||
const gridScrollbar = document.getElementById('grid-scrollbar');
|
||||
const gridScrollbarThumb = document.getElementById('grid-scrollbar-thumb');
|
||||
const fileCount = document.getElementById('file-count');
|
||||
const selCount = document.getElementById('sel-count');
|
||||
const inputPrefix = document.getElementById('input-prefix');
|
||||
@@ -14,6 +16,103 @@ const inputDigits = document.getElementById('input-digits');
|
||||
const inputPostfix = document.getElementById('input-postfix');
|
||||
|
||||
let _cardObserver = null;
|
||||
let _scrollStateRaf = null;
|
||||
let _scrollbarDrag = null;
|
||||
|
||||
function updateGridScrollState() {
|
||||
if (_scrollStateRaf) cancelAnimationFrame(_scrollStateRaf);
|
||||
_scrollStateRaf = requestAnimationFrame(() => {
|
||||
_scrollStateRaf = null;
|
||||
const isScrollable = gridContainer.scrollHeight > gridContainer.clientHeight;
|
||||
gridContainer.classList.toggle('is-scrollable', isScrollable);
|
||||
updateGridScrollbarFrame();
|
||||
updateGridScrollbar();
|
||||
});
|
||||
}
|
||||
|
||||
function updateGridScrollbarFrame() {
|
||||
if (!gridScrollbar) return;
|
||||
|
||||
const rect = gridContainer.getBoundingClientRect();
|
||||
const top = rect.top + 20;
|
||||
const height = Math.max(0, rect.height - 40);
|
||||
|
||||
gridScrollbar.style.setProperty('--grid-scrollbar-top', `${top}px`);
|
||||
gridScrollbar.style.setProperty('--grid-scrollbar-height', `${height}px`);
|
||||
}
|
||||
|
||||
function updateGridScrollbar() {
|
||||
if (!gridScrollbar || !gridScrollbarThumb) return;
|
||||
|
||||
const { clientHeight, scrollHeight, scrollTop } = gridContainer;
|
||||
const maxScrollTop = scrollHeight - clientHeight;
|
||||
if (maxScrollTop <= 0) {
|
||||
gridScrollbarThumb.style.height = '';
|
||||
gridScrollbarThumb.style.transform = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const trackHeight = gridScrollbar.clientHeight;
|
||||
const thumbHeight = Math.max(36, Math.round((clientHeight / scrollHeight) * trackHeight));
|
||||
const maxThumbTop = trackHeight - thumbHeight;
|
||||
const thumbTop = Math.round((scrollTop / maxScrollTop) * maxThumbTop);
|
||||
|
||||
gridScrollbarThumb.style.height = `${thumbHeight}px`;
|
||||
gridScrollbarThumb.style.transform = `translateY(${thumbTop}px)`;
|
||||
}
|
||||
|
||||
function scrollGridFromPointer(clientY) {
|
||||
const trackRect = gridScrollbar.getBoundingClientRect();
|
||||
const thumbHeight = gridScrollbarThumb.offsetHeight;
|
||||
const maxThumbTop = gridScrollbar.clientHeight - thumbHeight;
|
||||
const maxScrollTop = gridContainer.scrollHeight - gridContainer.clientHeight;
|
||||
if (maxThumbTop <= 0 || maxScrollTop <= 0) return;
|
||||
|
||||
const targetThumbTop = Math.min(
|
||||
maxThumbTop,
|
||||
Math.max(0, clientY - trackRect.top - _scrollbarDrag.thumbOffset),
|
||||
);
|
||||
gridContainer.scrollTop = (targetThumbTop / maxThumbTop) * maxScrollTop;
|
||||
}
|
||||
|
||||
const _gridResizeObserver = new ResizeObserver(updateGridScrollState);
|
||||
_gridResizeObserver.observe(gridContainer);
|
||||
_gridResizeObserver.observe(grid);
|
||||
|
||||
gridContainer.addEventListener('scroll', updateGridScrollbar);
|
||||
window.addEventListener('resize', updateGridScrollState);
|
||||
|
||||
gridScrollbar.addEventListener('pointerdown', (e) => {
|
||||
if (!gridContainer.classList.contains('is-scrollable')) return;
|
||||
|
||||
const thumbRect = gridScrollbarThumb.getBoundingClientRect();
|
||||
const isThumbPointer = e.target === gridScrollbarThumb;
|
||||
_scrollbarDrag = {
|
||||
thumbOffset: isThumbPointer ? e.clientY - thumbRect.top : thumbRect.height / 2,
|
||||
};
|
||||
|
||||
gridScrollbar.classList.add('dragging');
|
||||
gridScrollbar.setPointerCapture(e.pointerId);
|
||||
scrollGridFromPointer(e.clientY);
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
gridScrollbar.addEventListener('pointermove', (e) => {
|
||||
if (!_scrollbarDrag) return;
|
||||
scrollGridFromPointer(e.clientY);
|
||||
});
|
||||
|
||||
gridScrollbar.addEventListener('pointerup', (e) => {
|
||||
_scrollbarDrag = null;
|
||||
gridScrollbar.classList.remove('dragging');
|
||||
gridScrollbar.releasePointerCapture(e.pointerId);
|
||||
});
|
||||
|
||||
gridScrollbar.addEventListener('pointercancel', (e) => {
|
||||
_scrollbarDrag = null;
|
||||
gridScrollbar.classList.remove('dragging');
|
||||
gridScrollbar.releasePointerCapture(e.pointerId);
|
||||
});
|
||||
|
||||
export function renderGrid() {
|
||||
if (_cardObserver) { _cardObserver.disconnect(); _cardObserver = null; }
|
||||
@@ -44,6 +143,7 @@ export function renderGrid() {
|
||||
refreshCardSelection();
|
||||
updateNumberBadges();
|
||||
updateNewNameLabels();
|
||||
updateGridScrollState();
|
||||
}
|
||||
|
||||
export function createCard(file, idx) {
|
||||
@@ -138,4 +238,5 @@ export function showEmptyGrid() {
|
||||
emptyState.style.display = '';
|
||||
fileCount.textContent = '0개 파일';
|
||||
updateSelCount();
|
||||
updateGridScrollState();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user