Merge remote-tracking branch 'origin/sub_laptop_6' into sub_laptop_7

This commit is contained in:
2026-09-27 21:52:12 +09:00
3 changed files with 52 additions and 10 deletions
@@ -183,6 +183,19 @@
color: #c00;
}
/* 오류 칸 — 엑셀 오류 검사 초록 세모(왼 위 · sub3 오류 까닭은 title 풍선). */
.aislo-cell.is-error::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 0;
height: 0;
border-style: solid;
border-width: 6px 6px 0 0;
border-color: #2e7d32 transparent transparent transparent;
}
.aislo-cell.is-selected {
outline: 2px solid var(--aislo-grid-selected);
outline-offset: -1px;
+28 -9
View File
@@ -34,6 +34,7 @@ import {
type LineGeometry,
type OutlineRun,
} from "./spreadsheet_headers";
import { resolveCondFmtStyle, type CondFmtValue } from "./spreadsheet_condfmt";
import { formatValue } from "./spreadsheet_numfmt";
import { MAX_COLS, MAX_ROWS } from "./spreadsheet_types";
import type {
@@ -150,6 +151,23 @@ interface CellDisplay {
color?: string;
}
/** 칸 하나 값 — 식이면 엔진 값 · 아니면 입력 값(수는 Frac로) · 둘 다 없으면 빈 칸. */
function scalarAt(ctx: SpreadsheetContext, sheet: Sheet, r: number, c: number): Scalar {
const cell = sheet.칸[toA1(r, c)];
return cell?.식 !== undefined
? ctx.engine.value(sheet.id, r, c)
: cell?.값 !== undefined
? toScalar(cell.값)
: null;
}
/** 조건부 서식(sub4) 이 값을 보는 형 — Frac 는 근사 수로(조건 비교용 · 오류는 조건 대상 아님). */
function toCondFmtValue(scalar: Scalar): CondFmtValue {
if (scalar === null || typeof scalar === "boolean" || typeof scalar === "string") return scalar;
if ("error" in scalar) return null;
return Number(scalar.n) / Number(scalar.d);
}
function cellDisplay(
ctx: SpreadsheetContext,
sheet: Sheet,
@@ -158,13 +176,7 @@ function cellDisplay(
hAlign0: HAlign,
code?: string,
): CellDisplay {
const cell = sheet.칸[toA1(r, c)];
const scalar: Scalar =
cell?.식 !== undefined
? ctx.engine.value(sheet.id, r, c)
: cell?.값 !== undefined
? toScalar(cell.값)
: null;
const scalar = scalarAt(ctx, sheet, r, c);
const formatted = formatValue(scalar, code);
const isError = typeof scalar === "object" && scalar !== null && "error" in scalar;
const hAlign: HAlign = hAlign0 !== "general" ? hAlign0 : formatted.정렬;
@@ -365,7 +377,13 @@ export function createGrid(ctx: SpreadsheetContext): GridHandle {
cellEl.dataset.c = String(c0);
const addr = toA1(r0, c0);
cellEl.dataset.addr = addr;
const style = cellStyleAt(book, sheet, r0, c0);
const baseStyle = cellStyleAt(book, sheet, r0, c0);
const condStyle = sheet.조건부서식?.length
? resolveCondFmtStyle(sheet, r0, c0, {
value: (rr, cc) => toCondFmtValue(scalarAt(ctx, sheet, rr, cc)),
})
: null;
const style = condStyle ? { ...baseStyle, ...condStyle } : baseStyle;
const borders: ResolvedBorders = resolveBoxBorders(
book,
sheet,
@@ -378,7 +396,8 @@ export function createGrid(ctx: SpreadsheetContext): GridHandle {
);
const display = cellDisplay(ctx, sheet, r0, c0, style.가로 ?? "general", style.형식);
applyCellStyle(cellEl, style, borders, display.hAlign, zoom);
if (display.color) cellEl.style.color = display.color;
// 조건부 서식 글자색이 이기고(엑셀과 같음) · 없으면 숫자 형식([Red] 등)이 정한 색.
if (display.color && !condStyle?.글자색) cellEl.style.color = display.color;
const comment = sheet.comments?.[addr];
setCellText(cellEl, display.text, display.title || comment || undefined);
cellEl.classList.toggle("is-error", display.isError);
@@ -35,9 +35,18 @@ const sheet: Sheet = {
A6: { 값: "선택 영역 가운데(centerContinuous) 보기", 서식: 5 },
A8: { 값: "hair 테두리 칸", 서식: 3 },
B8: { 값: 3.14159, 서식: 7 },
G9: { 식: "1/0" },
J10000: { 값: "맨 끝 칸(1만 행 스크롤 확인용)" },
},
comments: { H4: "메모 — ROUNDDOWN 끝수 확인" },
조건부서식: [
{
id: "cf1",
범위: [{ r0: 3, c0: 7, r1: 3, c1: 7 }],
조건: { 종류: "비교", 연산: "보다작음", 값: 0 },
스타일: { 채움: "#ffe0e0", 글자색: "#c00000", 굵게: true },
},
],
병합: ["A1:H1", "B8:C9"],
열: {
A: { 폭: 14 },
@@ -96,7 +105,8 @@ function noopEngine(): CalcEngine {
return {
update: () => [],
rebuild: () => {},
value: () => null,
value: (_sheet, r, c) =>
r === 8 && c === 6 ? { error: "#DIV/0!", why: "0 으로 나눔(시험 칸 G9)" } : null,
precedents: () => [],
snapshot: () => ({}),
};