feat: 모든 자산 목록 뷰에 마우스 드래그를 이용한 테이블 컬럼 너비 조절(Resizable Columns) 기능 추가

This commit is contained in:
이태훈
2026-06-25 17:16:44 +09:00
parent ed3d8812c2
commit 8747b3946f
2 changed files with 57 additions and 1 deletions

View File

@@ -670,7 +670,7 @@ export function createListView(container: HTMLElement, config: ListViewConfig) {
thead.innerHTML = `<tr>${config.columns.map(col => {
const isDateCol = col.header.includes('일') || col.header.includes('날짜') || col.header.includes('연월');
const alignmentClass = col.align ? `text-${col.align}` : (isDateCol ? 'text-center' : '');
return `<th class="${alignmentClass}" ${col.sortKey ? `data-sort="${col.sortKey}"` : ''} style="${col.width ? `width:${col.width};` : ''}">${col.header}</th>`;
return `<th class="${alignmentClass}" ${col.sortKey ? `data-sort="${col.sortKey}"` : ''} style="${col.width ? `width:${col.width};` : ''}">${col.header}<div class="resizer"></div></th>`;
}).join('')}</tr>`;
tbody.innerHTML = filtered.length === 0 ? `<tr><td colspan="${config.columns.length}" class="text-center empty-cell">${UI_TEXT.MESSAGES.NO_DATA}</td></tr>`
@@ -698,8 +698,45 @@ export function createListView(container: HTMLElement, config: ListViewConfig) {
tbody.querySelectorAll('.asset-row').forEach((tr, idx) => { tr.addEventListener('click', () => config.onRowClick && config.onRowClick(filtered[idx])); });
setupTableSorting(table, sortState, (key, dir) => { sortState = { key, direction: dir }; updateTable(); });
makeColumnsResizable(table);
};
function makeColumnsResizable(tableElement: HTMLTableElement) {
const headers = tableElement.querySelectorAll('th');
headers.forEach(th => {
const resizer = th.querySelector('.resizer') as HTMLElement;
if (!resizer) return;
let startX = 0;
let startWidth = 0;
const onMouseMove = (e: MouseEvent) => {
const dx = e.clientX - startX;
const newWidth = Math.max(50, startWidth + dx);
th.style.width = `${newWidth}px`;
};
const onMouseUp = () => {
resizer.classList.remove('resizing');
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
};
resizer.addEventListener('mousedown', (e: MouseEvent) => {
// Prevents header click sorting trigger from firing
e.stopPropagation();
e.preventDefault();
startX = e.clientX;
startWidth = th.offsetWidth;
resizer.classList.add('resizing');
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
});
});
}
const switchView = () => {
contentWrapper.innerHTML = '';
const isAssetMode = !isServer || state.viewMode === 'list';

View File

@@ -68,6 +68,25 @@ th {
letter-spacing: -0.02em;
box-shadow: inset 0 -1px 0 var(--hairline);
text-align: center; /* Set default header alignment to center */
position: relative; /* Essential for absolute positioning of resizer handles */
}
.resizer {
position: absolute;
right: 0;
top: 0;
width: 6px;
height: 100%;
cursor: col-resize;
user-select: none;
z-index: 60; /* Higher than thead's sticky z-index (50) to catch mouse events */
}
.resizer:hover,
.resizer.resizing {
background-color: var(--primary, #1e5149);
opacity: 0.8;
width: 3px;
}
td {