open-grid 1.1.0 → 1.1.1

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.
@@ -1,12 +1,15 @@
1
1
  import { EventEmitter } from './EventEmitter.js';
2
+ import { SkinTokenDelta, GridOptions, OpenGridInstance, ColumnDef, SortItem, FilterItem, ExportOptions, Position, TriggerHandler, TriggerEvent, OverrideApi, OverrideCallOptions, CellRange, RangeStats } from './types.js';
2
3
  import { FilterSelectConfig } from './FilterSelect.js';
3
4
  import { MergeCell } from './MergeEngine.js';
5
+ import { RendererFactory } from './renderers/CellRenderer.js';
6
+ import { EditorFactory } from './editors/CellEditor.js';
4
7
  import { FlatRowModel } from './FlatRowModel.js';
5
8
  import { ChartInstance } from './ChartManager.js';
6
9
  import { ChartConfig } from './chart/types.js';
7
10
  import { OverrideLayer } from './OverrideKernel.js';
11
+ import { ExtensionPointRegistry } from './ExtensionPointRegistry.js';
8
12
  import { FormulaErrorCode } from './formula/types.js';
9
- import { GridOptions, OpenGridInstance, ColumnDef, SortItem, FilterItem, ExportOptions, Position, TriggerHandler, TriggerEvent, OverrideApi, OverrideCallOptions, CellRange, RangeStats } from './types.js';
10
13
  export declare class OpenGrid<T extends Record<string, any> = any> extends EventEmitter implements OpenGridInstance<T> {
11
14
  private _container;
12
15
  private _options;
@@ -15,12 +18,22 @@ export declare class OpenGrid<T extends Record<string, any> = any> extends Event
15
18
  private _vs;
16
19
  private _ro;
17
20
  private _renderer;
21
+ private _appearance;
22
+ /** R12c: per-instance 아이콘 오버라이드 레지스트리(전역 iconRegistry 의 child). 첫 setIcon 때 지연 생성. */
23
+ private _icons;
18
24
  private _sfMgr;
19
25
  private _rowMgr;
20
26
  private _editMgr;
21
27
  private _trigMgr;
22
28
  private _destroyed;
23
- private _autoHeightWarned;
29
+ /** R5(§3.1 C4): 렌더 루프 컨트롤러. `_mount` 에서 renderer/vs 배선 초입에 생성. */
30
+ private _render;
31
+ /** R6(§3.1 C5): 데이터 변경 서비스(setData/insert/push/delete/writeCell/writeCells + batch + commit). */
32
+ private _mutation;
33
+ /** R7(§3.1 C9): F3 수식 컨트롤러(accessor 조립·recalc flush·에러 표현 + 공개 수식 API). */
34
+ private _formula;
35
+ /** R7(§3.1 C8): 크로스그리드 컨트롤러(moveRowsTo/매핑/3단계 드롭 발화). */
36
+ private _cross;
24
37
  private _colWidths;
25
38
  /** 사용자가 헤더 드래그로 직접 조절한 컬럼 폭 (field 기준). _recalcWidths 가 덮어쓰지 않도록 보존 */
26
39
  private _userWidths;
@@ -52,13 +65,13 @@ export declare class OpenGrid<T extends Record<string, any> = any> extends Event
52
65
  private _recalc;
53
66
  /** F3(C2): writeCell 단건 쓰기가 쌓아두는 dirty seed. endBatch/즉시 flush 시 onValuesChanged 1회로 소비. */
54
67
  private _formulaDirtySeeds;
55
- /** reentrant 카운터. 0 초과면 배치 중(writeCell 이 render/dataChange 를 지연). */
56
- private _batchDepth;
57
- /** 배치 구간 중 실제 쓰기가 1건이라도 있었는지(빈 배치는 endBatch 에서 아무 것도 안 함). */
58
- private _batchDirty;
59
68
  private _ovk;
60
69
  /** 공개 override API (호출가능 + .strategy). 생성자 말미에 부착. */
61
70
  override: OverrideApi<T>;
71
+ /** R11(§3.1 C7): 커널 위 타입드 확장점 레지스트리. _mount 초입에 생성(커널 준비 후). */
72
+ private _extensions;
73
+ /** R11: 타입드 확장점 레지스트리 정면(렌더훅 등록·strategy/override 타입드 카탈로그·MutationHook·catalog). */
74
+ get extensions(): ExtensionPointRegistry<T>;
62
75
  /** 정적 전역 override 레이어 (모든 신규 인스턴스에 생성자 말미 적용). */
63
76
  private static _defaultOverrides;
64
77
  /** 정적 전역 strategy 슬롯. */
@@ -69,6 +82,31 @@ export declare class OpenGrid<T extends Record<string, any> = any> extends Event
69
82
  static defaults: {
70
83
  strategy(slot: string, fn: Function): typeof OpenGrid;
71
84
  };
85
+ /**
86
+ * 커스텀 셀 렌더러 타입을 코어 편집 없이 등록. `col.type`/`col.renderer`(문자열 또는 `{type}`)가
87
+ * `typeName` 과 일치하면 등록 팩토리가 렌더러를 생성한다. 프로세스 전역(모든 그리드 인스턴스 공유).
88
+ */
89
+ static registerRenderer(typeName: string, factory: RendererFactory): typeof OpenGrid;
90
+ /** 커스텀 셀 에디터 타입을 코어 편집 없이 등록(OCP). 프로세스 전역. */
91
+ static registerEditor(typeName: string, factory: EditorFactory): typeof OpenGrid;
92
+ /**
93
+ * R12b(item3 §6.2): 커스텀 스킨을 코어 편집 없이 등록(defaultOverride 와 동형의 전역 정책).
94
+ * FORM-only 검증(색 리터럴 거부 = 색⊥형태 직교성) + HANMS 접근성 가드레일(포커스 <2px/none 클램프)
95
+ * 적용 후, 런타임 `<style>` 로 `.og-container[data-og-skin="name"]` 블록을 주입한다. 이후 어떤
96
+ * 인스턴스도 `grid.setSkin(name)` 으로 사용. 색값이 델타에 있으면 **throw**.
97
+ *
98
+ * 예) Neumorph 는 기본 카탈로그에서 컷(HANMS §1.3)됐으나 여기서 레시피로 재현 가능:
99
+ * OpenGrid.defineSkin('neumorph', { '--og-radius-md':'14px', '--og-elevation-inset':'inset -4px -4px 8px', … })
100
+ */
101
+ static defineSkin(name: string, delta: SkinTokenDelta): typeof OpenGrid;
102
+ /**
103
+ * R12c(item3 §5.1, 계약 C13): 시맨틱 아이콘 role 세트를 코어 편집 없이 **전역** 등록/교체
104
+ * (defineSkin/registerRenderer 와 동형의 프로세스 전역 정책). 값은 알려진 아이콘 key(`BOOTSTRAP_ICONS`)
105
+ * 이거나 원시 SVG 본문 마크업. 이후 모든 인스턴스의 아이콘 해석에 반영된다. per-instance 교체는
106
+ * `grid.setIcon(role, svg)` 참조.
107
+ * 예) OpenGrid.defineIconSet({ 'sort.asc': 'arrow-up', 'row.delete': '<path d="…"/>' })
108
+ */
109
+ static defineIconSet(map: Record<string, string>): typeof OpenGrid;
72
110
  constructor(container: string | HTMLElement, options: GridOptions<T>);
73
111
  restore(name: string): this;
74
112
  restoreAll(): this;
@@ -98,21 +136,15 @@ export declare class OpenGrid<T extends Record<string, any> = any> extends Event
98
136
  getWorksheet(name: string): import('./types').WorksheetState<T> | undefined;
99
137
  getWorksheetNames(): string[];
100
138
  exportSheetsExcel(filename?: string): void;
101
- private _paginationHeight;
102
139
  private _onResize;
103
140
  private _recalcWidths;
104
141
  private _renderHeader;
105
- /**
106
- * 렌더된 헤더의 실제 높이를 측정해 본문(bodyWrap) 높이와 뷰포트 높이에 반영한다.
107
- * 줄바꿈이 없는 헤더는 측정 높이가 headerHeight 이하라 기존 고정 동작과 동일하다.
108
- */
109
142
  private _syncHeaderLayout;
110
143
  private _doRender;
111
144
  /** F2(§4.5/§6.1): masterDetail.enabled 아니면 undefined(GridRenderer 는 기존 경로 그대로). */
112
145
  private _buildDetailRenderContext;
113
146
  private _handleGroupToggle;
114
147
  private _visRange;
115
- private _visCount;
116
148
  private _handleSortClick;
117
149
  private _isToggleCol;
118
150
  private _handleCellClick;
@@ -131,27 +163,13 @@ export declare class OpenGrid<T extends Record<string, any> = any> extends Event
131
163
  _crossBodyEl(): HTMLElement;
132
164
  /** fromIndex 를 잡고 드래그할 때 함께 이동할 행 집합 (다중선택에 포함되면 선택 전체) */
133
165
  private _dragRowSet;
134
- /** 드래그 드롭 어댑터 → 공개 moveRowsTo 로 위임 */
135
- private _handleCrossGridDrop;
136
166
  /**
137
167
  * 이 그리드의 행들을 다른 그리드로 이동(move)한다. 드래그·화살표 셔틀 공통 경로.
138
168
  * 3단계 이벤트(before→after→complete)와 crossGridMapping(필드 매핑)을 적용한다.
139
- * @param targetGrid 대상 그리드
140
- * @param sourceIndexes 이동할 (표시)행 인덱스들
141
- * @param targetIndex 대상에서 삽입 위치 (생략 시 맨 끝에 추가)
142
- * @returns 이동 성공 true, 취소/무효 false
143
169
  */
144
170
  moveRowsTo(target: OpenGridInstance<T>, sourceIndexes: number[], targetIndex?: number): Promise<boolean>;
145
171
  /** 체크된 행을 다른 그리드로 이동 (화살표 셔틀용). 체크 없으면 무시. */
146
172
  moveCheckedTo(target: OpenGridInstance<T>): Promise<boolean>;
147
- /**
148
- * 크로스그리드 이동의 행 변환 함수를 결정한다.
149
- * 반환: null=변환 불필요(그대로) / 함수=변환 적용 / false=매핑 모달 취소(이동 중단)
150
- */
151
- private _resolveCrossTransform;
152
- private _fireGridDropBefore;
153
- private _fireGridDropAfter;
154
- private _fireGridDropComplete;
155
173
  /** 행을 드롭으로 이동할 때 실행된다 */
156
174
  /** ???꾩튂 ?대룞 */
157
175
  reorderRow(fromIndex: number, toIndex: number): void;
@@ -174,6 +192,10 @@ export declare class OpenGrid<T extends Record<string, any> = any> extends Event
174
192
  /** @deprecated 하위호환 alias → unshiftRow */
175
193
  prependRows(items: Partial<T> | Partial<T>[]): void;
176
194
  deleteRow(rowIndex: number | number[]): void;
195
+ /**
196
+ * @deprecated no-op stub — id 기반 삭제는 미구현(본문 없음). 인덱스로 지우려면 `deleteRow(rowIndex)`
197
+ * 를, id→인덱스 변환은 `getFlatRowModel()`/`getData()` 조회 후 `deleteRow` 를 사용.
198
+ */
177
199
  deleteById(_ids: string[]): void;
178
200
  readCell(rowIndex: number, field: string): any;
179
201
  getDisplayValue(rowIndex: number, field: string): string;
@@ -181,32 +203,9 @@ export declare class OpenGrid<T extends Record<string, any> = any> extends Event
181
203
  getRowAt(rowIndex: number): T;
182
204
  /** flat/visual index ↔ data 리졸버(C0.3). F1/F3/F4 는 이 모델만 경유해야 한다. */
183
205
  getFlatRowModel(): FlatRowModel;
184
- /**
185
- * 배치 쓰기 시작(C2.1). 이후 writeCell 호출들은 render/dataChange 를 지연·coalesce 한다.
186
- * 중첩 호출은 카운팅(reentrant) — 가장 바깥 endBatch 에서만 실제로 flush 된다.
187
- */
188
206
  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
207
  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
208
  setCellFormula(rowIndex: number, field: string, formula: string): void;
209
- private _setCellFormulaByRowId;
210
209
  getCellFormula(rowIndex: number, field: string): string | null;
211
210
  hasCellFormula(rowIndex: number, field: string): boolean;
212
211
  clearCellFormula(rowIndex: number, field: string): void;
@@ -223,8 +222,6 @@ export declare class OpenGrid<T extends Record<string, any> = any> extends Event
223
222
  recalculateCell(rowIndex: number, field: string): void;
224
223
  /** C3(F1 fill 전용): srcRowId/srcField 수식의 상대축만 dRow/dCol 오프셋한 새 수식 원문. */
225
224
  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
225
  /**
229
226
  * beginBatch+루프+endBatch 래퍼(C2.1). patches 의 rowIndex 는 flat index — 대상이
230
227
  * FlatRowModel.resolveFlatRow 로 해소해 kind!=='data' (group/tree/detail 의사행)이면
@@ -266,8 +263,12 @@ export declare class OpenGrid<T extends Record<string, any> = any> extends Event
266
263
  getRemovedRows(): T[];
267
264
  getOriginalRow(rowIndex: number): T | undefined;
268
265
  getRowsWithState(stateField: string): T[];
266
+ /** @deprecated no-op stub — undo/redo 히스토리는 미구현. 변이 가드는 TriggerManager `before:*` 훅,
267
+ * 변경 추적은 `getChanges()`/`getOriginalRow()` 를 사용. */
269
268
  undo(): void;
269
+ /** @deprecated no-op stub — undo/redo 히스토리 미구현(위 `undo()` 참조). */
270
270
  redo(): void;
271
+ /** @deprecated no-op stub — undo/redo 히스토리 자체가 없어 비울 것도 없음(위 `undo()` 참조). */
271
272
  clearHistory(): void;
272
273
  getColumnDefs(): ColumnDef<T>[];
273
274
  getAllColumnDefs(): ColumnDef<T>[];
@@ -282,7 +283,10 @@ export declare class OpenGrid<T extends Record<string, any> = any> extends Event
282
283
  getFieldAt(idx: number): string;
283
284
  getColValues(field: string, _all?: boolean): any[];
284
285
  getUniqueValues(field: string, all?: boolean): any[];
286
+ /** @deprecated no-op stub — 컬럼 폭 일괄 설정 미구현. 폭은 `ColumnDef.width` 또는 헤더 드래그로
287
+ * 지정하며 내부 `_recalcWidths` 가 자동 배분한다. */
285
288
  setColWidths(_widths: number[]): void;
289
+ /** @deprecated no-op stub — 항상 빈 배열 반환(폭 계산 미구현). 자동 배분은 내부 `_recalcWidths` 담당. */
286
290
  calcColWidths(_fitToGrid?: boolean): number[];
287
291
  getSelections(): T[];
288
292
  getActiveRow(): number;
@@ -293,9 +297,12 @@ export declare class OpenGrid<T extends Record<string, any> = any> extends Event
293
297
  rowIndex: number;
294
298
  }>;
