From 67c1d1f13d6a368ce4c837055440ae3666266d7d Mon Sep 17 00:00:00 2001 From: umsangdon Date: Wed, 4 Nov 2026 08:38:33 +0900 Subject: [PATCH] =?UTF-8?q?feat(779-3):=20=EA=B3=B5=EC=9A=A9=20=EA=B2=A9?= =?UTF-8?q?=EC=9E=90=20=EC=B9=A0=20=EC=98=B5=EC=85=98=20=E2=80=94=20?= =?UTF-8?q?=EC=B9=B8=20data-tone(success=20=C2=B7=20warning=20=C2=B7=20inf?= =?UTF-8?q?o=20=C2=B7=20danger=20=C2=B7=20muted=20=C2=B7=20mine=20?= =?UTF-8?q?=EB=9D=A0)=20=C2=B7=20repaint()=20=C2=B7=20=EC=9D=BD=EA=B8=B0?= =?UTF-8?q?=EB=A7=8C=20=EC=8B=9C=ED=8A=B8=20=ED=83=AD=20=E3=80=8C+?= =?UTF-8?q?=E3=80=8D=20=EC=88=A8=EA=B9=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 안 주면 data-tone 없음 · 보이는 칸만 부름 · 색은 --color-* 만 - 새 시험 test_779_3_grid_fill.py Co-Authored-By: Claude Sonnet 5.5 --- A00_Common/spreadsheet/spreadsheet.ts | 7 ++++ A00_Common/spreadsheet/spreadsheet_grid.css | 37 +++++++++++++++++++ .../spreadsheet/spreadsheet_grid_cells.ts | 3 ++ .../spreadsheet/spreadsheet_view_types.ts | 2 + resources/tester/test_779_3_grid_fill.py | 37 +++++++++++++++++++ 5 files changed, 86 insertions(+) create mode 100644 resources/tester/test_779_3_grid_fill.py diff --git a/A00_Common/spreadsheet/spreadsheet.ts b/A00_Common/spreadsheet/spreadsheet.ts index 4ae39fd85..2d5b09871 100644 --- a/A00_Common/spreadsheet/spreadsheet.ts +++ b/A00_Common/spreadsheet/spreadsheet.ts @@ -72,6 +72,9 @@ export interface SpreadsheetOptions { conditions?: ConditionOptions; /** 그림 열쇠 → 주소(요구 347-5 · 시트 `그림`) — 없으면 이름 적힌 빈 상자 */ figureSrc?: FigureSrc; + /** 칸 칠(요구 621-5) — 시트 id · 행 · 열(0 부터) → 꼴 이름 `success|warning|info|danger|muted`(흐림) + `mine`(왼쪽 띠) · + * 공백으로 여럿 · 칸 `data-tone` 으로 나감 · 보이는 칸만 부름 · 안 주면 화면 그대로 */ + 칠?: (시트: string, r: number, c: number) => string | null | undefined; } export interface SpreadsheetHandle { @@ -93,6 +96,8 @@ export interface SpreadsheetHandle { /** HTML 표를 지금 시트 고른 칸(왼위)부터 붙임(105-1 도면 → 면적) — 붙여넣기와 같은 길(병합 · 되돌리기 한 번) · * `check` = 덮을 자리의 값 칸 · 알약 칸 수 · 범위 → false 면 안 붙임 · 붙인 범위 A1 · 읽기만 · 못 하면 null */ pasteTable(html: string, check?: PasteCheck): Promise; + /** 보이는 칸만 다시 그림(`칠` 결과가 바뀐 뒤) — 문서는 안 읽음 */ + repaint(): void; /** 범위 고름 · 보이게 굴림(329-2 엑셀 보기 창) — `시트` = id 나 이름(없으면 지금 시트 · 다르면 그 탭으로) · * 범위 여럿 = 여러 고름 · 시트가 없거나 주소가 틀리면 false(고름 그대로) */ select(범위: string | string[], 시트?: string): boolean; @@ -138,6 +143,7 @@ export function createSpreadsheet( root, book, readOnly, + fill: opts.칠, engine: createCalcEngine(book), history: createHistory(), selection: selectCell(firstSheet(book), 0, 0), @@ -369,6 +375,7 @@ export function createSpreadsheet( return 이름; }, pasteTable: (html, check) => pasteTableAt(ctx, html, check), + repaint: () => grid.render(), select(range, sheetKey) { const target = sheetKey ? ctx.book.시트.find((s) => s.id === sheetKey || s.이름 === sheetKey) diff --git a/A00_Common/spreadsheet/spreadsheet_grid.css b/A00_Common/spreadsheet/spreadsheet_grid.css index d4c8eeb13..d3b141962 100644 --- a/A00_Common/spreadsheet/spreadsheet_grid.css +++ b/A00_Common/spreadsheet/spreadsheet_grid.css @@ -276,3 +276,40 @@ html[data-touch="on"] .aislo-grid__fill-handle { touch-action: pan-x pan-y; } } + +/* 칸 칠(옵션 `칠` · 요구 621-5) — 칸 원래 채움(인라인 background-color) 위에 겹침 · 어두움은 --color-* 가 따라감. */ +.aislo-cell[data-tone~="success"] { + background-image: linear-gradient( + color-mix(in srgb, var(--color-success) 18%, transparent), + color-mix(in srgb, var(--color-success) 18%, transparent) + ); +} +.aislo-cell[data-tone~="warning"] { + background-image: linear-gradient( + color-mix(in srgb, var(--color-warning) 18%, transparent), + color-mix(in srgb, var(--color-warning) 18%, transparent) + ); +} +.aislo-cell[data-tone~="info"] { + background-image: linear-gradient( + color-mix(in srgb, var(--color-info) 18%, transparent), + color-mix(in srgb, var(--color-info) 18%, transparent) + ); +} +.aislo-cell[data-tone~="danger"] { + background-image: linear-gradient( + color-mix(in srgb, var(--color-danger) 18%, transparent), + color-mix(in srgb, var(--color-danger) 18%, transparent) + ); +} +.aislo-cell[data-tone~="muted"] { + color: var(--color-text-muted) !important; +} +.aislo-cell[data-tone~="mine"] { + box-shadow: inset 3px 0 0 var(--color-accent); +} + +/* 읽기만 — 시트 탭 「+」 숨김 */ +.ss--readonly .ss-tabs__add { + display: none; +} diff --git a/A00_Common/spreadsheet/spreadsheet_grid_cells.ts b/A00_Common/spreadsheet/spreadsheet_grid_cells.ts index 21160b525..f7aa3b203 100644 --- a/A00_Common/spreadsheet/spreadsheet_grid_cells.ts +++ b/A00_Common/spreadsheet/spreadsheet_grid_cells.ts @@ -319,6 +319,9 @@ export function renderCells( if (need > w * 0.9) longs.push({ r: r0, c: c0, el: cellEl, own: w, need, align }); } } + const tone = ctx.fill?.(sheet.id, r0, c0); + if (tone) cellEl.dataset.tone = tone; + else delete cellEl.dataset.tone; // 재사용 div — 앞 칸 꼴을 지움 cellEl.classList.toggle("is-error", display.isError); cellEl.classList.toggle("has-comment", !!comment); cellEl.classList.toggle( diff --git a/A00_Common/spreadsheet/spreadsheet_view_types.ts b/A00_Common/spreadsheet/spreadsheet_view_types.ts index 6158bbf9c..a88aa2811 100644 --- a/A00_Common/spreadsheet/spreadsheet_view_types.ts +++ b/A00_Common/spreadsheet/spreadsheet_view_types.ts @@ -94,6 +94,8 @@ export interface SpreadsheetContext { /** 지금 문서 — 명령만이 바꿈(직접 고치지 말 것) */ book: Workbook; readOnly: boolean; + /** 칸 칠(옵션 `칠`) — 시트 id · 행 · 열 → 꼴 이름(공백으로 여럿 · 없으면 칠 없음) · 보이는 칸만 부름 */ + fill?: (시트: string, r: number, c: number) => string | null | undefined; engine: CalcEngine; history: History; selection: Selection; diff --git a/resources/tester/test_779_3_grid_fill.py b/resources/tester/test_779_3_grid_fill.py new file mode 100644 index 000000000..92356721f --- /dev/null +++ b/resources/tester/test_779_3_grid_fill.py @@ -0,0 +1,37 @@ +"""779-3 지킴 — 공용 격자 「칠」 옵션(요구 621-5): 안 주면 data-tone 없음 · 주면 보이는 칸만 · 색은 --color-* 만. +화면 수치 판정은 tmp 의 playwright 일회 검증 · 여기서는 길이 빠지지 않았는지만 지킴.""" + +import re +from pathlib import Path + +SS = Path(__file__).parents[2] / "A00_Common/spreadsheet" + + +def read(name: str) -> str: + return (SS / name).read_text(encoding="utf-8") + + +def test_fill_option_flows_option_to_ctx_to_cell(): + assert "칠?:" in read("spreadsheet.ts") and "fill: opts.칠" in read("spreadsheet.ts") + assert "fill?:" in read("spreadsheet_view_types.ts") + + +def test_tone_set_only_in_visible_cell_loop_and_cleared_when_absent(): + cells = read("spreadsheet_grid_cells.ts") + assert cells.count("ctx.fill?.(") == 1 # 칸 그리는 반복(보이는 칸) 안 한 곳뿐 + assert "cellEl.dataset.tone = tone" in cells + assert "delete cellEl.dataset.tone" in cells # 재사용 div — 안 주면 data-tone 없음 + + +def test_repaint_handle_only_rerenders_grid(): + assert re.search(r"repaint:\s*\(\)\s*=>\s*grid\.render\(\)", read("spreadsheet.ts")) + + +def test_css_tones_use_color_tokens_only_and_readonly_hides_add(): + css = read("spreadsheet_grid.css") + tone = css[css.index('[data-tone~="success"]') :] + for name in ("success", "warning", "info", "danger"): + assert f"var(--color-{name}) 18%" in tone + assert "--draw-" not in tone + assert "var(--color-text-muted)" in tone and "var(--color-accent)" in tone + assert ".ss--readonly .ss-tabs__add" in css