feat(spreadsheet): D 잇기 — 정렬 · 자동 필터(sub3)
- spreadsheet_extras.ts — 수식 입력줄 앞 데이터 단추(↑ ↓ 정렬… 필터) · attachFilter 부품 붙임 · Alt+↓ 머리 칸 값 목록 - spreadsheet_keys.ts — Ctrl+Shift+L 필터 · Alt+↓ 자리 - spreadsheet_selection.ts — 걸러진 행도 방향키 · Enter 가 건너뜀 - spreadsheet.ts — recalc 뒤 부품 refresh Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6KXAabDTenEU7hrDQmCKY
This commit is contained in:
@@ -244,6 +244,7 @@ export function createSpreadsheet(
|
||||
recalc() {
|
||||
ctx.engine.rebuild(ctx.book);
|
||||
grid.render();
|
||||
for (const p of others) p.refresh();
|
||||
return ctx.engine.snapshot();
|
||||
},
|
||||
destroy() {
|
||||
|
||||
@@ -2,19 +2,22 @@
|
||||
* spreadsheet_extras.ts (주인 D)
|
||||
* 2단계 부품 잇기(sub1 파일들의 「잇는 법」) — 찾기 패널 · 메모 풍선(마우스 올림) · 메모 편집(Shift+F2) ·
|
||||
* 서식 붓(수식 입력줄 앞 단추 · 두 번 누르면 계속 · Esc 끔) · 골라 붙여넣기(Ctrl+Shift+V → 값만 · 서식만 · 수식만) ·
|
||||
* 도구 모음을 누른 뒤 초점을 격자로 되돌림.
|
||||
* 도구 모음을 누른 뒤 초점을 격자로 되돌림 · 정렬(↑ ↓ 정렬…) · 자동 필터(단추 · Ctrl+Shift+L · Alt+↓).
|
||||
* 빨간 세모는 C 격자 · 우클릭 [메모 …] 는 E 메뉴 몫(브레인 배정).
|
||||
* ========================================================================== */
|
||||
|
||||
import { el } from "@ui/ui_template_elements";
|
||||
import { parseRange } from "./spreadsheet_address";
|
||||
import { parseClipboard } from "./spreadsheet_clipboard";
|
||||
import { attachComments, getComment, setComment } from "./spreadsheet_comments";
|
||||
import type { Editor } from "./spreadsheet_editor";
|
||||
import { attachFilter, toggleFilter } from "./spreadsheet_filter";
|
||||
import { mountFindPanel } from "./spreadsheet_find_panel";
|
||||
import { createFormatPainter } from "./spreadsheet_format_painter";
|
||||
import type { KeyHooks } from "./spreadsheet_keys";
|
||||
import type { MouseHooks } from "./spreadsheet_mouse";
|
||||
import { pasteSpecial, type PasteSpecialMode } from "./spreadsheet_paste_special";
|
||||
import { openSortDialog, sortSelection } from "./spreadsheet_sort_dialog";
|
||||
import type { PartHandle, SpreadsheetContext } from "./spreadsheet_view_types";
|
||||
|
||||
const TEXT = {
|
||||
@@ -22,6 +25,10 @@ const TEXT = {
|
||||
pasteTitle: "골라 붙여넣기",
|
||||
modes: { 값: "값만", 서식: "서식만", 수식: "수식만" } as Record<PasteSpecialMode, string>,
|
||||
memo: "메모 — Ctrl+Enter 또는 밖을 누르면 적용 · Esc 취소",
|
||||
sortAsc: "오름차순 정렬(활성 칸 열 기준)",
|
||||
sortDesc: "내림차순 정렬(활성 칸 열 기준)",
|
||||
sortDialog: "정렬 — 기준 여럿 · 머리 행",
|
||||
filter: "자동 필터 켜기 · 끄기(Ctrl+Shift+L · 머리 칸에서 Alt+↓ 값 목록)",
|
||||
};
|
||||
|
||||
export interface Extras {
|
||||
@@ -108,6 +115,33 @@ export function attachExtras(
|
||||
});
|
||||
if (!ctx.readOnly) ed.bar.prepend(brush);
|
||||
|
||||
// ── 정렬 · 필터(sub3) — 수식 입력줄 앞 데이터 단추 ───────────────────────
|
||||
const filter = attachFilter(ctx);
|
||||
let closeSort: (() => void) | null = null;
|
||||
const dataButton = (text: string, title: string, run: () => void, dialog = false) => {
|
||||
const b = el("button", { className: "ss-brush", text, attrs: { type: "button", title } });
|
||||
b.addEventListener("click", () => {
|
||||
run();
|
||||
if (!dialog) back();
|
||||
});
|
||||
return b;
|
||||
};
|
||||
const data = [
|
||||
dataButton("↑", TEXT.sortAsc, () => sortSelection(ctx, false)),
|
||||
dataButton("↓", TEXT.sortDesc, () => sortSelection(ctx, true)),
|
||||
dataButton(
|
||||
"정렬…",
|
||||
TEXT.sortDialog,
|
||||
() => {
|
||||
closeSort?.();
|
||||
closeSort = openSortDialog(ctx);
|
||||
},
|
||||
true,
|
||||
),
|
||||
dataButton("필터", TEXT.filter, () => toggleFilter(ctx)),
|
||||
];
|
||||
if (!ctx.readOnly) brush.after(...data);
|
||||
|
||||
// ── 골라 붙여넣기 ─────────────────────────────────────────────────────────
|
||||
let armed = false;
|
||||
const picker = el("div", { className: "ss-pick", attrs: { hidden: "", title: TEXT.pasteTitle } });
|
||||
@@ -167,6 +201,8 @@ export function attachExtras(
|
||||
root: null,
|
||||
refresh: () => showBrush(),
|
||||
destroy() {
|
||||
closeSort?.();
|
||||
data.forEach((b) => b.remove());
|
||||
ctx.root.removeEventListener("paste", onPaste, true);
|
||||
toolbarRoot?.removeEventListener("click", onToolbarClick);
|
||||
toolbarRoot?.removeEventListener("change", back);
|
||||
@@ -181,6 +217,15 @@ export function attachExtras(
|
||||
find: (replace) => find.open(replace),
|
||||
pasteSpecial: () => (armed = true),
|
||||
comment: editMemo,
|
||||
filter: () => toggleFilter(ctx),
|
||||
dropdown() {
|
||||
const f = ctx.sheet().필터;
|
||||
const g = f && parseRange(f.범위);
|
||||
const { r, c } = ctx.selection.활성;
|
||||
if (!g || r !== g.r0 || c < g.c0 || c > g.c1) return false;
|
||||
filter.open(c);
|
||||
return true;
|
||||
},
|
||||
escape() {
|
||||
painter.clear();
|
||||
sticky = false;
|
||||
@@ -203,6 +248,6 @@ export function attachExtras(
|
||||
put(comments.root!, hit.r, hit.c); // showBalloon 의 cellBox 자리 → 화면 자리로 고침
|
||||
},
|
||||
},
|
||||
parts: [find, comments, self],
|
||||
parts: [find, comments, filter, self],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -41,6 +41,10 @@ export interface KeyHooks {
|
||||
comment?(): void;
|
||||
/** 고름 중 Esc(서식 붓 끄기 등) */
|
||||
escape?(): void;
|
||||
/** Ctrl+Shift+L 필터 켜기 · 끄기 */
|
||||
filter?(): void;
|
||||
/** Alt+↓ — 필터 머리 칸이면 값 목록을 열고 true */
|
||||
dropdown?(): boolean;
|
||||
}
|
||||
|
||||
export function attachKeys(ctx: SpreadsheetContext, ed: Editor, hooks: KeyHooks = {}): () => void {
|
||||
@@ -97,6 +101,7 @@ export function attachKeys(ctx: SpreadsheetContext, ed: Editor, hooks: KeyHooks
|
||||
const sheet = ctx.sheet();
|
||||
const sel = ctx.selection;
|
||||
const arrow = ARROWS[e.key];
|
||||
if (e.altKey && e.key === "ArrowDown" && hooks.dropdown?.()) return true;
|
||||
if (arrow && !e.altKey) {
|
||||
go(move(sheet, sel, arrow[0], arrow[1], { extend: e.shiftKey, jump: ctrl }));
|
||||
return true;
|
||||
@@ -135,6 +140,10 @@ export function attachKeys(ctx: SpreadsheetContext, ed: Editor, hooks: KeyHooks
|
||||
if (!hooks.find) return false;
|
||||
hooks.find(k === "h");
|
||||
return true;
|
||||
case "l":
|
||||
if (!e.shiftKey || !hooks.filter) return false;
|
||||
hooks.filter();
|
||||
return true;
|
||||
case "v":
|
||||
if (e.shiftKey) hooks.pasteSpecial?.();
|
||||
return false; // paste 사건은 그대로 — E 클립보드 · 골라 붙여넣기가 받음
|
||||
|
||||
@@ -51,7 +51,11 @@ export function mergeAt(sheet: Sheet, r: number, c: number): CellRange {
|
||||
return merges(sheet).find((m) => inRange(m, r, c)) ?? { r0: r, c0: c, r1: r, c1: c };
|
||||
}
|
||||
|
||||
export const rowHidden = (sheet: Sheet, r: number): boolean => !!sheet.행?.[String(r + 1)]?.숨김;
|
||||
/** 숨긴 행 · 필터로 걸러진 행(방향키 · Enter 가 건너뜀 — 엑셀) */
|
||||
export function rowHidden(sheet: Sheet, r: number): boolean {
|
||||
const row = sheet.행?.[String(r + 1)];
|
||||
return !!(row?.숨김 || row?.걸러짐);
|
||||
}
|
||||
|
||||
export function colHidden(sheet: Sheet, c: number): boolean {
|
||||
const cols = sheet.열;
|
||||
|
||||
Reference in New Issue
Block a user