feat(spreadsheet): 칸 우클릭 메뉴에 메모 삽입 · 편집 · 삭제
- sub1 `spreadsheet_comments.ts` 의 setComment · deleteComment · getComment 호출 - 글 입력은 E 자체 작은 오버레이(native prompt 안 씀 — 자동화 창 자동 취소 문제) - ORCA 실 엔진 시험 — 삽입 → 메뉴 글자가 「메모 편집·삭제」로 바뀜 → 삭제 → 취소(저장 안 됨) 확인
This commit is contained in:
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user