Files
Aislo/A00_Common/spreadsheet/spreadsheet_find_panel.ts
T
eomsangdonandClaude Opus 5.5 ce3df39206 feat(spreadsheet): D 잇기 마무리 — 진짜 부품으로 createSpreadsheet · 2단계 찾기 · 메모 · 서식 붓 · 골라 붙여넣기 · 폭 맞춤 · 함수 도움말
- spreadsheet.ts — 가짜 부품 자리(parts) 걷고 A · B · C · E 직접 잇기 · 2단계는 spreadsheet_extras.ts 로
- spreadsheet_extras.ts(새) — 메모 풍선(마우스 올림) · Shift+F2 메모 편집 · 서식 붓 단추 · Ctrl+Shift+V 값만 · 서식만 · 수식만 · 도구 모음 뒤 초점 되돌림
- spreadsheet_find_panel.ts(새) — Ctrl+F · Shift+F5 찾기 · Ctrl+H 바꾸기 패널
- spreadsheet_editor.ts — C editorSlot(격자 뿌리 기준) → 층 안 좌표 맞춤(slotOf) · 함수 자동 완성(Tab) · 인자 도움말
- spreadsheet_keys.ts · spreadsheet_mouse.ts — 2단계 잇기 자리 · 열 경계 두 번 누르면 폭 자동 맞춤
- 시험 — 함수 도움말 자리 판정 추가(8 묶음)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L6KXAabDTenEU7hrDQmCKY
2026-09-27 20:57:25 +09:00

132 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* =============================================================================
* spreadsheet_find_panel.ts (주인 D)
* 찾기(Ctrl+F) · 바꾸기(Ctrl+H) 작은 패널 — 부품 오른위에 뜸. 찾기 규칙은 `spreadsheet_find.ts`(sub1).
* Enter = 다음 · Esc = 닫고 격자로. 바꾸기 = 활성 칸이 맞으면 바꾸고 다음으로(엑셀).
* ========================================================================== */
import { el } from "@ui/ui_template_elements";
import { findAll, findNext, replaceAll, replaceOne, type FindOptions } from "./spreadsheet_find";
import { selectCell } from "./spreadsheet_selection";
import type { PartHandle, SpreadsheetContext } from "./spreadsheet_view_types";
const TEXT = {
find: "찾을 내용",
replace: "바꿀 내용",
next: "다음",
one: "바꾸기",
all: "모두 바꾸기",
close: "닫기",
case: "대소문자",
whole: "전체 칸",
formula: "수식 안",
none: "찾을 수 없음",
replaced: "{n} 개 바꿈",
};
export interface FindPanelHandle extends PartHandle {
open(replace: boolean): void;
close(): void;
}
export function mountFindPanel(ctx: SpreadsheetContext, back: () => void): FindPanelHandle {
const query = el("input", { className: "ss-find__input", attrs: { placeholder: TEXT.find } });
const repl = el("input", { className: "ss-find__input", attrs: { placeholder: TEXT.replace } });
const status = el("span", { className: "ss-find__status" });
const button = (text: string, onClick: () => void): HTMLButtonElement => {
const b = el("button", { className: "ss-find__btn", text, attrs: { type: "button" } });
b.addEventListener("click", onClick);
return b;
};
const check = (text: string): HTMLInputElement => {
const box = el("input", { attrs: { type: "checkbox" } });
options.append(el("label", { className: "ss-find__opt", children: [box, text] }));
return box;
};
const closeBtn = button("×", close);
closeBtn.title = TEXT.close;
const options = el("div", { className: "ss-find__row" });
const caseBox = check(TEXT.case);
const wholeBox = check(TEXT.whole);
const formulaBox = check(TEXT.formula);
const replaceRow = el("div", {
className: "ss-find__row",
children: [repl, button(TEXT.one, one), button(TEXT.all, all)],
});
const root = el("div", {
className: "ss-find",
attrs: { hidden: "" },
children: [
el("div", {
className: "ss-find__row",
children: [query, button(TEXT.next, next), closeBtn],
}),
replaceRow,
options,
status,
],
});
ctx.root.append(root);
const opts = (): FindOptions => ({
대소문자: caseBox.checked,
전체칸: wholeBox.checked,
수식안: formulaBox.checked,
});
const here = () => ({ 시트: ctx.selection.시트, ...ctx.selection.활성 });
function next(): void {
status.textContent = "";
if (!query.value) return;
const hit = findNext(ctx.book, here(), query.value, opts());
if (!hit) return void (status.textContent = TEXT.none);
if (hit.시트 !== ctx.selection.시트) ctx.showSheet(hit.시트);
ctx.select(selectCell(ctx.sheet(), hit.r, hit.c), true);
}
function one(): void {
if (ctx.readOnly || !query.value) return;
const at = here();
const onIt = findAll(ctx.book, query.value, opts(), [at.시트]).some(
(h) => h.r === at.r && h.c === at.c,
);
if (onIt) replaceOne(ctx, at, query.value, repl.value, opts());
next();
}
function all(): void {
if (ctx.readOnly || !query.value) return;
const n = replaceAll(ctx, query.value, repl.value, opts());
status.textContent = n ? TEXT.replaced.replace("{n}", String(n)) : TEXT.none;
}
function open(replace: boolean): void {
replaceRow.hidden = !replace || ctx.readOnly;
root.removeAttribute("hidden");
status.textContent = "";
query.focus();
query.select();
}
function close(): void {
root.setAttribute("hidden", "");
back();
}
const onKey = (e: KeyboardEvent): void => {
if (e.key === "Escape") close();
else if (e.key === "Enter") e.target === repl ? one() : next();
else return;
e.preventDefault();
e.stopPropagation();
};
root.addEventListener("keydown", onKey);
return {
root,
open,
close,
refresh() {},
destroy: () => root.remove(),
};
}