295
299
  getAllChecked(): T[];
300
+ /** @deprecated no-op stub — id 기반 체크 미구현. 값 기준 체크는 `checkByValue(field, values)` 사용. */
296
301
  checkById(_ids: string[]): void;
302
+ /** @deprecated no-op stub — id 기반 체크 추가 미구현(위 `checkById()` 참조). `checkByValue` 사용. */
297
303
  addCheckById(_ids: string[]): void;
298
304
  checkByValue(field: string, values: any[]): void;
305
+ /** @deprecated no-op stub — id 기반 체크 해제 미구현. 전체 해제는 `uncheckAll()` 사용. */
299
306
  uncheckById(_ids: string[]): void;
300
307
  uncheckAll(): void;
301
308
  orderBy(fieldOrList: string | SortItem[], dir?: 'asc' | 'desc'): void;
@@ -315,6 +322,7 @@ export declare class OpenGrid<T extends Record<string, any> = any> extends Event
315
322
  autoMerge(fields: string[]): void;
316
323
  /** 蹂묓빀 ?댁젣 */
317
324
  clearMerge(): void;
325
+ /** @deprecated no-op stub — 행 고정(freeze rows) 미구현. 컬럼 고정만 지원하며 `freeze(n)` 사용. */
318
326
  freezeRows(_n: number): void;
