open-grid 1.0.8 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/OpenGrid-5flQwc3W.js +8434 -0
  3. package/dist/OpenGrid-DahxRY7C.cjs +92 -0
  4. package/dist/open-grid-base.css +61 -0
  5. package/dist/open-grid-react.cjs +1 -1
  6. package/dist/open-grid-react.js +1 -1
  7. package/dist/open-grid-themes.css +96 -1
  8. package/dist/open-grid-vue.cjs +1 -1
  9. package/dist/open-grid-vue.js +1 -1
  10. package/dist/open-grid.cjs +1 -1
  11. package/dist/open-grid.js +2 -2
  12. package/dist/types/core/CellEditManager.d.ts +4 -0
  13. package/dist/types/core/CellEventHandler.d.ts +6 -0
  14. package/dist/types/core/ChartManager.d.ts +58 -0
  15. package/dist/types/core/DataLayer.d.ts +17 -0
  16. package/dist/types/core/DetailManager.d.ts +72 -0
  17. package/dist/types/core/FlatRowModel.d.ts +56 -0
  18. package/dist/types/core/GridRenderer.d.ts +33 -1
  19. package/dist/types/core/GroupTreeManager.d.ts +13 -0
  20. package/dist/types/core/KeyboardManager.d.ts +19 -0
  21. package/dist/types/core/OpenGrid.d.ts +109 -1
  22. package/dist/types/core/RangeSelectionManager.d.ts +95 -0
  23. package/dist/types/core/SortFilterManager.d.ts +2 -0
  24. package/dist/types/core/chart/CanvasAdapter.d.ts +52 -0
  25. package/dist/types/core/chart/DataExtractor.d.ts +49 -0
  26. package/dist/types/core/chart/a11y.d.ts +21 -0
  27. package/dist/types/core/chart/downsample.d.ts +25 -0
  28. package/dist/types/core/chart/hittest.d.ts +20 -0
  29. package/dist/types/core/chart/palette.d.ts +56 -0
  30. package/dist/types/core/chart/scales.d.ts +24 -0
  31. package/dist/types/core/chart/types.d.ts +181 -0
  32. package/dist/types/core/detail/DetailGlyph.d.ts +48 -0
  33. package/dist/types/core/detail/DetailSplice.d.ts +67 -0
  34. package/dist/types/core/detail/DetailState.d.ts +67 -0
  35. package/dist/types/core/detail/SubgridCache.d.ts +73 -0
  36. package/dist/types/core/detail/index.d.ts +14 -0
  37. package/dist/types/core/formula/FormulaEvaluator.d.ts +15 -0
  38. package/dist/types/core/formula/FormulaGraph.d.ts +43 -0
  39. package/dist/types/core/formula/FormulaParser.d.ts +6 -0
  40. package/dist/types/core/formula/FormulaStore.d.ts +17 -0
  41. package/dist/types/core/formula/RecalcCoordinator.d.ts +85 -0
  42. package/dist/types/core/formula/normalizeRefs.d.ts +15 -0
  43. package/dist/types/core/formula/numericLiteral.d.ts +7 -0
  44. package/dist/types/core/formula/serializeFormula.d.ts +6 -0
  45. package/dist/types/core/formula/types.d.ts +104 -0
  46. package/dist/types/core/range/ClipboardCodec.d.ts +30 -0
  47. package/dist/types/core/range/FillEngine.d.ts +63 -0
  48. package/dist/types/core/range/RangeModel.d.ts +53 -0
  49. package/dist/types/core/range/RangeQuery.d.ts +16 -0
  50. package/dist/types/core/range/types.d.ts +47 -0
  51. package/dist/types/core/renderers/CellRenderer.d.ts +2 -0
  52. package/dist/types/core/types.d.ts +242 -0
  53. package/dist/types/index.d.ts +2 -1
  54. package/package.json +1 -1
  55. package/dist/OpenGrid-CZRcxruq.cjs +0 -90
  56. package/dist/OpenGrid-Cjv7Os5a.js +0 -4871
@@ -1,8 +1,12 @@
1
1
  import { EventEmitter } from './EventEmitter.js';
2
2
  import { FilterSelectConfig } from './FilterSelect.js';
3
3
  import { MergeCell } from './MergeEngine.js';
4
+ import { FlatRowModel } from './FlatRowModel.js';
5
+ import { ChartInstance } from './ChartManager.js';
6
+ import { ChartConfig } from './chart/types.js';
4
7
  import { OverrideLayer } from './OverrideKernel.js';
