diff --git a/M02_MasterTemplete/M02_MasterTemplete_Labor_Table.ts b/M02_MasterTemplete/M02_MasterTemplete_Labor_Table.ts index a28183dc6..96516971d 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Labor_Table.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_Labor_Table.ts @@ -5,7 +5,7 @@ * · 옵션 값 = 규격 칸 · 「빈 값 n」 = 비고 칸 붉게(입력 빈 로직 줄) * · 수량 칸 = 확인용 임시 입력(723-1 · 휘발 · 문서 · 저장 · dirty 안 건드림) → 그 줄 금액 · 합계 미리 보기 * (짝 셈 `Labor_Calc.settle` · 서버 안 부름) · 문서 안 줄 수량은 늘 null(쓰는 쪽 알약으로 이음) - * · 머리 단추 = [로직 추가] [자재 추가] · 줄 우클릭 = 위로 · 아래로 · 별칭 · 삭제 + * · 머리 단추 = [로직 추가] [자재 추가] · 줄 우클릭 · 줄 끝 ⋯ = 위로 · 아래로 · 별칭 · 삭제 * · 줄 누름 = 줄 모달(`Labor_RowModal` — 옵션 · 메모 · 켜쌓기율 · 로직 자재 바꾸기 · 세 칸 · 단가산출) · * 사전조합 참조 줄 = 서고 원본 모달(`Labor_RefEdit`) · 사용자 자재 줄 = 자재 모달(세 칸) * · 금액 = 서버 calc(최신 응답만 · 저장 안 함) · [저장]은 UI_Main(서버가 수량 걷고 다시 셈) @@ -155,6 +155,13 @@ export function mountLaborTable( const n = at(i); if (n !== null) openAt(n); }, + // 줄 끝 ⋯(731-1) — 우클릭 · 길게 누름이 없어도 같은 메뉴(위로 · 아래로 · 별칭 · 삭제) + rowMenu: edit + ? { + show: (i) => at(i) !== null, + open: (i, ev) => openTreeMenu(ev, menuAt(at(i) ?? 0)), + } + : undefined, editable: (i, key) => { const n = at(i); if (key !== "qty" || n === null) return false; diff --git a/resources/tester/helper_ui_data_table.cjs b/resources/tester/helper_ui_data_table.cjs index 65a334c15..39df9c92f 100644 --- a/resources/tester/helper_ui_data_table.cjs +++ b/resources/tester/helper_ui_data_table.cjs @@ -96,6 +96,24 @@ console.log( total: { x: 3 }, rowClass: (i) => (i === 0 ? "is-title" : null), }), + rowMenu: m.renderDataTableHtml({ + columns: [ + { key: "a", label: "A" }, + { key: "b", label: "B" }, + ], + rows: [ + { a: "제목", b: 1 }, + { a: "줄", b: 2 }, + { a: "줄2", b: 3 }, + ], + onRowClick: () => undefined, + rowMenu: { show: (i) => i > 0, open: () => undefined }, + }), + noRowMenu: m.renderDataTableHtml({ + columns: [{ key: "a", label: "A" }], + rows: [{ a: "줄" }], + onRowClick: () => undefined, + }), scale: m.SCALE, widths: m.renderDataTableHtml({ columns: [ diff --git a/resources/tester/test_ui_data_table.py b/resources/tester/test_ui_data_table.py index 6b61be801..7467f92ce 100644 --- a/resources/tester/test_ui_data_table.py +++ b/resources/tester/test_ui_data_table.py @@ -122,3 +122,15 @@ def test_155_2_줄_꼴_곁값(r): assert rc.count("is-title") == 1 # 합계 줄엔 안 붙음 assert "white-space-collapse: preserve-spaces;" in r["css"] assert "tr.is-title td" in r["css"] + + +def test_줄_끝_메뉴_단추_731(r): + """rowMenu — show 참인 줄의 첫 칸에만 ⋯ 단추 · 없으면 단추 없음 · 줄 번호가 data-more 로.""" + rows = r["rowMenu"].split("
")[1].split("")[0].split("")[:3] + assert "data-more" not in rows[0] + for i in (1, 2): + assert rows[i].count("data-more") == 1 + assert f'data-more="{i}"' in rows[i] + assert rows[i].split("")[0].count("data-more") == 1 # 첫 칸 안 + assert 'class="ui-icon-btn ui-icon-btn--ghost ui-data-table__more"' in rows[1] + assert "data-more" not in r["noRowMenu"] diff --git a/ui_template/ui_template_data_table.css b/ui_template/ui_template_data_table.css index 4d404a9ba..b36eb7fce 100644 --- a/ui_template/ui_template_data_table.css +++ b/ui_template/ui_template_data_table.css @@ -330,3 +330,17 @@ :is(.ui-data-table__icon, .ui-data-table__lamp)[hidden] { display: none !important; } + +/* 줄 끝 ⋯(731-1) — 첫 칸 오른쪽 가운데에 얹음(줄 높이를 안 밀음) · 글은 단추 자리만큼 비워 줄임표 */ +.ui-data-table__table td:has(> .ui-data-table__more) { + padding-right: calc(var(--ui-size-h, 28px) + var(--spacing-4)); +} +.ui-data-table__table td:has(> .ui-data-table__more):not(.is-frozen) { + position: relative; /* 고정 칸은 이미 sticky(자리 기준) */ +} +.ui-data-table__table td > .ui-data-table__more { + position: absolute; + top: 50%; + right: var(--spacing-4); + transform: translateY(-50%); +} diff --git a/ui_template/ui_template_data_table.ts b/ui_template/ui_template_data_table.ts index eb64b3e3a..48189b004 100644 --- a/ui_template/ui_template_data_table.ts +++ b/ui_template/ui_template_data_table.ts @@ -21,6 +21,8 @@ * 칸 누른 뒤 Enter · F2 = 그 칸 입력 시작(PLAN 243-1 · keyEdit false = 표 밖이 맡음) * 같은 값 세로 병합(PLAN 201-1) = mergeSame [열] → 위아래로 같은 값이 이어지면 한 칸(rowSpan) · 본문만 * 앞 병합 열이 갈리면 뒤 병합 열도 갈림 · 빈 값 · 고칠 칸은 안 합침 · 칸 → 열 = `cellColumn`(병합 표는 data-c) + * 줄 끝 메뉴(731-1) = rowMenu { show(줄), open(줄, 누름) } → show 참인 줄의 첫 칸 오른쪽에 ⋯ 단추(우클릭 · 길게 누름이 없는 손가락 · 마우스도 보이는 길) · + * 누르면 줄 누름 없이 open 만 부름(메뉴는 부르는 쪽이 엶) · 단추 꼴 = 아이콘 단추(28 · ghost) 그대로(DOM 없는 시험도 같은 글) * 칸 램프(557-2) = cellClass 가 `ui-data-table__lamp` → 칸 글 앞 점 8 · 노랑 = 없음 · `is-linked` = 초록 ✓(색만으로 안 가름) · * `ui-data-table__lamp--only` = 글 숨기고 점만 가운데 · 대신하는 옛 변종 = B08 `b08-summary__lamp` · `b08-milestone__lamp` · B09 `b09-bill__lamp` * ========================================================================== */ @@ -65,6 +67,8 @@ export interface DataTableOptions { onRowClick?: (index: number) => void; /** 칸 누름(본문 줄만) — 주면 줄에 `data-i`·`is-clickable` 이 붙음 · 줄 누름보다 먼저 부름 */ onCellClick?: (index: number, key: string) => void; + /** 줄 끝 ⋯ 메뉴(731-1) — show 참인 줄의 첫 칸에 ⋯ 단추 · 누르면 줄 누름 없이 open(줄 · 누름 이벤트) */ + rowMenu?: { show: (index: number) => boolean; open: (index: number, event: MouseEvent) => void }; /** 강조할 줄(`focusRow` 가 정함) */ focus?: number | null; /** 고칠 칸인지(본문 줄만 · 합계 제외) — 참이면 칸이 글 상자 */ @@ -113,6 +117,9 @@ export const TREE_ICON = { }; const EMPTY = "-"; +/** ⋯ 단추(731-1) — 아이콘 단추(`createIconButton` icon "dots" · 28 · ghost)와 같은 글 · 표 글은 DOM 없이 만들어서 글로 둠 */ +const MORE_BUTTON = (i: number): string => + ``; const ESC: Record