fix(spreadsheet): 단위 Φ 앞붙이기 — 강제 글 낫표 뒤에 붙임 · 식은 그대로(81-5)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MgChuAcyBqUkhHMTXomQC7
This commit is contained in:
@@ -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));
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* 결과 = JSON {pass, fail, failures} 한 줄(`test_spreadsheet_toolbar_unit.py` 가 읽음) */
|
||||
import {
|
||||
attachUnitToFormat,
|
||||
prefixUnit,
|
||||
UNIT_GROUPS,
|
||||
UNIT_PREFIX,
|
||||
} from "../../../A00_Common/spreadsheet/spreadsheet_toolbar";
|
||||
@@ -36,4 +37,12 @@ eq("Φ 서식 — 빈 코드도 0 기준", attachUnitToFormat("", PHI), '"Φ"0')
|
||||
eq("Φ 서식 — 옛 Φ 바꿈(겹쳐 안 붙음)", attachUnitToFormat('"Φ"0', PHI), '"Φ"0');
|
||||
eq("Φ 서식 — 뒤 단위와 공존", attachUnitToFormat('0"㎡"', PHI), '"Φ"0"㎡"');
|
||||
|
||||
// 앞 단위 글 — 낫표(강제 글)는 맨 앞에 두고 그 뒤에 붙임 · 이미 붙었으면 그대로(81-5 흠)
|
||||
eq("Φ 글 — 숫자 앞", prefixUnit("300", PHI), "Φ300");
|
||||
eq("Φ 글 — 낫표 뒤", prefixUnit("'300", PHI), "'Φ300");
|
||||
eq("Φ 글 — 두 번 안 붙음", prefixUnit("Φ300", PHI), "Φ300");
|
||||
eq("Φ 글 — 낫표 뒤 두 번 안 붙음", prefixUnit("'Φ300", PHI), "'Φ300");
|
||||
eq("Φ 글 — 빈 칸", prefixUnit("", PHI), "Φ");
|
||||
eq("Φ 글 — 식은 그대로", prefixUnit("=A1", PHI), "=A1");
|
||||
|
||||
console.log(JSON.stringify({ pass, fail: failures.length, failures }));
|
||||
|
||||
@@ -54,4 +54,4 @@ def test_단위_파이_순수_길():
|
||||
assert run.stdout.strip(), run.stderr
|
||||
result = json.loads(run.stdout)
|
||||
assert result["fail"] == 0, result["failures"]
|
||||
assert result["pass"] == 9
|
||||
assert result["pass"] == 15
|
||||
|
||||
@@ -66,7 +66,23 @@ def test_Φ_길이_묶음_끝_칸_글_편집기():
|
||||
assert '["UnitGroup_area", ["㎡", "㎥", "m", "㎝", "㎜", "㎞", "Φ"]]' in src
|
||||
body = src.split("function insertUnit")[1].split("function renderRecent")[0]
|
||||
assert "UNIT_PREFIX.includes(unit)" in body
|
||||
assert (
|
||||
"now.startsWith(unit)" in body and "unit + now" in body
|
||||
) # 「300」 → 「Φ300」 · 두 번 안 붙음
|
||||
assert "const s = front ? 0" in body # 편집 중에도 글 맨 앞
|
||||
assert "prefixUnit(now, unit)" in body # 칸 글 — 「300」 → 「Φ300」 · 두 번 안 붙음
|
||||
assert "prefixUnit(active.value, unit)" in body # 편집 중에도 같은 규칙
|
||||
|
||||
|
||||
@pytest.mark.skipif(shutil.which("node") is None, reason="node 가 없음")
|
||||
def test_앞_단위_글_낫표_뒤에_붙음():
|
||||
"""강제 글 낫표(`'300`)는 맨 앞에 두고 그 뒤에 붙임 — 「Φ'300」 처럼 낫표가 가운데 남지 않음."""
|
||||
func = re.search(r"^export function prefixUnit[\s\S]*?^}", _text(), re.M)
|
||||
assert func
|
||||
script = (
|
||||
func.group(0).replace("export ", "").replace(": string", "")
|
||||
+ """
|
||||
const f = prefixUnit;
|
||||
console.log(JSON.stringify([f("300", "Φ"), f("'300", "Φ"), f("Φ300", "Φ"), f("'Φ300", "Φ"), f("=A1", "Φ"), f("", "Φ")]));
|
||||
"""
|
||||
)
|
||||
out = subprocess.run(
|
||||
["node", "-e", script], capture_output=True, text=True, encoding="utf-8", check=True
|
||||
)
|
||||
assert json.loads(out.stdout) == ["Φ300", "'Φ300", "Φ300", "'Φ300", "=A1", "Φ"]
|
||||
|
||||
Reference in New Issue
Block a user