5
- import { GridOptions, OpenGridInstance, ColumnDef, SortItem, FilterItem, ExportOptions, Position, TriggerHandler, TriggerEvent, OverrideApi, OverrideCallOptions } from './types.js';
8
+ import { FormulaErrorCode } from './formula/types.js';
9
+ import { GridOptions, OpenGridInstance, ColumnDef, SortItem, FilterItem, ExportOptions, Position, TriggerHandler, TriggerEvent, OverrideApi, OverrideCallOptions, CellRange, RangeStats } from './types.js';
6
10
  export declare class OpenGrid<T extends Record<string, any> = any> extends EventEmitter implements OpenGridInstance<T> {
7
11
  private _container;
8
12
  private _options;
@@ -36,6 +40,22 @@ export declare class OpenGrid<T extends Record<string, any> = any> extends Event
36
40
  private _findMgr;
37
41
  private _grpMgr;
38
42
  private _cellEvt;
43
+ /** F1: 범위 선택 + 채우기 핸들 배선 관리자(11_design_F1_v2.md). */
44
+ private _rangeMgr;
45
+ /** F2: 마스터/디테일 배선(11_design_F2_v2.md). 헤드리스 코어(core/detail/*)를 소비. */
46
+ private _detailMgr;
47
+ /** F4: 그리드 데이터 통합 차트 오케스트레이터(11_design_F4_v2.md). 헤드리스 chart/* 소비. */
48
+ private _chartMgr;
49
+ /** Phase 0(C0.3): flat/visual index ↔ data 리졸버. 어떤 기능도 켜지지 않아도 항상 존재. */
50
+ private _flatModel;
51
+ /** F3: 셀 수식 헤드리스 오케스트레이터(core/formula/* 소비, 재구현 아님). 항상 존재(헤드리스라 유휴 비용 0). */
52
+ private _recalc;
53
+ /** F3(C2): writeCell 단건 쓰기가 쌓아두는 dirty seed. endBatch/즉시 flush 시 onValuesChanged 1회로 소비. */
54
+ private _formulaDirtySeeds;
55
+ /** reentrant 카운터. 0 초과면 배치 중(writeCell 이 render/dataChange 를 지연). */
56
+ private _batchDepth;
57
+ /** 배치 구간 중 실제 쓰기가 1건이라도 있었는지(빈 배치는 endBatch 에서 아무 것도 안 함). */
58
+ private _batchDirty;
39
59
  private _ovk;
40
60
  /** 공개 override API (호출가능 + .strategy). 생성자 말미에 부착. */
41
61
  override: OverrideApi<T>;
@@ -88,6 +108,8 @@ export declare class OpenGrid<T extends Record<string, any> = any> extends Event
88
108
  */
89
109
  private _syncHeaderLayout;
90
110
  private _doRender;
111
+ /** F2(§4.5/§6.1): masterDetail.enabled 아니면 undefined(GridRenderer 는 기존 경로 그대로). */
112
+ private _buildDetailRenderContext;
91
113
  private _handleGroupToggle;
92
114
  private _visRange;
93
115
  private _visCount;
@@ -157,6 +179,73 @@ export declare class OpenGrid<T extends Record<string, any> = any> extends Event
157
179
  getDisplayValue(rowIndex: number, field: string): string;
158
180
  writeCell(rowIndex: number, field: string, value: any): void;
159
181
  getRowAt(rowIndex: number): T;
182
+ /** flat/visual index ↔ data 리졸버(C0.3). F1/F3/F4 는 이 모델만 경유해야 한다. */
183
+ getFlatRowModel(): FlatRowModel;
184
+ /**
185
+ * 배치 쓰기 시작(C2.1). 이후 writeCell 호출들은 render/dataChange 를 지연·coalesce 한다.
186
+ * 중첩 호출은 카운팅(reentrant) — 가장 바깥 endBatch 에서만 실제로 flush 된다.
187
+ */
188
+ beginBatch(): void;
189
+ /**
190
+ * 배치 종료(C2.1). 카운터가 0 이 되는 시점에 한해 배치 중 발생한 쓰기가 있으면
191
+ * _doRender 1회 + dataChange 1회를 발생시킨다(둘 다 0회 또는 1회 — 폭주 차단).
192
+ * 향후 F3 재계산 훅 지점: 아래 flush 블록 안에 RecalcCoordinator.onValuesChanged(dirtyKeys)
193
+ * 1회 호출을 끼워 넣는다(Phase 0 시점엔 미구현 — 계약 §C2.1 참조).
194
+ */
195
+ endBatch(): void;
196
+ /** writeCell 이 적립한 dirty seed 를 1회 onValuesChanged 로 소비하고 formulaRecalc 를 emit. */
197
+ private _flushFormulaRecalc;
198
+ /**
199
+ * RecalcCoordinator 호출 결과를 표면화: formulaError 는 onFormulaError 콜백에서 이미 개별
200
+ * emit 되므로 여기선 배치당 1회 formulaRecalc 만 emit(C2.2 개명, `recalc`→`formulaRecalc`).
201
+ * Spike-A §8 교훈: 폐포가 큰 재계산은 large 플래그로 표시(가이드 문서화/모니터링용).
202
+ */
203
+ private _afterRecalc;
204
+ private _handleFormulaError;
205
+ private _formulaErrorMessageKo;
206
+ /** F3 accessor(C0/C0.5/C1) — FlatRowModel + ColumnLayout.visibleLeaves + DataLayer(rowId 기반)만 본다. */
207
+ private _buildFormulaAccessor;
208
+ setCellFormula(rowIndex: number, field: string, formula: string): void;
209
+ private _setCellFormulaByRowId;
210
+ getCellFormula(rowIndex: number, field: string): string | null;
211
+ hasCellFormula(rowIndex: number, field: string): boolean;
212
+ clearCellFormula(rowIndex: number, field: string): void;
213
+ getCellError(rowIndex: number, field: string): FormulaErrorCode | null;
214
+ getDependents(rowIndex: number, field: string): Array<{
215
+ rowIndex: number;
216
+ field: string;
217
+ }>;
218
+ getPrecedents(rowIndex: number, field: string): Array<{
219
+ rowIndex: number;
220
+ field: string;
221
+ }>;
222
+ recalculate(): void;
223
+ recalculateCell(rowIndex: number, field: string): void;
224
+ /** C3(F1 fill 전용): srcRowId/srcField 수식의 상대축만 dRow/dCol 오프셋한 새 수식 원문. */
225
+ offsetFormula(srcRowId: string, srcField: string, dRow: number, dCol: number): string;
226
+ /** F3 렌더 배선(§4.4/C7, §7.4/§7.5/§7.6) — 셀 수식 메타(없으면 null). */
227
+ private _getFormulaMeta;
228
+ /**
229
+ * beginBatch+루프+endBatch 래퍼(C2.1). patches 의 rowIndex 는 flat index — 대상이
230
+ * FlatRowModel.resolveFlatRow 로 해소해 kind!=='data' (group/tree/detail 의사행)이면
231
+ * 쓰기 전에 skip 한다(C0.3 쓰기 안전, filler 에 writeCell 절대 금지).
232
+ * 건너뛴 셀 수를 반환하고, 1건이라도 있으면 announce + 'writeCellsSkip' 이벤트로 표면화한다.
233
+ */
234
+ writeCells(patches: Array<{
235
+ rowIndex: number;
236
+ field: string;
237
+ value: any;
238
+ }>): number;
239
+ getRangeSelection(): CellRange[];
240
+ getActiveRange(): CellRange | null;
241
+ setRangeSelection(range: CellRange | CellRange[]): void;
242
+ clearRangeSelection(): void;
243
+ getRangeValues(): any[][];
244
+ getRangeStats(): RangeStats | null;
245
+ fillRange(source: CellRange, target: CellRange, mode?: 'copy' | 'series'): void;
246
+ createChart(config: ChartConfig): ChartInstance;
247
+ getCharts(): ChartInstance[];
248
+ destroyCharts(): void;
160
249
  getChanges(): {
161
250
  added: T[];
162
251
  edited: T[];
@@ -213,6 +302,8 @@ export declare class OpenGrid<T extends Record<string, any> = any> extends Event
213
302
  resetOrder(): void;
214
303
  setFilter(field: string, filterItems: FilterItem[]): void;
215
304
  resetFilter(field?: string): void;
305
+ /** F3-R13/MCCONNELL-03(P0): 정렬/필터 후 범위-보유(hasRangeRef) 수식 전부 dirty(§3.5). */
306
+ private _recalcRangeBearingFormulas;
216
307
  getFilterState(): Record<string, FilterItem[]>;
217
308
  restoreFilter(state: Record<string, FilterItem[]>): void;
218
309
  private _applyFilters;
@@ -234,6 +325,23 @@ export declare class OpenGrid<T extends Record<string, any> = any> extends Event
234
325
  expandNodes(ids: any | any[], open?: boolean): void;
235
326
  expandAllNodes(): void;
236
327
  collapseAllNodes(): void;
328
+ expandRow(rowRef: number | {
329
+ id: string;
330
+ }): void;
331
+ collapseRow(rowRef: number | {
332
+ id: string;
333
+ }): void;
334
+ toggleRow(rowRef: number | {
335
+ id: string;
336
+ }): void;
337
+ isRowExpanded(rowRef: number | {
338
+ id: string;
339
+ }): boolean;
340
+ collapseAllDetails(): void;
341
+ getDetailInstance<D = any>(rowRef: number | {
342
+ id: string;
343
+ }): D | undefined;
344
+ resyncPanelWidths(): void;
237
345
  addTreeRow(_item: Partial<T>, _pid: string, _pos?: Position): void;
238
346
  exportExcel(options?: ExportOptions | string): void;
239
347
  exportCsv(options?: ExportOptions | string): void;
@@ -0,0 +1,95 @@
1
+ import { DataLayer } from './DataLayer.js';
2
+ import { ColumnLayout } from './ColumnLayout.js';
3
+ import { GridRenderer } from './GridRenderer.js';
4
+ import { CellEditManager } from './CellEditManager.js';
5
+ import { FlatRowModel } from './FlatRowModel.js';
6
+ import { GridOptions } from './types.js';
7
+ import { RangeStats } from './range/RangeQuery.js';
8
+ import { CellRange, Direction, FillMode, FillPreview } from './range/types.js';
9
+ export interface RangeSelectionDeps<T extends Record<string, any>> {
10
+ getOptions: () => Required<GridOptions<T>>;
11
+ getData: () => DataLayer<T>;
12
+ getColLayout: () => ColumnLayout<T>;
13
+ getFlatModel: () => FlatRowModel;
14
+ getRenderer: () => GridRenderer | null;
15
+ getEditMgr: () => CellEditManager<T>;
16
+ setFocusCell: (ri: number, ci: number) => void;
17
+ writeCells: (patches: Array<{
18
+ rowIndex: number;
19
+ field: string;
20
+ value: any;
21
+ }>) => number;
22
+ getDisplayValue: (ri: number, field: string) => string;
23
+ emit: (event: string, ...args: any[]) => void;
24
+ doRender: () => void;
25
+ announce: (msg: string) => void;
26
+ hasCellFormula?: (rowId: string, field: string) => boolean;
27
+ offsetFormula?: (rowId: string, field: string, dRow: number, dCol: number) => string;
28
+ setCellFormulaByRowId?: (rowId: string, field: string, formula: string) => void;
29
+ }
30
+ export declare class RangeSelectionManager<T extends Record<string, any> = any> {
31
+ private _d;
32
+ private _model;
33
+ private _isDragging;
34
+ private _fillDragSource;
35
+ private _fillDragStart;
36
+ private _fillPreview;
37
+ private _handlePointerId;
38
+ private _autoScrollRAF;
39
+ private _autoScrollVX;
40
+ private _autoScrollVY;
41
+ private _overlayEl;
42
+ private _borderEl;
43
+ private _previewEl;
44
+ private _handleEl;
45
+ private _mountedWrap;
46
+ constructor(deps: RangeSelectionDeps<T>);
47
+ isEnabled(): boolean;
48
+ private _rangeOpts;
49
+ private _editorActive;
50
+ hasSelection(): boolean;
51
+ getRangeSelection(): CellRange[];
52
+ getActiveRange(): CellRange | null;
53
+ getFillPreview(): FillPreview | null;
54
+ getRangeValues(): any[][];
55
+ getRangeStats(): RangeStats | null;
56
+ private _queryCtx;
57
+ handleCellMouseDown(ri: number, ci: number, e: MouseEvent): void;
58
+ handleCellMouseMove(ri: number, ci: number, e: MouseEvent): void;
59
+ handleCellMouseUp(_ri: number, _ci: number, _e: MouseEvent): void;
60
+ handleClick(ri: number, ci: number, shiftKey: boolean): void;
61
+ extendFocus(dir: Direction): void;
62
+ clear(): void;
63
+ clearRangeSelection(): void;
64
+ setRangeSelection(range: CellRange | CellRange[]): void;
65
+ /** applySort/applyFilter 직후 호출(C0.5, §2.5) — 선택은 rowId 집합 기준으로 재투영(해제 아님). */
66
+ reproject(): void;
67
+ private _afterModelChange;
68
+ private _announceSelection;
69
+ ctrlFill(axis: 'down' | 'right'): void;
70
+ /** 공개 API(§6.2): source→target 채우기. axis 는 두 rect 의 상대 위치로 추론한다. */
71
+ fillRange(source: CellRange, target: CellRange, mode?: FillMode): void;
72
+ private _isEditable;
73
+ private _commitFill;
74
+ /** KeyboardManager._copyToClipboard 가 소비. 범위 없으면 null(호출측이 기존 경로로 폴백). */
75
+ copyText(): string | null;
76
+ /** KeyboardManager._pasteFromClipboard 가 소비. true = 처리함(배치 경유), false = 범위 없음(폴백). */
77
+ pasteText(text: string): boolean;
78
+ private _maybeAutoscroll;
79
+ private _scrollSpeed;
80
+ private _runAutoscroll;
81
+ private _stopAutoscroll;
82
+ /** bodyWrap 자식으로 오버레이 1회 생성(renderBody 파괴 회피, CON-3 해법). */
83
+ mount(bodyWrap: HTMLElement): void;
84
+ /** _doRender 매 호출 뒤 실행(M-6 통합점) — 코너 셀 실측 → 연속 테두리/핸들/프리뷰 갱신(QA-2, CON-4). */
85
+ repaint(): void;
86
+ private _hideOverlay;
87
+ /** OpenGrid._doRender 가 renderBody extraOpts 로 전달(M-6). */
88
+ getOverlayExtraOpts(): {
89
+ _rangeRects: CellRange[];
90
+ };
91
+ private _resolveCellAtPoint;
92
+ private _onHandlePointerDown;
93
+ private _onHandlePointerMove;
94
+ private _onHandlePointerUp;
95
+ }
@@ -14,6 +14,8 @@ export interface SortFilterDeps<T extends Record<string, any>> {
14
14
  doRender: () => void;
15
15
  announce: (msg: string) => void;
16
16
  emit: (event: string, ...args: any[]) => void;
17
+ /** C0.5/§2.5: 정렬/필터 후 F1 범위 선택을 rowId 집합 기준으로 재투영(해제 아님). */
18
+ onReproject?: () => void;
17
19
  }
18
20
  export declare class SortFilterManager<T extends Record<string, any> = any> {
19
21
  private _sortList;
@@ -0,0 +1,52 @@
1
+ import { ChartAdapter, ChartDataModel, ChartPoint, ChartRenderSpec } from './types.js';
2
+ import { PointGeom } from './hittest.js';
3
+ export declare class CanvasAdapter implements ChartAdapter {
4
+ readonly id = "builtin-canvas";
5
+ private _host;
6
+ private _canvas;
7
+ private _a11yTable;
8
+ private _legend;
9
+ private _tooltip;
10
+ private _live;
11
+ private _spec;
12
+ private _model;
13
+ private _w;
14
+ private _h;
15
+ private _geoms;
16
+ private _hidden;
17
+ private _cursor;
18
+ private _onPoint;
19
+ init(host: HTMLElement, spec: ChartRenderSpec): Promise<void>;
20
+ render(model: ChartDataModel, spec: ChartRenderSpec): void;
21
+ resize(width: number, height: number): void;
22
+ onPointClick(cb: (p: ChartPoint) => void): void;
23
+ destroy(): void;
24
+ /** 렌더된 포인트 기하(hittest.spec/adapter.spec 검증용). */
25
+ getGeometry(): readonly PointGeom[];
26
+ private _renderA11yTable;
27
+ /**
28
+ * diff 렌더: series 수/이름이 이전과 동일하면 기존 버튼 DOM 을 재사용해 aria-pressed·색·
29
+ * 라벨만 갱신한다(포커스 보존). series 구성 자체가 바뀔 때만 통째로 재생성하되, 재생성 직전
30
+ * activeElement 가 범례 내부 버튼이면 재생성 후 동일 index 버튼에 포커스를 복원한다
31
+ * (코드리뷰 MAJOR: 라이브 재렌더가 범례 innerHTML='' 로 키보드 포커스를 유실하던 문제).
32
+ */
33
+ private _renderLegend;
34
+ /** 범례 버튼의 aria-pressed/색상 스와치/라벨(패턴 병기)을 현재 상태로 갱신(DOM 은 그대로). */
35
+ private _updateLegendButton;
36
+ private _styles;
37
+ private _plotRect;
38
+ private _valueExtent;
39
+ private _computeGeometry;
40
+ private _paint;
41
+ private _localXY;
42
+ private _onMouseMove;
43
+ private _onMouseLeave;
44
+ private _onClick;
45
+ private _onKeyDown;
46
+ private _cursorPoint;
47
+ private _announceCursor;
48
+ private _pointOf;
49
+ private _emitPoint;
50
+ private _showTooltip;
51
+ private _hideTooltip;
52
+ }
@@ -0,0 +1,49 @@
1
+ import { CellRange } from '../types.js';
2
+ import { ChartAggregate, ChartDataModel, ChartSeriesSpec, ChartSource } from './types.js';
3
+ import { ChartNumberFormat } from './a11y.js';
4
+ /** ColumnLayout.visibleLeaves 의 최소 투영(field/header/type 만 필요, C0.4). */
5
+ export interface ChartColumnRef {
6
+ field: string;
7
+ header?: string | undefined;
8
+ type?: string | undefined;
9
+ }
10
+ export interface ChartExtractDeps {
11
+ /** getData() 전체(display 순서) — kind='all'|'columns' */
12
+ getAllRows(): Array<Record<string, any>>;
13
+ /** getSelections() — kind='selection' */
14
+ getSelectedRows(): Array<Record<string, any>>;
15
+ /** getChecked() 의 row 부분 — kind='checked' */
16
+ getCheckedRows(): Array<Record<string, any>>;
17
+ /** ColumnLayout.visibleLeaves 투영(숨김 제외, C0.4) */
18
+ getVisibleColumns(): ChartColumnRef[];
19
+ /**
20
+ * kind='range' 전용: 스냅샷/현재 range 에 대응하는 행 배열(FlatRowModel 로 이미
21
+ * pseudo-row 를 제외한 상태, C0.3). 부재 시 range 소스는 selection 으로 강등한다(§7).
22
+ */
23
+ getRangeRows?: ((range: CellRange) => Array<Record<string, any>>) | undefined;
24
+ /** kind='range' 전용: range.startCol..endCol 에 대응하는 visibleLeaves 서브셋(C0.4). */
25
+ getRangeColumns?: ((range: CellRange) => ChartColumnRef[]) | undefined;
26
+ /** range 생략 시 초기값 취득(C4). 없거나 null 이면 selection 으로 강등(§7). */
27
+ getActiveRange?: (() => CellRange | null) | undefined;
28
+ }
29
+ export interface ChartExtractConfig {
30
+ /** 축 카테고리 컬럼 override(FR-2). 미지정 시 §2.2 자동 추론. */
31
+ category?: string | undefined;
32
+ /** 시리즈 컬럼 override(FR-2). 'columns' 소스는 필수(source.series 로도 지정 가능). */
33
+ series?: Array<string | ChartSeriesSpec> | undefined;
34
+ /** 동일 category 중복 시 축약(FR-3). 미지정 시 중복 category 를 그대로 유지(비집계). */
35
+ aggregate?: ChartAggregate | undefined;
36
+ title?: string | undefined;
37
+ numberFormat?: ChartNumberFormat | undefined;
38
+ }
39
+ /** 소스 강등 없이 실제로 사용된 소스 종류(§7 fallback 반영, 배지 판단에 쓰라고 반환). */
40
+ export interface ChartExtractResult {
41
+ model: ChartDataModel;
42
+ /** true 면 kind='range' 요청이 selection 으로 강등됐다(F1 부재/range 미해소, §7 HANMS-04). */
43
+ rangeFallback: boolean;
44
+ }
45
+ /**
46
+ * 소스(§2.1)로부터 `ChartDataModel`을 산출한다(FR-1: 4소스 → 동형 모델).
47
+ * `meta.a11yTable`은 항상 채운다(§B 하드 게이트, "생성비용 0" 미러).
48
+ */
49
+ export declare function extractChartData(source: ChartSource, deps: ChartExtractDeps, config?: ChartExtractConfig): ChartExtractResult;
@@ -0,0 +1,21 @@
1
+ import { ChartDataModel, A11yTableModel } from './types.js';
2
+ export type ChartNumberFormat = (v: number, ctx: {
3
+ axis: 'x' | 'y' | 'tooltip' | 'legend';
4
+ field?: string;
5
+ }) => string;
6
+ export interface BuildA11yTableOptions {
7
+ title?: string | undefined;
8
+ numberFormat?: ChartNumberFormat | undefined;
9
+ }
10
+ /**
11
+ * `ChartDataModel`(categories/series 부분)을 시각숨김 `<table>` 미러 데이터로 변환한다(§B.1).
12
+ * - colHeaders: ['category', ...series names]
13
+ * - rows: [category, v1, v2, ...] (locale 포맷 문자열, 결측=빈 문자열)
14
+ * - caption: 요약 — 카테고리 수·시리즈명 나열(스크린리더가 표 진입 전 듣는 개요).
15
+ */
16
+ export declare function buildA11yTable(model: Pick<ChartDataModel, 'categories' | 'series'>, opts?: BuildA11yTableOptions): A11yTableModel;
17
+ /**
18
+ * `<canvas role="img" aria-label="...">` 에 쓸 요약 문자열(§B.1).
19
+ * "{title}: {category}별 {series} — {요약}" 형태. 상세 값은 형제 `<table>`(aria-describedby)로.
20
+ */
21
+ export declare function chartAriaLabel(model: Pick<ChartDataModel, 'categories' | 'series'>, title?: string): string;
@@ -0,0 +1,25 @@
1
+ import { ChartDataModel, ChartType } from './types.js';
2
+ import { ChartNumberFormat } from './a11y.js';
3
+ /**
4
+ * LTTB 코어 — x(=category index 0..n-1)·y(대표값) 에서 threshold 개 인덱스를 고른다.
5
+ * 항상 첫/마지막을 포함한다. threshold>=n 이면 전 인덱스 반환.
6
+ */
7
+ export declare function lttbIndices(y: Array<number | null>, threshold: number): number[];
8
+ export interface DownsampleResult {
9
+ model: ChartDataModel;
10
+ /** true 면 축약이 일어났다(배지 §C). */
11
+ sampled: boolean;
12
+ }
13
+ export interface DownsampleOptions {
14
+ title?: string | undefined;
15
+ numberFormat?: ChartNumberFormat | undefined;
16
+ }
17
+ /**
18
+ * `ChartDataModel`을 maxPoints 이하로 축약한다(§8.2). categories/series 를 함께 줄여
19
+ * 정합을 유지하고, meta.sampled/sampledFrom/sampledTo 를 채운다(배지 §C). a11yTable 은
20
+ * 축약된 데이터로 재생성해 모델 자기정합을 유지한다(§B.4: 셀 수 == categories×(series+1)).
21
+ *
22
+ * - line/area: 공유 인덱스 LTTB + 각 series 엔벨로프(argmin/argmax) 강제 포함 → 극값 정확 보존.
23
+ * - 그 외(bar): 인접 category 를 균등 버킷으로 묶어 대표(첫 category 라벨) + 값 합산.
24
+ */
25
+ export declare function downsampleModel(model: ChartDataModel, maxPoints: number, type: ChartType, opts?: DownsampleOptions): DownsampleResult;
@@ -0,0 +1,20 @@
1
+ import { ChartType } from './types.js';
2
+ /** 렌더된 데이터포인트 하나의 기하. bar=사각형(x,y,w,h), line/marker=중심(cx,cy). */
3
+ export interface PointGeom {
4
+ seriesIndex: number;
5
+ categoryIndex: number;
6
+ /** 사각형 좌상단(bar). line 은 마커 bbox 좌상단으로도 채울 수 있다. */
7
+ x: number;
8
+ y: number;
9
+ w: number;
10
+ h: number;
11
+ /** 중심 좌표(line 마커 최근접용, bar 도 중심 계산해 채운다). */
12
+ cx: number;
13
+ cy: number;
14
+ }
15
+ /**
16
+ * 포인터 좌표(px,py)에 해당하는 데이터포인트 기하를 찾는다(§8.3).
17
+ * - bar/bar-*: 사각형 contains(미스면 null → 툴팁 숨김).
18
+ * - line/area: 최근접 x → 최근접 y(항상 하나 반환, 미스 개념 없음).
19
+ */
20
+ export declare function hitTest(geoms: readonly PointGeom[], px: number, py: number, type: ChartType): PointGeom | null;
@@ -0,0 +1,56 @@
1
+ import { ChartSeries } from './types.js';
2
+ /** Okabe-Ito 8색(색약 안전 세트) — 배치 순서는 인접 대비≥3:1이 되도록 §D에 맞춰 조정. */
3
+ export declare const OKABE_ITO: readonly string[];
4
+ /** 색 외 구분(§D): series 인덱스 순환 배정. bar=텍스처, line=대시+마커에 대응. */
5
+ export declare const PATTERN_CYCLE: ReadonlyArray<NonNullable<ChartSeries['pattern']>>;
6
+ export interface Rgb {
7
+ r: number;
8
+ g: number;
9
+ b: number;
10
+ }
11
+ /** '#rgb'/'#rrggbb'/'rgb(a)()' → RGB(0-255). 실패 시 null. */
12
+ export declare function parseColor(input: string): Rgb | null;
13
+ /** 'rgba(r, g, b, 1)' 정규화 문자열 — UR-2 theme_bind 비교용(§1). */
14
+ export declare function normalizeColor(input: string): string | null;
15
+ /** WCAG relative luminance (0..1). */
16
+ export declare function relativeLuminance(c: Rgb): number;
17
+ /** WCAG 대비비 (1..21). 파싱 실패 색은 1로 취급. */
18
+ export declare function contrastRatio(a: string, b: string): number;
19
+ export type Dichromacy = 'deuteranopia' | 'protanopia';
20
+ /** 색약 변환 시뮬레이션 — RGB in/out(0-255). */
21
+ export declare function simulateDichromacy(c: Rgb, kind: Dichromacy): Rgb;
22
+ /** 단순 유클리드 RGB 거리(ΔE 근사, 0..441). palette.spec 임계 비교용. */
23
+ export declare function colorDistance(a: Rgb, b: Rgb): number;
24
+ /**
25
+ * 인접 대비를 **최대화**하도록 팔레트를 그리디 재배치한다(§D "미달 시 팔레트 재배치").
26
+ * 첫 색에서 시작해 매번 직전 색과 대비가 가장 큰 색을 이어붙인다(O(n²), 결정론적).
27
+ *
28
+ * ⚠️ 정직한 한계(설계 갱신 필요): 8색 색약안전 팔레트는 **어떤 순열로도** 인접 luminance
29
+ * 대비 ≥3:1 을 전부 만족할 수 없다(수학적 상한 ≈1.52, 브루트포스 확인). WCAG 3:1 은 본디
30
+ * 전경-배경(텍스트/UI) 기준이지 인접 카테고리 기준이 아니다. 색약 구별의 실보증은 (1) 색약
31
+ * 시뮬 ΔE 분리(§simulateDichromacy, 인접 최소 ≈78)와 (2) 색 외 중복 부호화(패턴/대시/마커,
32
+ * PATTERN_CYCLE)에 있다. 본 함수는 대비를 "가능한 만큼" 키우는 best-effort 재배치다.
33
+ */
34
+ export declare function orderByAdjacentContrast(colors: readonly string[]): string[];
35
+ /** 인접 색쌍의 최소 대비비(테스트/재배치 판정용). */
36
+ export declare function minAdjacentContrast(colors: readonly string[]): number;
37
+ /** 인접 색쌍의 색약 시뮬 ΔE 최소값(deuteranopia/protanopia 중 나쁜 쪽). 색약 구별 실보증. */
38
+ export declare function minAdjacentDichromacyDeltaE(colors: readonly string[]): number;
39
+ export interface ResolvedSeriesStyle {
40
+ color: string;
41
+ pattern: NonNullable<ChartSeries['pattern']>;
42
+ }
43
+ /**
44
+ * series 별 색/패턴을 결정한다(§D).
45
+ * - series.color 명시가 최우선.
46
+ * - primary(테마 --og-primary)가 주어지면 첫 팔레트 색으로 유도하되, 다음 색과의
47
+ * 대비가 3:1 미달이면 안전 팔레트 원본을 그대로 쓴다(§D "fallback으로만").
48
+ * - 패턴은 인덱스 순환(유일성은 series≤4에서 보장, 초과 시 색+패턴 조합으로 구분).
49
+ */
50
+ export declare function resolveSeriesStyles(series: ReadonlyArray<{
51
+ color?: string | undefined;
52
+ pattern?: ChartSeries['pattern'] | undefined;
53
+ }>, opts?: {
54
+ palette?: readonly string[] | undefined;
55
+ primary?: string | undefined;
56
+ }): ResolvedSeriesStyle[];
@@ -0,0 +1,24 @@
1
+ /**
2
+ * F4 — nice-number 축 눈금 스케일(순수 함수, 헤드리스). §8.1(자동검증 대상 수학).
3
+ *
4
+ * 표준 Heckbert "Nice Numbers for Graph Labels" 알고리즘. 목표 tick 수(default 6)에 맞춰
5
+ * step 을 1/2/5×10^n 중에서 선택하고, [min,max] 를 그 step 배수로 확장한다.
6
+ *
7
+ * 한계(정직 명시): 이 알고리즘은 min=0 류의 "원점 기준" 범위에서는 매우 안정적으로
8
+ * tick 5~6개를 낸다(설계서 §8.1 문서화 예시 [0,97]→6 tick 포함). min≠0 인 임의 구간에서는
9
+ * step 후보가 {1,2,5,10} 로 성기어(coarse) tick 수가 4개나 7개로 튈 수 있다 — 표준 알고리즘의
10
+ * 알려진 한계이며, 본 구현은 문서화된 대표 입력(§8.1)에 대해서만 5~6 불변식을 보장한다.
11
+ */
12
+ export interface NiceScale {
13
+ min: number;
14
+ max: number;
15
+ step: number;
16
+ ticks: number[];
17
+ }
18
+ /** range 를 1/2/5×10^n 중 하나로 반올림한다(round=true: 가장 가까운 값, false: 올림 쪽 느슨한 값). */
19
+ export declare function niceNum(range: number, round: boolean): number;
20
+ /**
21
+ * [dataMin,dataMax] → nice range/step/tick 배열(§8.1). maxTicks 는 목표 tick 수(default 6).
22
+ * dataMin===dataMax(degenerate) 이면 값 주변으로 인위 확장한다.
23
+ */
24
+ export declare function niceScale(dataMin: number, dataMax: number, maxTicks?: number): NiceScale;