Merge remote-tracking branches 'origin/sub_laptop_2', 'origin/sub_laptop_7' and 'origin/sub_laptop_8' into sub_laptop_5

This commit is contained in:
2026-10-08 13:38:09 +09:00
8 changed files with 82 additions and 25 deletions
@@ -23,6 +23,7 @@ import {
colMoreItems,
copyCut,
copyOnlyItems,
keepGridFocus,
pasteItems,
rowInsertItems,
rowMoreItems,
@@ -44,6 +45,7 @@ export function attachMenu(ctx: SpreadsheetContext): PartHandle {
const menu = createMapContextMenu("ss");
ctx.root.append(menu.element);
keepGridFocus(ctx, menu.element); // 메뉴 동작 뒤 Ctrl+Z 등 키가 격자로
ctx.cellMenuExtra ??= () => []; // 칸 메뉴 있음 — 조건 만들기가 이 끝에 붙음
function isMerged(range: CellRange): boolean {
@@ -83,6 +83,18 @@ function back(ctx: SpreadsheetContext): void {
(ctx.editor as Editor).focus?.();
}
/** 항목을 누른 뒤 초점이 문서(body)에 떨어졌으면 격자로 — 창을 띄운 동작은 그 창이 초점을 가짐 */
export function keepGridFocus(ctx: SpreadsheetContext, menuRoot: HTMLElement): void {
menuRoot.addEventListener(
"click",
() =>
setTimeout(() => {
if (document.activeElement === document.body) back(ctx);
}),
true,
);
}
/** 잘라내기 · 복사 — 초점을 격자로 되돌린 뒤 불러야 부품의 copy · cut 듣기가 받음(빈 복사 막음) */
export function copyCut(ctx: SpreadsheetContext, kind: "cut" | "copy"): void {
back(ctx);
+21 -11
View File
@@ -103,6 +103,15 @@ export function attachUnitToFormat(code: string, unit: string): string {
return `"${unit}"` + plain;
}
/** 앞 단위를 글 앞에 붙임(「300」 → 「Φ300」) — 글 낫표(강제 글 `'300`)는 맨 앞에 두고 그 뒤에
* 붙임(`'Φ300` → 칸 값 「Φ300」) · 이미 붙어 있거나 식(`=`)이면 그대로 */
export function prefixUnit(text: string, unit: string): string {
if (text.startsWith("=")) return text;
const lead = text.startsWith("'") ? "'" : "";
const body = text.slice(lead.length);
return body.startsWith(unit) ? text : lead + unit + body;
}
/** 활성 칸에 지금 걸린 서식 — 칸 자기 것 없으면 열 · 행 · 시트 기본(`Sheet.기본.서식`) · 그다음 0. */
function styleAt(ctx: SpreadsheetContext, r: number, c: number): CellStyle {
const sheet = ctx.sheet();
@@ -405,13 +414,18 @@ export function mountToolbar(ctx: SpreadsheetContext): PartHandle {
if (ctx.editor.editing()) {
const active = document.activeElement;
if (active instanceof HTMLTextAreaElement && ctx.root.contains(active)) {
const front = UNIT_PREFIX.includes(unit);
const s = front ? 0 : (active.selectionStart ?? active.value.length);
const e = front ? 0 : (active.selectionEnd ?? active.value.length);
const was = active.selectionStart ?? active.value.length;
active.value = active.value.slice(0, s) + unit + active.value.slice(e);
const at = front ? was + unit.length : s + unit.length;
active.setSelectionRange(at, at);
if (UNIT_PREFIX.includes(unit)) {
const next = prefixUnit(active.value, unit);
const at = was + next.length - active.value.length;
active.value = next;
active.setSelectionRange(at, at);
} else {
const s = was;
const e = active.selectionEnd ?? active.value.length;
active.value = active.value.slice(0, s) + unit + active.value.slice(e);
active.setSelectionRange(s + unit.length, s + unit.length);
}
active.dispatchEvent(new Event("input", { bubbles: true }));
}
} else if (unitAsFormat.checked) {
@@ -422,11 +436,7 @@ export function mountToolbar(ctx: SpreadsheetContext): PartHandle {
const sheet = ctx.sheet();
const key = toA1(r, c);
const now = cellToText(sheet.칸[key]);
const text = UNIT_PREFIX.includes(unit)
? now.startsWith(unit)
? now
: unit + now // 앞 단위 — 「300」 → 「Φ300」
: now + unit;
const text = UNIT_PREFIX.includes(unit) ? prefixUnit(now, unit) : now + unit;
ctx.dispatch({ 종류: "칸", 시트: sheet.id, 칸: { [key]: textToCell(text, sheet.칸[key]) } });
}
renderRecent(pushRecentUnit(unit));