feat(spreadsheet): 자동 필터 화면 — 머리 행 단추 · 값 목록 체크 · 이 열 정렬 · 필터 켜기 · 끄기
- spreadsheet_filter.ts — attachFilter(ctx) 단추 층(격자 overlay) · 값 목록 창(모두 선택 · 빈 칸 · 이 열 필터 지움 · 오름 · 내림) · toggleFilter · open(열) · 걸러진 행은 엔진이 행.걸러짐 으로 적음(사용자 숨김과 따로) · 잇는 법(D · C)은 머리 주석 - 시험 틀 harness_sortfilter.html · .ts · _bundle.cjs — 진짜 부품에 정렬 · 필터를 얹어 ORCA 로 봄 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TULoa94ZFL26KU6ZqVpjkF
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
<!doctype html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>정렬 · 필터 시험 틀</title>
|
||||
<link rel="stylesheet" href="../../../ui_template/ui_template_theme.css" />
|
||||
<style>
|
||||
body { margin: 0; font-family: sans-serif; }
|
||||
#bar { margin: 8px; display: flex; gap: 8px; }
|
||||
#host { height: 520px; margin: 8px; border: 1px solid #999; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="bar">
|
||||
<button id="btn-sort">정렬…</button>
|
||||
<button id="btn-asc">오름차순</button>
|
||||
<button id="btn-desc">내림차순</button>
|
||||
<button id="btn-filter">필터</button>
|
||||
<button id="btn-undo">되돌리기</button>
|
||||
</div>
|
||||
<div id="host"></div>
|
||||
<script>
|
||||
window.ssErrors = [];
|
||||
window.addEventListener("error", (e) => window.ssErrors.push(String(e.error?.stack || e.message)));
|
||||
</script>
|
||||
<script src="../../../tmp/spreadsheet_sortfilter/harness.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,85 @@
|
||||
/* 정렬 · 자동 필터 화면 시험 틀 입구 — 진짜 부품(createSpreadsheet)에 정렬 대화 상자 · 필터 단추를 얹음.
|
||||
* sub7 잇기 전이라 `parts.mountTabs` 를 감싸 문맥(ctx)을 꺼내고, grid.render 뒤 filter.refresh 를 부름.
|
||||
* 번들: node harness_sortfilter_bundle.cjs → tmp/spreadsheet_sortfilter/harness.js (git 밖) */
|
||||
import { createSpreadsheet, parts } from "../../../A00_Common/spreadsheet/spreadsheet";
|
||||
import {
|
||||
openSortDialog,
|
||||
sortSelection,
|
||||
} from "../../../A00_Common/spreadsheet/spreadsheet_sort_dialog";
|
||||
import { attachFilter, toggleFilter } from "../../../A00_Common/spreadsheet/spreadsheet_filter";
|
||||
import type { SpreadsheetContext } from "../../../A00_Common/spreadsheet/spreadsheet_view_types";
|
||||
import type { Workbook } from "../../../A00_Common/spreadsheet/spreadsheet_types";
|
||||
|
||||
const w = window as unknown as Record<string, unknown>;
|
||||
let ctx: SpreadsheetContext | null = null;
|
||||
const origTabs = parts.mountTabs;
|
||||
parts.mountTabs = (c: SpreadsheetContext) => {
|
||||
ctx = c;
|
||||
return origTabs(c);
|
||||
};
|
||||
|
||||
const rows: [string, string, number][] = [
|
||||
["터파기", "토사", 12.5],
|
||||
["되메우기", "토사", 3],
|
||||
["잡석", "", 7.25],
|
||||
["터파기", "풍화암", 4],
|
||||
["콘크리트", "18-21-8", 9.9],
|
||||
["되메우기", "토사", 0],
|
||||
["거푸집", "합판 3회", 21],
|
||||
["터파기", "토사", 1.5],
|
||||
];
|
||||
const 칸: Workbook["시트"][number]["칸"] = {
|
||||
A1: { 값: "공종" },
|
||||
B1: { 값: "규격" },
|
||||
C1: { 값: "수량" },
|
||||
D1: { 값: "두 배" },
|
||||
C12: { 식: "SUM(C2:C9)" },
|
||||
B12: { 값: "합계" },
|
||||
};
|
||||
rows.forEach(([a, b, c], i) => {
|
||||
const r = i + 2;
|
||||
칸[`A${r}`] = { 값: a };
|
||||
if (b) 칸[`B${r}`] = { 값: b };
|
||||
칸[`C${r}`] = { 값: c, 서식: 1 };
|
||||
칸[`D${r}`] = { 식: `C${r}*2`, 서식: 1 };
|
||||
});
|
||||
const book: Workbook = {
|
||||
종류: "통합문서",
|
||||
판: 1,
|
||||
열: "harness",
|
||||
서식: [{}, { 형식: "0.00" }],
|
||||
시트: [{ id: "s1", 이름: "정렬필터", 칸, 행: { 11: { 숨김: true } } }],
|
||||
활성: "s1",
|
||||
};
|
||||
|
||||
const host = document.getElementById("host")!;
|
||||
w.ss = createSpreadsheet(host, book, {
|
||||
onChange: () => (w.ssChanges = Number(w.ssChanges ?? 0) + 1),
|
||||
});
|
||||
const c = ctx as unknown as SpreadsheetContext;
|
||||
const filter = attachFilter(c);
|
||||
const render = c.grid.render.bind(c.grid);
|
||||
c.grid.render = () => {
|
||||
render();
|
||||
filter.refresh();
|
||||
};
|
||||
w.ssCtx = c;
|
||||
w.ssFilter = filter;
|
||||
w.ssSortDialog = () => openSortDialog(c);
|
||||
w.ssSortQuick = (desc: boolean) => sortSelection(c, desc);
|
||||
w.ssToggleFilter = () => toggleFilter(c);
|
||||
/** 열 값(보이는 칸 글) — 판정용 */
|
||||
w.ssCol = (letter: string, r0: number, r1: number) => {
|
||||
const out: string[] = [];
|
||||
for (let r = r0; r <= r1; r++)
|
||||
out.push(document.querySelector(`[data-a1="${letter}${r}"]`)?.textContent ?? "");
|
||||
return out;
|
||||
};
|
||||
for (const [id, run] of [
|
||||
["btn-sort", () => openSortDialog(c)],
|
||||
["btn-asc", () => sortSelection(c, false)],
|
||||
["btn-desc", () => sortSelection(c, true)],
|
||||
["btn-filter", () => toggleFilter(c)],
|
||||
["btn-undo", () => c.undo()],
|
||||
] as const)
|
||||
document.getElementById(id)!.addEventListener("click", run);
|
||||
@@ -0,0 +1,29 @@
|
||||
/* 정렬 · 필터 시험 틀 번들 — rolldown(이미 설치) 한 파일 · css 는 <style> 로 넣음 · @ui 별칭.
|
||||
* node harness_sortfilter_bundle.cjs → tmp/spreadsheet_sortfilter/harness.js (git 밖) */
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const ROOT = path.join(__dirname, "..", "..", "..");
|
||||
const { build } = require(path.join(ROOT, "config", "node_modules", "rolldown"));
|
||||
|
||||
build({
|
||||
input: path.join(__dirname, "harness_sortfilter.ts"),
|
||||
resolve: { alias: { "@ui": path.join(ROOT, "ui_template") } },
|
||||
plugins: [
|
||||
{
|
||||
name: "css-inline",
|
||||
load(id) {
|
||||
if (!id.endsWith(".css")) return null;
|
||||
const css = JSON.stringify(fs.readFileSync(id, "utf8"));
|
||||
const code = `const s = document.createElement("style"); s.textContent = ${css}; document.head.append(s);`;
|
||||
return { code, moduleType: "js" };
|
||||
},
|
||||
},
|
||||
],
|
||||
output: { file: path.join(ROOT, "tmp", "spreadsheet_sortfilter", "harness.js"), format: "iife" },
|
||||
}).then(
|
||||
() => console.log("ok"),
|
||||
(e) => {
|
||||
console.error(e.message);
|
||||
process.exit(1);
|
||||
},
|
||||
);
|
||||
Reference in New Issue
Block a user