feat(B01): 임시 보관함 그룹 접기/펼치기 + 제목 행 한 줄 정리
- 그룹은 기본 접힘, 제목 행 전체가 토글 트리거(액션 버튼 클릭은 전파 차단) - 목록을 다시 그려도 펼친 그룹은 유지, 업로드 시작 시 해당 그룹 자동 펼침 - 파일 수·용량·보관 만료를 상태 배지 오른쪽으로 옮겨 제목과 한 줄로 배치 - 파일 표 상하 여백 축소(셀·버튼 padding 2px, 글자 크기 유지) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -34,24 +34,26 @@
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* 제목 행 전체가 접기/펼치기 트리거다. */
|
||||
.b01-temp__group-head {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--spacing-8);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.b01-temp__group-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.b01-temp__group-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-8);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.b01-temp__caret {
|
||||
width: 12px;
|
||||
color: var(--color-muted-text, var(--color-primary-text));
|
||||
}
|
||||
|
||||
.b01-temp__group-meta {
|
||||
@@ -59,6 +61,29 @@
|
||||
color: var(--color-muted-text, var(--color-primary-text));
|
||||
}
|
||||
|
||||
/* 기본은 접힘 — 펼친 그룹만 파일 표를 보여 준다. */
|
||||
.b01-temp__group-body {
|
||||
display: none;
|
||||
margin-top: var(--spacing-8);
|
||||
}
|
||||
|
||||
.b01-temp__group.is-open .b01-temp__group-body {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 파일 표는 행이 많아 촘촘하게 — 글자 크기는 그대로 두고 상하 여백만 줄인다. */
|
||||
.b01-temp__group-body .ui-general-block__table th,
|
||||
.b01-temp__group-body .ui-general-block__table td {
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.b01-temp__group-body .ui-general-block__table .ui-btn {
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.b01-temp__badge {
|
||||
padding: 2px var(--spacing-8);
|
||||
border: 1px solid var(--color-border);
|
||||
|
||||
@@ -84,6 +84,8 @@ export function buildTempUploadSection(): HTMLElement {
|
||||
const tasks = new Map<string, UploadTask[]>();
|
||||
/** 진행률 막대 DOM — 청크 하나 올릴 때마다 목록 전체를 다시 그리지 않기 위해 잡아 둔다. */
|
||||
const progressNodes = new Map<string, { fill: HTMLElement; caption: HTMLElement }>();
|
||||
/** 펼쳐 둔 그룹 — 기본은 접힘, 사용자가 연 그룹만 목록을 다시 그려도 열린 상태를 유지한다. */
|
||||
const openGroups = new Set<string>();
|
||||
|
||||
/* ── 행 구성 ─────────────────────────────────────────────────────────── */
|
||||
|
||||
@@ -174,13 +176,18 @@ export function buildTempUploadSection(): HTMLElement {
|
||||
const group = document.createElement("div");
|
||||
group.className = `b01-temp__group is-${batch.status}`;
|
||||
|
||||
const opened = openGroups.has(batch.batch_id);
|
||||
group.classList.toggle("is-open", opened);
|
||||
|
||||
const head = document.createElement("div");
|
||||
head.className = "b01-temp__group-head";
|
||||
|
||||
const info = document.createElement("div");
|
||||
info.className = "b01-temp__group-info";
|
||||
// 제목 행 = 접기/펼치기 트리거. 이름·상태·요약이 한 줄에 들어간다.
|
||||
const titleRow = document.createElement("div");
|
||||
titleRow.className = "b01-temp__group-title";
|
||||
const caret = document.createElement("span");
|
||||
caret.className = "b01-temp__caret";
|
||||
caret.textContent = opened ? "▾" : "▸";
|
||||
const title = document.createElement("strong");
|
||||
title.textContent = batch.name;
|
||||
const badge = document.createElement("span");
|
||||
@@ -191,9 +198,8 @@ export function buildTempUploadSection(): HTMLElement {
|
||||
: batch.required_complete
|
||||
? L("B01_Temp_Status_Ready")
|
||||
: L("B01_Temp_Status_Uploading");
|
||||
titleRow.append(title, badge);
|
||||
|
||||
const meta = document.createElement("div");
|
||||
const meta = document.createElement("span");
|
||||
meta.className = "b01-temp__group-meta";
|
||||
const parts = [
|
||||
`${batch.files.length}${L("B01_Temp_Meta_FileCount")}`,
|
||||
@@ -205,10 +211,12 @@ export function buildTempUploadSection(): HTMLElement {
|
||||
parts.push(`${L("B01_Temp_Meta_Expires")} ${formatDate(batch.expires_at)}`);
|
||||
}
|
||||
meta.textContent = parts.join(" · ");
|
||||
info.append(titleRow, meta);
|
||||
titleRow.append(caret, title, badge, meta);
|
||||
|
||||
const actions = document.createElement("div");
|
||||
actions.className = "b01-dashboard__actions";
|
||||
// 버튼 클릭이 접기/펼치기까지 건드리지 않게 막는다.
|
||||
actions.addEventListener("click", (event) => event.stopPropagation());
|
||||
if (batch.status !== "linked") {
|
||||
actions.append(
|
||||
createButton({
|
||||
@@ -223,10 +231,21 @@ export function buildTempUploadSection(): HTMLElement {
|
||||
}),
|
||||
);
|
||||
}
|
||||
head.append(info, actions);
|
||||
head.append(titleRow, actions);
|
||||
head.setAttribute("aria-expanded", String(opened));
|
||||
head.addEventListener("click", () => {
|
||||
const nowOpen = !group.classList.contains("is-open");
|
||||
group.classList.toggle("is-open", nowOpen);
|
||||
head.setAttribute("aria-expanded", String(nowOpen));
|
||||
caret.textContent = nowOpen ? "▾" : "▸";
|
||||
// 목록을 다시 그려도 열어 둔 그룹은 계속 열려 있어야 한다(업로드 중 진행률 확인).
|
||||
if (nowOpen) openGroups.add(batch.batch_id);
|
||||
else openGroups.delete(batch.batch_id);
|
||||
});
|
||||
|
||||
group.append(
|
||||
head,
|
||||
const body = document.createElement("div");
|
||||
body.className = "b01-temp__group-body";
|
||||
body.append(
|
||||
table(
|
||||
[
|
||||
L("B01_Temp_Table_Type"),
|
||||
@@ -238,6 +257,8 @@ export function buildTempUploadSection(): HTMLElement {
|
||||
fileRows(batch),
|
||||
),
|
||||
);
|
||||
|
||||
group.append(head, body);
|
||||
return group;
|
||||
}
|
||||
|
||||
@@ -361,6 +382,8 @@ export function buildTempUploadSection(): HTMLElement {
|
||||
// 지난번 실패 행은 새로 올리기 시작하면 치운다.
|
||||
const previous = (tasks.get(batchId) ?? []).filter((item) => item.status !== "failed");
|
||||
tasks.set(batchId, [...previous, ...queued]);
|
||||
// 올리는 동안은 진행률이 보여야 하므로 해당 그룹을 펼친 상태로 둔다.
|
||||
openGroups.add(batchId);
|
||||
await refresh();
|
||||
|
||||
let failed = 0;
|
||||
|
||||
Reference in New Issue
Block a user