diff --git a/A00_Common/spreadsheet/spreadsheet_menu.ts b/A00_Common/spreadsheet/spreadsheet_menu.ts index f2b7f2ee..cc4d585b 100644 --- a/A00_Common/spreadsheet/spreadsheet_menu.ts +++ b/A00_Common/spreadsheet/spreadsheet_menu.ts @@ -1,16 +1,20 @@ /* ============================================================================= * spreadsheet_menu.ts (주인 E) * 우클릭 메뉴 — 칸 · 행 머리 · 열 머리마다: 잘라내기 · 복사 · (붙여넣기 = 「Ctrl+V 를 쓸 것」 안내) · - * 행열 넣기 · 지우기 · 숨기기 · 보이기 · 내용 지우기 · 서식 지우기 · 병합 · 풀기. + * 행열 넣기 · 지우기 · 숨기기 · 보이기 · 내용 지우기 · 서식 지우기 · 병합 · 풀기 · 메모 삽입/편집/삭제. * `ui_template_context_menu.ts` 재사용 — 우클릭 밖 자리는 그냥 눌러 선택부터 옮김. + * 메모는 `spreadsheet_comments.ts`(sub1 · 브레인 답 2026-09-27)의 `setComment` · `deleteComment` · + * `getComment` 를 부름 — 안에서 「시트통째」 명령 · 되돌리기 짝까지 만듦. 글 입력 창은 이 파일이 + * 직접 띄움(작은 오버레이 · `showConfirmDialog` 와 같은 결의 자체 모달 · native prompt 안 씀). * * 이음(D `spreadsheet.ts` 잇는 자리) — `attachMenu(ctx)` 한 번 부름(반환 `root` = 메뉴 껍데기 · * 안 붙여도 됨 · `ctx.root` 안에 스스로 넣음). `ctx.grid.hitTest` 가 준비된 뒤(C 격자 붙은 뒤) 불러야 함. * ========================================================================== */ import { createMapContextMenu, type MapContextMenuItem } from "@ui/ui_template_context_menu"; -import { showToast } from "@ui/ui_template_elements"; +import { createButton, el, showToast } from "@ui/ui_template_elements"; import { rangeToA1 } from "./spreadsheet_address"; +import { deleteComment, getComment, setComment } from "./spreadsheet_comments"; import { st } from "./spreadsheet_text"; import { MAX_COLS, MAX_ROWS, type CellRange } from "./spreadsheet_types"; import type { PartHandle, SpreadsheetContext } from "./spreadsheet_view_types"; @@ -36,7 +40,39 @@ export function attachMenu(ctx: SpreadsheetContext): PartHandle { document.execCommand(kind); } - function cellMenuItems(range: CellRange): MapContextMenuItem[] { + /** 메모 글 입력 — 작은 오버레이(native prompt 안 씀, 자동화 창이 native 창을 자동 취소하는 + * 문제는 `showConfirmDialog` 머리 주석과 같은 까닭). 저장을 누르면 `onSave`. */ + function openCommentEditor(initial: string, onSave: (text: string) => void): void { + const overlay = el("div", { className: "ss-comment-editor" }); + const panel = el("div", { className: "ss-comment-editor__panel" }); + const textarea = el("textarea", { + className: "ss-comment-editor__input", + }) as HTMLTextAreaElement; + textarea.value = initial; + const done = (): void => overlay.remove(); + const actions = el("div", { + className: "ss-comment-editor__actions", + children: [ + createButton({ label: "취소", variant: "ghost", onClick: done }), + createButton({ + label: "저장", + variant: "filled", + onClick: () => { + onSave(textarea.value); + done(); + }, + }), + ], + }); + panel.append(textarea, actions); + overlay.append(panel); + document.body.append(overlay); + textarea.focus(); + textarea.select(); + } + + function cellMenuItems(range: CellRange, r: number, c: number): MapContextMenuItem[] { + const note = getComment(ctx.sheet(), r, c); return [ [st("MenuCut"), () => cutCopy("cut")], [st("MenuCopy"), () => cutCopy("copy")], @@ -58,6 +94,13 @@ export function attachMenu(ctx: SpreadsheetContext): PartHandle { : { 종류: "병합", 시트: ctx.selection.시트, 범위: range }, ), ], + [ + note ? st("MenuCommentEdit") : st("MenuCommentInsert"), + () => openCommentEditor(note ?? "", (text) => setComment(ctx, r, c, text)), + ], + ...(note + ? ([[st("MenuCommentDelete"), () => deleteComment(ctx, r, c)]] as MapContextMenuItem[]) + : []), ]; } @@ -196,7 +239,7 @@ export function attachMenu(ctx: SpreadsheetContext): PartHandle { }); } const active = ctx.selection.범위[0] ?? range(hit.r, hit.c, hit.r, hit.c); - menu.open(x, y, cellMenuItems(active)); + menu.open(x, y, cellMenuItems(active, hit.r, hit.c)); } } diff --git a/A00_Common/spreadsheet/spreadsheet_text.ts b/A00_Common/spreadsheet/spreadsheet_text.ts index 67546a24..9eb36a89 100644 --- a/A00_Common/spreadsheet/spreadsheet_text.ts +++ b/A00_Common/spreadsheet/spreadsheet_text.ts @@ -78,6 +78,9 @@ const TEXT = { MenuClearFormat: ["서식 지우기", "Clear format"], MenuMerge: ["병합", "Merge"], MenuUnmerge: ["병합 해제", "Unmerge"], + MenuCommentInsert: ["메모 삽입", "Insert comment"], + MenuCommentEdit: ["메모 편집", "Edit comment"], + MenuCommentDelete: ["메모 삭제", "Delete comment"], ClipboardPasteFailed: ["붙여넣기를 읽지 못함", "Could not read paste content"], } as const; diff --git a/A00_Common/spreadsheet/spreadsheet_toolbar.css b/A00_Common/spreadsheet/spreadsheet_toolbar.css index 864f26b1..d43a375f 100644 --- a/A00_Common/spreadsheet/spreadsheet_toolbar.css +++ b/A00_Common/spreadsheet/spreadsheet_toolbar.css @@ -215,3 +215,43 @@ margin: 2px 4px; border-top: 1px solid var(--color-border); } + +/* ── 메모 편집 창(칸 우클릭 [메모 삽입 · 편집]) ──────────────────────────────── */ + +.ss-comment-editor { + position: fixed; + inset: 0; + z-index: var(--z-modal); + display: flex; + align-items: center; + justify-content: center; + background: rgb(0 0 0 / 35%); +} + +.ss-comment-editor__panel { + display: flex; + flex-direction: column; + gap: var(--spacing-8); + width: 280px; + padding: var(--spacing-16); + border-radius: var(--radius-cards); + background: var(--color-surface-raised); + box-shadow: var(--shadow-lg); +} + +.ss-comment-editor__input { + min-height: 80px; + padding: var(--spacing-8); + border: 1px solid var(--color-border); + border-radius: var(--radius-sm); + background: var(--color-canvas); + color: var(--color-text-body); + font: inherit; + resize: vertical; +} + +.ss-comment-editor__actions { + display: flex; + justify-content: flex-end; + gap: var(--spacing-8); +}