319
327
  groupBy(fields: string[]): void;
320
328
  clearGroup(): void;
@@ -342,6 +350,8 @@ export declare class OpenGrid<T extends Record<string, any> = any> extends Event
342
350
  id: string;
343
351
  }): D | undefined;
344
352
  resyncPanelWidths(): void;
353
+ /** @deprecated no-op stub — 부모 지정 트리행 삽입 미구현. 평면 삽입은 `insertRow(item, pos)`,
354
+ * 트리 구성은 `enableTree()`/`groupBy(fields)` 사용. */
345
355
  addTreeRow(_item: Partial<T>, _pid: string, _pos?: Position): void;
346
356
  exportExcel(options?: ExportOptions | string): void;
347
357
  exportCsv(options?: ExportOptions | string): void;
@@ -352,6 +362,7 @@ export declare class OpenGrid<T extends Record<string, any> = any> extends Event
352
362
  }): void;
353
363
  toArray(keyValue?: boolean): any[];
354
364
  jumpToRow(rowIndex: number): void;
365
+ /** @deprecated no-op stub — 특정 컬럼으로 가로 스크롤 이동 미구현. 행 이동은 `jumpToRow(rowIndex)` 사용. */
355
366
  jumpToCol(_field: string): void;
356
367
  getScrollPos(): {
357
368
  x: number;
@@ -364,6 +375,32 @@ export declare class OpenGrid<T extends Record<string, any> = any> extends Event
364
375
  resize(w?: number, h?: number): void;
365
376
  setTheme(theme: string): void;
366
377
  setThemeVar(k: string, v: string): void;
378
+ /**
379
+ * R12b: 스킨(FORM 축) 전환. data-og-skin 설정 + resolver 컨텍스트 교체 + 인라인 form 사이트 재해석.
380
+ * 색 테마와 직교(색 토큰 무변경). default→named 전환 시 인라인 보더가 리터럴→var() 로 승격되므로
381
+ * 헤더/본문을 한 번 재렌더한다(opt-in 비용). named→named/…→default 도 동일 경로로 안전.
382
+ */
383
+ setSkin(skin: string): void;
384
+ /** R12b: 현재 스킨 id('default' = 오늘). */
385
+ getSkin(): string;
386
+ /**
387
+ * R12c(계약 C13): 이 그리드 인스턴스에 한해 시맨틱 아이콘 role 을 교체(멀티그리드 격리 —
388
+ * 전역 iconRegistry 를 부모로 하는 child 에만 기록). svgOrKey 는 알려진 아이콘 key 또는 원시 SVG 본문.
389
+ * R11 확장점과의 연결: 오버라이드를 `extensions.iconResolver` strategy 슬롯으로도 표면화해 발견가능하게 한다.
390
+ * 반환은 this(체이닝). 미지정 role/글리프는 안전 폴백(never throw).
391
+ */
392
+ setIcon(role: string, svgOrKey: string): this;
393
+ /** R12c: 이 인스턴스의 아이콘 role 해석(오버라이드 우선, 없으면 전역). 렌더 마크업/SVGElement. */
394
+ renderIcon(role: string, opts?: {
395
+ size?: number;
396
+ title?: string;
397
+ el?: boolean;
398
+ }): string | SVGElement;
399
+ /**
400
+ * R12b: FORM 축 단일 토큰 런타임 오버라이드(setThemeVar 의 형태-축 형제). 컨테이너 인라인이라
401
+ * 스타일시트를 항상 이긴다. 색⊥형태 직교성 보호를 위해 색 값은 거부한다.
402
+ */
403
+ setSkinVar(k: string, v: string): void;
367
404
  addTrigger(event: TriggerEvent | string, handler: TriggerHandler): this;
368
405
  removeTrigger(event: TriggerEvent | string, handler: TriggerHandler): this;
369
406
  clearTriggers(event?: TriggerEvent | string): this;
@@ -25,6 +25,8 @@ export declare class OrgChart {
25
25
  constructor(selector: string | HTMLElement, opts: OrgChartOptions);
26
26
  setData(data: Record<string, any>[]): void;
27
27
  setTheme(theme: string): void;
28
+ /** R12b: FORM(스킨) 축 — data-og-skin 을 자기 컨테이너에 설정(setTheme 과 동형, 색과 직교). */
29
+ setSkin(skin: string): void;
28
30
  expandAll(): void;
29
31
  collapseAll(): void;
30
32
  private _toggle;
@@ -0,0 +1,65 @@
1
+ import { GridRenderer, DetailRenderContext } from './GridRenderer.js';
2
+ import { VirtualScroll } from './VirtualScroll.js';
3
+ import { Pagination } from './Pagination.js';
4
+ import { ColumnLayout } from './ColumnLayout.js';
5
+ import { DataLayer } from './DataLayer.js';
6
+ import { FlatRowModel } from './FlatRowModel.js';
7
+ import { MergeEngine } from './MergeEngine.js';
8
+ import { SortFilterManager } from './SortFilterManager.js';
9
+ import { RowManager } from './RowManager.js';
10
+ import { CellEditManager } from './CellEditManager.js';
11
+ import { GroupTreeManager } from './GroupTreeManager.js';
12
+ import { DetailManager } from './DetailManager.js';
13
+ import { RangeSelectionManager } from './RangeSelectionManager.js';
14
+ import { GridOptions } from './types.js';
15
+ /**
16
+ * R5(§3.1 C4, §6-R5): 렌더 루프의 유일 진입점. `OpenGrid` God object 에서
17
+ * `_onResize`/`_recalcWidths`/`_renderHeader`/`_syncHeaderLayout`/`_doRender`/`_visRange`
18
+ * (+ 전용 헬퍼 `_visCount`/`_paginationHeight`)를 **동작 불변**으로 옮긴 것.
19
+ *
20
+ * 스트랭글러 원칙(A2): 협력자(GridRenderer/VirtualScroll/Pagination 등)는 여전히
21
+ * `OpenGrid._mount` 가 생성·소유하며, 여기에는 `*Deps` 클로저 역전 패턴으로 **주입**만 된다.
22
+ * 값(컬럼폭·옵션 등)은 늦은-null / 재할당(worksheet 전환 시 colLayout 교체)을 견디도록
23
+ * 전부 getter 클로저로 읽는다. `fallbackViewportHeight` 클램프 시맨틱과 RenderFrame 구성은
24
+ * 원본과 1:1 동일하다(회귀 0).
25
+ */
26
+ export interface RenderControllerDeps<T extends Record<string, any> = any> {
27
+ getContainer: () => HTMLElement;
28
+ getOptions: () => Required<GridOptions<T>>;
29
+ getRenderer: () => GridRenderer | null;
30
+ getVs: () => VirtualScroll | null;
31
+ getPagination: () => Pagination | null;
32
+ getData: () => DataLayer<T>;
33
+ getColLayout: () => ColumnLayout<T>;
34
+ getFlatModel: () => FlatRowModel;
35
+ getMergeEngine: () => MergeEngine;
36
+ getColWidths: () => number[];
37
+ setColWidths: (widths: number[]) => void;
38
+ getUserWidths: () => Map<string, number>;
39
+ getSfMgr: () => SortFilterManager<T>;
40
+ getRowMgr: () => RowManager<T>;
41
+ getEditMgr: () => CellEditManager<T>;
42
+ getGrpMgr: () => GroupTreeManager<T>;
43
+ getDetailMgr: () => DetailManager<T>;
44
+ getRangeMgr: () => RangeSelectionManager<T>;
45
+ buildDetailRenderContext: () => DetailRenderContext | undefined;
46
+ renderFooterEl: () => void;
47
+ }
48
+ export declare class RenderController<T extends Record<string, any> = any> {
49
+ private _deps;
50
+ private _autoHeightWarned;
51
+ constructor(deps: RenderControllerDeps<T>);
52
+ private _paginationHeight;
53
+ onResize(): void;
54
+ recalcWidths(totalWidth: number): void;
55
+ renderHeader(): void;
56
+ /**
57
+ * 렌더된 헤더의 실제 높이를 측정해 본문(bodyWrap) 높이와 뷰포트 높이에 반영한다.
58
+ * 줄바꿈이 없는 헤더는 측정 높이가 headerHeight 이하라 기존 고정 동작과 동일하다.
59
+ * (public: OpenGrid 가 `_syncHeaderLayout` 위임으로 노출 — 특성화 테스트가 직접 호출한다.)
60
+ */
61
+ syncHeaderLayout(): void;
62
+ doRender(startIndex: number, endIndex: number): void;
63
+ visRange(): [number, number];
64
+ private _visCount;
65
+ }
@@ -0,0 +1,53 @@
1
+ import { SkinTokenDelta } from './types.js';
2
+ export interface SkinDefineResult {
3
+ /** 실제 등록된(가드레일 클램프가 반영된) 델타. */
4
+ readonly delta: SkinTokenDelta;
5
+ /** 접근성 가드레일이 조정한 토큰 경고(있으면 콘솔에도 출력). */
6
+ readonly warnings: string[];
7
+ }
8
+ /** 스킨 델타가 색 리터럴을 담고 있는지 검사하고, 위반 시 던진다(FORM-only, Rule 2). */
9
+ export declare function assertFormOnly(id: string, delta: SkinTokenDelta): void;
10
+ /**
11
+ * HANMS 접근성 가드레일(불변식) 적용 — 정의 시점 클램프(§6.4, HANMS P0-4).
12
+ * - focus-width < 2px → 2px 로 클램프(가시 포커스 비협상).
13
+ * - focus-style: none → solid.
14
+ * 반환은 조정된 델타 + 경고 목록(silent override 아님 — 무엇을 클램프했는지 알린다).
15
+ */
16
+ export declare function applyGuardrails(id: string, delta: SkinTokenDelta): SkinDefineResult;
17
+ /**
18
+ * 프로세스 전역 스킨 등록소. defineSkin(사용자) 는 검증+가드레일+`<style>` 주입,
19
+ * registerBuiltin(내장) 은 검증+가드레일만(CSS 는 skins.css 정적 번들 소유).
20
+ */
21
+ export declare class SkinRegistry {
22
+ private _skins;
23
+ private _styleEl;
24
+ /** 내장 스킨 등록(주입 없음 — CSS 는 skins.css 가 전달). 검증/가드레일은 동일 적용. */
25
+ registerBuiltin(id: string, delta: SkinTokenDelta): void;
26
+ /**
27
+ * 사용자 스킨 등록. FORM-only 검증 + 가드레일 클램프 후 런타임 `<style>` 로
28
+ * `.og-container[data-og-skin="id"]` 블록을 주입(브라우저 환경). 반환은 조정 결과.
29
+ */
30
+ define(id: string, delta: SkinTokenDelta): SkinDefineResult;
31
+ has(id: string): boolean;
32
+ get(id: string): SkinTokenDelta | undefined;
33
+ /** 등록된 모든 스킨 id(내장 + 사용자). */
34
+ list(): string[];
35
+ /** 런타임 `<style>` 주입(테스트/SSR 등 document 없으면 no-op). 같은 태그를 누적 사용. */
36
+ private _inject;
37
+ }
38
+ /** Sharp/Gothic — 엔터프라이즈 고밀도·각짐(§3.1). HANMS: APPROVE-WITH-GUARDRAIL(G-S1). */
39
+ export declare const SKIN_SHARP: SkinTokenDelta;
40
+ /** Rounded — 소비자 SaaS 소프트(§3.2). HANMS: APPROVE. */
41
+ export declare const SKIN_ROUNDED: SkinTokenDelta;
42
+ /** Stitch — 핸드크래프트(§3.3). HANMS: APPROVE-WITH-GUARDRAIL(G-ST1~3). 색(리넨/자수)은 theme 축. */
43
+ export declare const SKIN_STITCH: SkinTokenDelta;
44
+ /** Flat/Minimal — 플랫 2.0(§3.5). HANMS: APPROVE-WITH-GUARDRAIL(G-F1: 플로팅 표면 1px 보더). */
45
+ export declare const SKIN_FLAT: SkinTokenDelta;
46
+ /** High-Contrast — 접근성 우선·레퍼런스(§3.6). HANMS: APPROVE(안전 폴백). */
47
+ export declare const SKIN_HIGH_CONTRAST: SkinTokenDelta;
48
+ /** Material/Elevated — Neumorph 대체(HANMS §3): 중간 반경 + 정직한 그림자 엘리베이션. APPROVE. */
49
+ export declare const SKIN_MATERIAL: SkinTokenDelta;
50
+ /** 내장 스킨 카탈로그(HANMS 확정 6종, Neumorph 제외). id → 델타. */
51
+ export declare const BUILTIN_SKINS: ReadonlyArray<readonly [string, SkinTokenDelta]>;
52
+ /** 프로세스 전역 기본 레지스트리 — 내장 스킨을 부트스트랩 등록(주입 없음, CSS=skins.css). */
53
+ export declare const skinRegistry: SkinRegistry;
@@ -1,4 +1,4 @@
1
- import { ColumnDef } from '../types.js';
1
+ import { ColumnDef, EditorDef } from '../types.js';
2
2
  import { RenderContext } from '../renderers/CellRenderer.js';
3
3
  export { DateEditor } from './DateEditor.js';
4
4
  export { SelectEditor } from './SelectEditor.js';
@@ -51,4 +51,20 @@ export declare class CheckboxEditor implements CellEditor {
51
51
  focus(): void;
52
52
  destroy(): void;
53
53
  }
54
+ /**
55
+ * R10(§6-R10, §2.5 R-4c, §3.1 C12): `createEditor` 삼중 switch 를 `Map<typeName, factory>`
56
+ * 레지스트리로 대체한다. 내장 타입은 모듈 로드시 부트스트랩 등록, 미등록 타입은 기존과 동일하게
57
+ * TextEditor 로 폴백한다. `registerEditor(typeName, factory)` 로 코어 편집 없이 커스텀 에디터를
58
+ * 추가할 수 있다(OCP). 등록은 렌더러와 동일하게 **프로세스 전역**.
59
+ *
60
+ * 팩토리는 `(col, def)` 를 받는다. `def`(EditorDef 객체)는 객체 경로에서만 채워지며, 원 switch 의
61
+ * 세 컨텍스트(col.type / 문자열 / 객체) 동작을 정확히 보존한다:
62
+ * - number 는 def 가 있으면 min/max/step 을 옵션으로, 없으면 기본 NumberEditor.
63
+ * - select 는 def 가 있으면 def.options, 없으면 col.options 를 쓴다(원 switch 와 동일).
64
+ */
65
+ export type EditorFactory = (col: ColumnDef, def?: EditorDef) => CellEditor;
66
+ /** 커스텀 셀 에디터 타입을 코어 편집 없이 등록(OCP). 프로세스 전역. */
67
+ export declare function registerEditor(typeName: string, factory: EditorFactory): void;
68
+ /** 등록 여부 조회(내부/테스트용). */
69
+ export declare function hasEditor(typeName: string): boolean;
54
70
  export declare function createEditor(col: ColumnDef): CellEditor;
@@ -0,0 +1,9 @@
1
+ /** 모든 큐레이션 글리프 공통 viewBox(Bootstrap Icons 16px 그리드). */
2
+ export declare const ICON_VIEWBOX = "0 0 16 16";
3
+ /**
4
+ * symbolId → SVG 내부 마크업(<path>/<g>). Bootstrap Icons v1.13.1(MIT)에서 큐레이션.
5
+ * `eye-reveal` 는 예외(기존 마스킹 셀 커스텀 글리프, 행동 보존용).
6
+ */
7
+ export declare const BOOTSTRAP_ICONS: Readonly<Record<string, string>>;
8
+ /** 큐레이션에 포함된 symbol 개수(테스트/디버깅용). `eye-reveal`(커스텀 1) 포함. */
9
+ export declare const BOOTSTRAP_ICON_COUNT: number;
@@ -1,4 +1,4 @@
1
- import { ColumnDef } from '../types.js';
1
+ import { ColumnDef, RendererDef } from '../types.js';
2
2
  export interface RenderContext<T = any> {
3
3
  value: any;
4
4
  row: T;
@@ -8,13 +8,26 @@ export interface RenderContext<T = any> {
8
8
  isSelected: boolean;
9
9
  rowState: 'none' | 'added' | 'edited' | 'removed';
10
10
  displayValue?: string | null;
11
+ /** R1b: per-instance displayFormatter 전략(값·필드·행 → 표시문자열|null). Number/Date 렌더러가
12
+ * ctx.value 위에 적용한다(모듈전역 없이 멀티그리드 격리). null 반환/미설정 시 기본 포맷 폴백.
13
+ * ※ getDisplayValue override(text 경로의 ctx.displayValue)와 달리 **전략만** 적용 — 숫자/날짜의
14
+ * 기본 포맷(천단위·통화·정밀도)을 보존하고 그룹모드 인덱스 불일치를 피한다. */
15
+ displayFormatter?: ((value: any, field: string, row: any) => string | null) | null;
11
16
  /** C7(15_cross_contracts.md/F3-R14): 이 셀에 F3 셀 수식이 있으면 true — ColumnDef.formula 재평가를 skip. */
12
17
  hasCellFormula?: boolean;
13
18
  }
14
19
  export interface CellRenderer {
15
20
  render(ctx: RenderContext): HTMLElement;
16
21
  }
17
- export declare function setDisplayFormatterResolver(resolver: ((value: any, field: string, row: any) => string | null) | null): void;
22
+ /**
23
+ * R1(OOP 리팩터, DIP 수정): 과거엔 formatNumber/formatDate 가 host 참조가 없어 **모듈 전역**
24
+ * resolver 로 displayFormatter 슬롯에 도달했다. 그 전역은 매 OpenGrid 생성자가 덮어써,
25
+ * 한 페이지에 그리드가 여럿이면 마지막 그리드의 포매터가 전 그리드에 새는 P0 정합성 결함이었다.
26
+ * → 전역을 제거했다. 이제 displayFormatter 는 **per-instance 안전 경로 하나**로만 흐른다:
27
+ * OpenGrid.getDisplayValue(슬롯 읽기) → GridRenderer 가 RenderContext.displayValue 로 주입
28
+ * → 각 셀 렌더러(Text/Number/Date)가 ctx.displayValue 를 우선 사용.
29
+ * formatNumber/formatDate 는 전역 상태 0 의 순수 포맷 함수로 환원했다(출력 동일, 회귀 0).
30
+ */
18
31
  /**
19
32
  * 컬럼에 formula가 있으면 OGDecimal로 평가해 표시값(string)을 반환.
20
33
  * formula가 없으면 null 반환 → 기존 ctx.value 사용.
@@ -62,6 +75,10 @@ export declare class CheckboxRenderer implements CellRenderer {
62
75
  export interface ButtonRendererDef {
63
76
  type: 'button';
64
77
  label?: string | ((value: any, row: any) => string);
78
+ /** R12c: 라벨과 함께 표시할 아이콘 role/key(renderIcon). 미지정 시 라벨만(기존과 동일). */
79
+ icon?: string;
80
+ /** 아이콘 위치(기본 'left'). */
81
+ iconPos?: 'left' | 'right';
65
82
  buttonClass?: string;
66
83
  style?: string;
67
84
  }
@@ -149,4 +166,23 @@ export declare class HtmlRenderer implements CellRenderer {
149
166
  export declare class BarcodeRenderer implements CellRenderer {
150
167
  render(ctx: RenderContext): HTMLElement;
151
168
  }
169
+ /**
170
+ * R10(§6-R10, §2.5 R-4c, §3.1 C11): `createRenderer` 이중 switch 를 `Map<typeName, factory>`
171
+ * 레지스트리로 대체한다. 내장 타입은 모듈 로드시 부트스트랩 등록(아래 switch 와 동일 매핑),
172
+ * `createRenderer` 는 레지스트리로 해석하고 미등록 타입은 기존과 동일하게 TextRenderer 로 폴백한다.
173
+ * `registerRenderer(typeName, factory)` 로 코어 편집 없이 커스텀 렌더러를 추가할 수 있다(OCP).
174
+ *
175
+ * 등록은 **프로세스 전역**(내장은 불변에 가깝고, 커스텀 등록은 defaultOverride 와 동형의 전역 정책).
176
+ * 인스턴스별 스코핑은 R11/R12(ExtensionPointRegistry)의 후속 작업으로 남긴다.
177
+ *
178
+ * 팩토리는 `(col, def)` 를 받는다. `def` 는 RendererDef 객체 경로에서만 채워지며(문자열/col.type
179
+ * 경로에서는 undefined), 원 switch 의 세 컨텍스트(col.type / 문자열 / 객체) 동작을 정확히 보존한다:
180
+ * - image/progress/sparkline/rating/template 은 def(설정 객체) 없이는 원래 TextRenderer 로 떨어졌으므로
181
+ * def 유무로 게이트한다(문자열 렌더러명만 준 미완성 설정 = 기존과 동일 폴백).
182
+ */
183
+ export type RendererFactory = (col: ColumnDef, def?: RendererDef) => CellRenderer;
184
+ /** 커스텀 셀 렌더러 타입을 코어 편집 없이 등록(OCP). 프로세스 전역. */
185
+ export declare function registerRenderer(typeName: string, factory: RendererFactory): void;
186
+ /** 등록 여부 조회(내부/테스트용). */
187
+ export declare function hasRenderer(typeName: string): boolean;
152
188
  export declare function createRenderer(col: ColumnDef): CellRenderer;
@@ -473,6 +473,7 @@ export interface GridOptions<T = any> {
473
473
  tooltips?: boolean;
474
474
  ariaLabel?: string;
475
475
  theme?: string;
476
+ skin?: string;
476
477
  cssVars?: Record<string, string>;
477
478
  contextMenu?: boolean | ContextMenuItem[];
478
479
  worksheets?: WorksheetDef[];
@@ -541,8 +542,15 @@ export interface OverrideCallOptions {
541
542
  /** 'fallback' → 레이어 예외 시 경고 후 원본 실행(멱등 가정, 롤백 불가). 미지정 시 strict. */
542
543
  onError?: 'fallback';
543
544
  }
544
- /** 등록 가능한 알고리즘 슬롯 이름(6종). */
545
- export type StrategySlot = 'sortComparator' | 'filterPredicate' | 'displayFormatter' | 'cellSerializer' | 'groupKeyFn' | 'summaryOp';
545
+ /** 등록 가능한 알고리즘 슬롯 이름. */
546
+ export type StrategySlot = 'sortComparator' | 'filterPredicate' | 'displayFormatter' | 'cellSerializer' | 'groupKeyFn' | 'summaryOp' | 'cellClassResolver' | 'ariaLabelResolver' | 'skinResolver';
547
+ /**
548
+ * SKIN 토큰 이름 집합 — **형태(FORM)만** 소유(색 0). COLOR 토큰과 disjoint name set 이라
549
+ * 색⊥형태 직교성이 이름 충돌 부재로 물리적으로 보장된다(item3 §1.1~1.2, HANMS §4).
550
+ */
551
+ export type SkinTokenName = '--og-radius-none' | '--og-radius-sm' | '--og-radius-md' | '--og-radius-lg' | '--og-radius-pill' | '--og-radius-container' | '--og-radius-control' | '--og-radius-widget' | '--og-container-radius' | '--og-border-width' | '--og-border-width-strong' | '--og-border-style' | '--og-divider-style' | '--og-divider-repeat' | '--og-elevation-sm' | '--og-elevation-md' | '--og-elevation-lg' | '--og-elevation-alpha-sm' | '--og-elevation-alpha-md' | '--og-elevation-alpha-lg' | '--og-elevation-inset' | '--og-cell-padding-x' | '--og-cell-padding-y' | '--og-density-row-height' | '--og-density-header-height' | '--og-density-footer-height' | '--og-scrollbar-size' | '--og-texture-bg' | '--og-texture-size' | '--og-texture-opacity' | '--og-focus-width' | '--og-focus-style' | '--og-focus-offset' | '--og-focus-radius' | '--og-icon-size' | '--og-icon-fill' | '--og-icon-stroke-width' | '--og-icon-corner' | '--og-transition-fast' | '--og-transition-base' | '--og-row-accent-width';
552
+ /** FORM-only 스킨 델타. 값에 색 리터럴이 있으면 SkinRegistry 가 런타임 거부(Rule 2, 직교성). */
553
+ export type SkinTokenDelta = Partial<Record<SkinTokenName, string>>;
546
554
  /** 단일키 정렬 비교자. dir 부호는 호출자(DataLayer)가 적용 — 슬롯은 비교만 반환. */
547
555
  export type SortComparatorFn = (a: any, b: any, field: string, dir: 'asc' | 'desc') => number;
548
556
  /** 필터 술어. true → 행 포함. */
@@ -555,6 +563,10 @@ export type CellSerializerFn = (value: any, col: any, row: any) => any;
555
563
  export type GroupKeyFn = (row: any, remainingFields: string[]) => any;
556
564
  /** 집계 연산. null 반환 시 기본 SUM/AVG/COUNT/MAX/MIN 분기로 폴백. */
557
565
  export type SummaryOpFn = (op: string, nums: any[], field: string) => number | null;
566
+ /** R11(§4.2): 셀 클래스 렌더훅 리졸버. 렌더층이 셀 element 에 추가할 className(null=미참여). */
567
+ export type CellClassResolverFn = (value: any, field: string, row: any) => string | null;
568
+ /** R11(§4.2): 셀 aria-label 렌더훅 리졸버. 렌더층이 셀 aria-label 을 대체(null=기본 유지). */
569
+ export type AriaLabelResolverFn = (value: any, field: string, row: any) => string | null;
558
570
  /**
559
571
  * F1 채우기 시리즈 커스텀 리졸버 슬롯(C5.3, 예약). 사용자가 날짜/커스텀 시리즈를 주입할 수 있다.
560
572
  * ⚠️ 슬롯 등록 경로만 확보되어 있으며, RangeSelectionManager/FillEngine 소비 배선은 F1-b(Full) 대상.
@@ -569,10 +581,32 @@ export interface StrategyMap {
569
581
  groupKeyFn: GroupKeyFn;
570
582
  summaryOp: SummaryOpFn;
571
583
  fillSeriesResolver: FillSeriesResolverFn;
584
+ cellClassResolver: CellClassResolverFn;
585
+ ariaLabelResolver: AriaLabelResolverFn;
586
+ skinResolver: SkinResolverFn;
587
+ }
588
+ /**
589
+ * R12b(item3 §6.1): 스킨 FORM 해석 인터셉트 슬롯. AppearanceResolver 가 fallback 과 함께 읽어(제로코스트)
590
+ * 오버라이드가 form 토큰 델타를 통째로 가로챌 수 있다. 미설정 시 내장 스킨 카탈로그가 그대로 적용.
591
+ */
592
+ export type SkinResolverFn = (skinId: string) => SkinTokenDelta | null;
593
+ /**
594
+ * R11(§4.3, T-ζ): SemVer 보증되는 **타입드 override 카탈로그**(좁은 "지원됨" 문).
595
+ * `override("anyMethod", fn)` 문자열 탈출구(UC-11, best-effort)는 그대로 열려 있고 —
596
+ * 이 인터페이스는 축복된(blessed) 확장점 이름을 IDE 발견가능하게 좁게 표시할 뿐, 넓은 문을 닫지 않는다.
597
+ * 각 항목은 소비 테스트를 동반한다(유령 확장점 금지, DeMarco M9b).
598
+ */
599
+ export interface OverridePoints<T = any> {
600
+ /** 셀 표시 텍스트 해석(렌더훅 displayText 의 근원). */
601
+ getDisplayValue(rowIndex: number, field: string): string;
602
+ /** 원시 셀 값 접근. */
603
+ readCell(rowIndex: number, field: string): any;
572
604
  }
573
605
  /** 호출가능 + .strategy 멤버를 가진 하이브리드 override API. */
574
606
  export interface OverrideApi<T = any> {
575
- /** 메서드 본문 무수정 런타임 래핑(C1-clean). 체이닝 위해 grid 인스턴스 반환. */
607
+ /** R11(§4.3): 타입드 오버로드 축복된 확장점 이름(IDE 발견). SemVer 보증 카탈로그. */
608
+ <K extends keyof OverridePoints<T>>(name: K, fn: OverrideLayerFn, opts?: OverrideCallOptions): OpenGridInstance<T>;
609
+ /** 메서드 본문 무수정 런타임 래핑(C1-clean, 탈출구 UC-11). 체이닝 위해 grid 인스턴스 반환. */
576
610
  (name: string, fn: OverrideLayerFn, opts?: OverrideCallOptions): OpenGridInstance<T>;
577
611
  /** 알고리즘 슬롯 등록(Phase 2 매니저 훅포인트). 체이닝 위해 grid 인스턴스 반환. */
578
612
  strategy<K extends StrategySlot>(slot: K, fn: StrategyMap[K]): OpenGridInstance<T>;
@@ -767,6 +801,12 @@ export interface OpenGridInstance<T = any> {
767
801
  resize(width?: number, height?: number): void;
768
802
  setTheme(theme: string): void;
769
803
  setThemeVar(varName: string, value: string): void;
804
+ /** R12b: 스킨(FORM 축) 전환 — data-og-skin 설정 + 인라인 form 사이트 재해석. 색 테마와 직교. */
805
+ setSkin(skin: string): void;
806
+ /** R12b: 현재 스킨 id('default' = 오늘). */
807
+ getSkin(): string;
808
+ /** R12b: FORM 축 단일 토큰 런타임 오버라이드(setThemeVar 의 형태-축 형제). 색 값은 거부. */
809
+ setSkinVar(varName: string, value: string): void;
770
810
  destroy(): void;
771
811
  setOptions(opts: Partial<GridOptions<T>>): void;
772
812
  /** 컬럼 마스킹 ON/OFF. enabled=true → 마스킹 적용, enabled=false → 전체 해제 */
@@ -3,6 +3,10 @@ export { GridShuttle, createGridShuttle } from './core/GridShuttle.js';
3
3
  export type { GridShuttleOptions } from './core/GridShuttle.js';
4
4
  export { OrgChart } from './core/OrgChart.js';
5
5
  export { XmlConverter } from './core/XmlConverter.js';
6
+ export { IconRegistry, iconRegistry, renderIcon, DEFAULT_ICON_ROLES } from './core/IconRegistry.js';
7
+ export type { IconRenderOptions } from './core/IconRegistry.js';
8
+ export { SkinRegistry, skinRegistry } from './core/SkinRegistry.js';
9
+ export type { SkinTokenDelta } from './core/types.js';
6
10
  export type { OrgChartOptions, OrgChartColumnDef } from './core/OrgChart.js';
7
11
  export type { TreeNodeIconDef } from './core/types.js';
8
12
  export type { FilterSelectConfig, FilterSelectColumn } from './core/FilterSelect.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-grid",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Ultra-light data grid with a zero-dependency core — works with React, Vue, Angular, jQuery, or vanilla JS. MIT-licensed, endlessly customizable, with an AI-friendly override guide.",
5
5
  "type": "module",
6
6
  "main": "dist/open-grid.cjs",