open-grid 0.4.0 → 1.0.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 (48) hide show
  1. package/dist/OpenGrid-C6SK6VeK.cjs +90 -0
  2. package/dist/OpenGrid-C6SK6VeK.cjs.map +1 -0
  3. package/dist/OpenGrid-D7EJOVi1.js +4864 -0
  4. package/dist/OpenGrid-D7EJOVi1.js.map +1 -0
  5. package/dist/open-grid-react.cjs +1 -1
  6. package/dist/open-grid-react.js +1 -1
  7. package/dist/open-grid-vue.cjs +1 -1
  8. package/dist/open-grid-vue.js +1 -1
  9. package/dist/open-grid.cjs +1 -1
  10. package/dist/open-grid.js +2 -2
  11. package/dist/types/core/CellEditManager.d.ts +2 -3
  12. package/dist/types/core/CellEventHandler.d.ts +4 -5
  13. package/dist/types/core/CellTypeRegistry.d.ts +1 -2
  14. package/dist/types/core/ColumnLayout.d.ts +1 -2
  15. package/dist/types/core/ContextMenu.d.ts +1 -2
  16. package/dist/types/core/CrossGridRegistry.d.ts +1 -2
  17. package/dist/types/core/DataLayer.d.ts +4 -2
  18. package/dist/types/core/ExportManager.d.ts +4 -3
  19. package/dist/types/core/FilterPanel.d.ts +1 -2
  20. package/dist/types/core/FilterSelect.d.ts +1 -2
  21. package/dist/types/core/FindBarManager.d.ts +4 -5
  22. package/dist/types/core/FooterManager.d.ts +2 -1
  23. package/dist/types/core/FormulaEngine.d.ts +1 -2
  24. package/dist/types/core/GridRenderer.d.ts +3 -2
  25. package/dist/types/core/GridShuttle.d.ts +1 -2
  26. package/dist/types/core/GroupEngine.d.ts +7 -4
  27. package/dist/types/core/GroupTreeManager.d.ts +6 -3
  28. package/dist/types/core/KeyboardManager.d.ts +4 -5
  29. package/dist/types/core/OpenGrid.d.ts +27 -4
  30. package/dist/types/core/OverrideKernel.d.ts +70 -0
  31. package/dist/types/core/PivotEngine.d.ts +1 -2
  32. package/dist/types/core/RowManager.d.ts +0 -1
  33. package/dist/types/core/SortFilterManager.d.ts +5 -6
  34. package/dist/types/core/TriggerManager.d.ts +1 -2
  35. package/dist/types/core/WorksheetManager.d.ts +1 -2
  36. package/dist/types/core/editors/CellEditor.d.ts +2 -3
  37. package/dist/types/core/editors/DateEditor.d.ts +2 -3
  38. package/dist/types/core/editors/SelectEditor.d.ts +2 -3
  39. package/dist/types/core/renderers/CellRenderer.d.ts +4 -4
  40. package/dist/types/core/types.d.ts +58 -0
  41. package/dist/types/index.d.ts +1 -1
  42. package/dist/types/react/OpenGrid.d.ts +3 -3
  43. package/dist/types/vue/types.d.ts +1 -2
  44. package/package.json +99 -99
  45. package/dist/OpenGrid-ClmeIJYR.cjs +0 -88
  46. package/dist/OpenGrid-ClmeIJYR.cjs.map +0 -1
  47. package/dist/OpenGrid-doS5aFVl.js +0 -4669
  48. package/dist/OpenGrid-doS5aFVl.js.map +0 -1
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const g=require("react/jsx-runtime"),r=require("react"),D=require("./OpenGrid-ClmeIJYR.cjs");function M({data:e,columns:t,height:u=400,width:s="100%",editable:l=!1,sortable:O=!0,filterable:o=!0,rowNumber:x=!1,checkColumn:y=!1,stateColumn:R=!1,draggable:G=!1,frozenColumns:j=0,theme:f="default",options:P,style:W,className:z,onReady:c,onDataChange:b,onCellClick:q,onRowClick:S,onEditEnd:d,onSortChange:v,onFilterChange:E,onRowDrop:T}){const i=r.useRef(null),n=r.useRef(null),A={height:typeof u=="number"?`${u}px`:u,width:typeof s=="number"?`${s}px`:s,display:"block",boxSizing:"border-box",...W};return r.useEffect(()=>{if(!i.current)return;const B={columns:t,height:"100%",width:"100%",editable:l,sortable:O,filterable:o,rowNumber:x,checkColumn:y,stateColumn:R,draggable:G,frozenColumns:j,theme:f,...P,onReady:p=>{n.current=p,e!=null&&e.length&&p.setData(e),c==null||c(p)},...b&&{onDataChange:b},...q&&{onCellClick:q},...S&&{onRowClick:S},...d&&{onEditEnd:d},...v&&{onSortChange:v},...E&&{onFilterChange:E},...T&&{onRowDrop:T}},$=new D.OpenGrid(i.current,B);return n.current=$,()=>{$.destroy(),n.current=null}},[t,l,O,o,x,y,R,G,j]),r.useEffect(()=>{n.current&&e&&n.current.setData(e)},[e]),r.useEffect(()=>{n.current&&n.current.setTheme(f)},[f]),g.jsx("div",{ref:i,style:A,className:z})}const H=r.forwardRef((e,t)=>{r.useRef(null);const u=r.useRef(null);return r.useEffect(()=>{u.current&&t&&(typeof t=="function"?t(u.current):t.current=u.current)}),g.jsx(M,{...e})});H.displayName="OpenGrid";exports.OpenGrid=M;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const g=require("react/jsx-runtime"),r=require("react"),D=require("./OpenGrid-C6SK6VeK.cjs");function M({data:e,columns:t,height:u=400,width:s="100%",editable:l=!1,sortable:O=!0,filterable:o=!0,rowNumber:x=!1,checkColumn:y=!1,stateColumn:R=!1,draggable:G=!1,frozenColumns:j=0,theme:f="default",options:P,style:W,className:z,onReady:c,onDataChange:b,onCellClick:q,onRowClick:S,onEditEnd:d,onSortChange:v,onFilterChange:E,onRowDrop:T}){const i=r.useRef(null),n=r.useRef(null),A={height:typeof u=="number"?`${u}px`:u,width:typeof s=="number"?`${s}px`:s,display:"block",boxSizing:"border-box",...W};return r.useEffect(()=>{if(!i.current)return;const B={columns:t,height:"100%",width:"100%",editable:l,sortable:O,filterable:o,rowNumber:x,checkColumn:y,stateColumn:R,draggable:G,frozenColumns:j,theme:f,...P,onReady:p=>{n.current=p,e!=null&&e.length&&p.setData(e),c==null||c(p)},...b&&{onDataChange:b},...q&&{onCellClick:q},...S&&{onRowClick:S},...d&&{onEditEnd:d},...v&&{onSortChange:v},...E&&{onFilterChange:E},...T&&{onRowDrop:T}},$=new D.OpenGrid(i.current,B);return n.current=$,()=>{$.destroy(),n.current=null}},[t,l,O,o,x,y,R,G,j]),r.useEffect(()=>{n.current&&e&&n.current.setData(e)},[e]),r.useEffect(()=>{n.current&&n.current.setTheme(f)},[f]),g.jsx("div",{ref:i,style:A,className:z})}const H=r.forwardRef((e,t)=>{r.useRef(null);const u=r.useRef(null);return r.useEffect(()=>{u.current&&t&&(typeof t=="function"?t(u.current):t.current=u.current)}),g.jsx(M,{...e})});H.displayName="OpenGrid";exports.OpenGrid=M;
2
2
  //# sourceMappingURL=open-grid-react.cjs.map
@@ -1,6 +1,6 @@
1
1
  import { jsx as A } from "react/jsx-runtime";
2
2
  import K, { useRef as f, useEffect as u } from "react";
3
- import { O as L } from "./OpenGrid-doS5aFVl.js";
3
+ import { O as L } from "./OpenGrid-D7EJOVi1.js";
4
4
  function M({
5
5
  data: r,
6
6
  columns: e,
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("vue"),p=require("./OpenGrid-ClmeIJYR.cjs"),h=a.defineComponent({__name:"OpenGrid",props:{data:{default:()=>[]},columns:{},height:{default:400},width:{default:"100%"},editable:{type:Boolean,default:!1},sortable:{type:Boolean,default:!0},filterable:{type:Boolean,default:!0},rowNumber:{type:Boolean,default:!1},checkColumn:{type:Boolean,default:!1},stateColumn:{type:Boolean,default:!1},draggable:{type:Boolean,default:!1},frozenColumns:{default:0},theme:{default:"default"},options:{}},emits:["update:data","ready","cell-click","row-click","edit-end","sort-change","filter-change","row-check"],setup(r,{expose:c,emit:u}){const e=r,n=u,d=a.ref(),o=a.shallowRef(null);let i=null;const f=a.computed(()=>({height:typeof e.height=="number"?`${e.height}px`:e.height,width:typeof e.width=="number"?`${e.width}px`:e.width}));return a.onMounted(()=>{if(!d.value)return;const l={columns:e.columns,height:"100%",width:"100%",editable:e.editable,sortable:e.sortable,filterable:e.filterable,rowNumber:e.rowNumber,checkColumn:e.checkColumn,stateColumn:e.stateColumn,draggable:e.draggable,frozenColumns:e.frozenColumns,theme:e.theme,...e.options,onReady:t=>{var s;(s=e.data)!=null&&s.length&&t.setData(e.data),n("ready",t)},onCellClick:t=>n("cell-click",t),onRowClick:t=>n("row-click",t),onEditEnd:t=>n("edit-end",t),onSortChange:t=>n("sort-change",t),onFilterChange:t=>n("filter-change",t),onDataChange:t=>{i=t,n("update:data",t)}};o.value=new p.OpenGrid(d.value,l)}),a.watch(()=>e.data,l=>{!o.value||!l||l!==i&&o.value.setData(l)},{deep:!1}),a.watch(()=>e.theme,l=>{o.value&&l&&o.value.setTheme(l)}),a.watch(()=>e.columns,l=>{o.value&&o.value.applyColumns(l)},{deep:!1}),a.onUnmounted(()=>{var l;(l=o.value)==null||l.destroy(),o.value=null}),c({grid:o}),(l,t)=>(a.openBlock(),a.createElementBlock("div",{ref_key:"containerRef",ref:d,class:"og-vue-wrapper",style:a.normalizeStyle(f.value)},null,4))}}),m=(r,c)=>{const u=r.__vccOpts||r;for(const[e,n]of c)u[e]=n;return u},g=m(h,[["__scopeId","data-v-0a49d4fc"]]);exports.OpenGrid=g;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("vue"),p=require("./OpenGrid-C6SK6VeK.cjs"),h=a.defineComponent({__name:"OpenGrid",props:{data:{default:()=>[]},columns:{},height:{default:400},width:{default:"100%"},editable:{type:Boolean,default:!1},sortable:{type:Boolean,default:!0},filterable:{type:Boolean,default:!0},rowNumber:{type:Boolean,default:!1},checkColumn:{type:Boolean,default:!1},stateColumn:{type:Boolean,default:!1},draggable:{type:Boolean,default:!1},frozenColumns:{default:0},theme:{default:"default"},options:{}},emits:["update:data","ready","cell-click","row-click","edit-end","sort-change","filter-change","row-check"],setup(r,{expose:c,emit:u}){const e=r,n=u,d=a.ref(),o=a.shallowRef(null);let i=null;const f=a.computed(()=>({height:typeof e.height=="number"?`${e.height}px`:e.height,width:typeof e.width=="number"?`${e.width}px`:e.width}));return a.onMounted(()=>{if(!d.value)return;const l={columns:e.columns,height:"100%",width:"100%",editable:e.editable,sortable:e.sortable,filterable:e.filterable,rowNumber:e.rowNumber,checkColumn:e.checkColumn,stateColumn:e.stateColumn,draggable:e.draggable,frozenColumns:e.frozenColumns,theme:e.theme,...e.options,onReady:t=>{var s;(s=e.data)!=null&&s.length&&t.setData(e.data),n("ready",t)},onCellClick:t=>n("cell-click",t),onRowClick:t=>n("row-click",t),onEditEnd:t=>n("edit-end",t),onSortChange:t=>n("sort-change",t),onFilterChange:t=>n("filter-change",t),onDataChange:t=>{i=t,n("update:data",t)}};o.value=new p.OpenGrid(d.value,l)}),a.watch(()=>e.data,l=>{!o.value||!l||l!==i&&o.value.setData(l)},{deep:!1}),a.watch(()=>e.theme,l=>{o.value&&l&&o.value.setTheme(l)}),a.watch(()=>e.columns,l=>{o.value&&o.value.applyColumns(l)},{deep:!1}),a.onUnmounted(()=>{var l;(l=o.value)==null||l.destroy(),o.value=null}),c({grid:o}),(l,t)=>(a.openBlock(),a.createElementBlock("div",{ref_key:"containerRef",ref:d,class:"og-vue-wrapper",style:a.normalizeStyle(f.value)},null,4))}}),m=(r,c)=>{const u=r.__vccOpts||r;for(const[e,n]of c)u[e]=n;return u},g=m(h,[["__scopeId","data-v-0a49d4fc"]]);exports.OpenGrid=g;
2
2
  //# sourceMappingURL=open-grid-vue.cjs.map
@@ -1,5 +1,5 @@
1
1
  import { defineComponent as p, ref as m, shallowRef as h, computed as g, onMounted as y, watch as d, onUnmounted as b, openBlock as v, createElementBlock as C, normalizeStyle as k } from "vue";
2
- import { O as w } from "./OpenGrid-doS5aFVl.js";
2
+ import { O as w } from "./OpenGrid-D7EJOVi1.js";
3
3
  const _ = /* @__PURE__ */ p({
4
4
  __name: "OpenGrid",
5
5
  props: {
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const A=require("./OpenGrid-ClmeIJYR.cjs");class C{constructor(t,e,o,i={}){this._left=t,this._right=e;const r=document.createElement("div");r.className="og-shuttle",r.style.cssText=`display:flex;gap:6px;align-items:center;justify-content:center;flex-direction:${i.layout==="horizontal"?"row":"column"};`;const c=(m,f,u)=>{const a=document.createElement("button");return a.type="button",a.className="og-shuttle-btn",a.textContent=m,a.title=f,a.style.cssText="min-width:34px;height:30px;padding:0 8px;border:1px solid #bbb;border-radius:7px;background:#fff;cursor:pointer;font-size:14px;color:#444;line-height:1;box-shadow:0 1px 2px rgba(0,0,0,0.06);",a.addEventListener("mouseover",()=>{a.style.background="#f0f6ff",a.style.borderColor="#1976d2"}),a.addEventListener("mouseout",()=>{a.style.background="#fff",a.style.borderColor="#bbb"}),a.addEventListener("click",u),a},_=i.labels??{};r.appendChild(c(_.toRight??"▶","체크한 행을 오른쪽 그리드로 이동",()=>{this._left.moveCheckedTo(this._right)})),r.appendChild(c(_.toLeft??"◀","체크한 행을 왼쪽 그리드로 이동",()=>{this._right.moveCheckedTo(this._left)})),i.includeAll&&(r.appendChild(c(_.allRight??"⏩","왼쪽 전체를 오른쪽으로 이동",()=>{this._moveAll(this._left,this._right)})),r.appendChild(c(_.allLeft??"⏪","오른쪽 전체를 왼쪽으로 이동",()=>{this._moveAll(this._right,this._left)}))),o.appendChild(r),this._el=r}_moveAll(t,e){const o=t.getData().length;o>0&&t.moveRowsTo(e,Array.from({length:o},(i,r)=>r))}destroy(){this._el.remove()}}function T(b,t,e,o){return new C(b,t,e,o)}class v{constructor(t,e){this._data=[],this._roots=[],this._expandedKeys=new Set,this._selectedId=null,this._container=typeof t=="string"?document.querySelector(t):t,this._opts={nodeWidth:160,nodeHeight:72,levelGap:52,siblingGap:20,expandOnLoad:!0,onNodeClick:()=>{},...e},this._container.classList.add("og-orgchart")}setData(t){this._data=t;const{idField:e,parentIdField:o,expandOnLoad:i}=this._opts;i&&this._expandedKeys.size===0&&t.forEach(r=>this._expandedKeys.add(r[e])),this._roots=A.buildTree(t,{idField:e,parentIdField:o},this._expandedKeys),this._render()}setTheme(t){this._container.setAttribute("data-og-theme",t)}expandAll(){const t=e=>{for(const o of e)this._expandedKeys.add(o._treeId),o.children.length&&t(o.children)};t(this._roots),this._rebuild()}collapseAll(){this._expandedKeys.clear(),this._rebuild()}_toggle(t){this._expandedKeys.has(t)?this._expandedKeys.delete(t):this._expandedKeys.add(t),this._rebuild()}_rebuild(){const{idField:t,parentIdField:e}=this._opts;this._roots=A.buildTree(this._data,{idField:t,parentIdField:e},this._expandedKeys),this._render()}_calcLayout(){const{nodeWidth:t,nodeHeight:e,levelGap:o,siblingGap:i}=this._opts,r=new Map;let c=0;const _=u=>{const a=u._depth*(e+o),p=u._expanded?u.children:[];if(!p.length){const g=c;return c+=t+i,r.set(u._treeId,{x:g,y:a}),{minX:g,maxX:g}}let n=1/0,l=-1/0;for(const g of p){const{minX:s,maxX:h}=_(g);s<n&&(n=s),h>l&&(l=h)}const d=n+(l-n+t)/2-t/2;return r.set(u._treeId,{x:d,y:a}),{minX:n,maxX:l}};for(const u of this._roots)_(u);let m=0,f=0;for(const{x:u,y:a}of r.values())u+t>m&&(m=u+t),a+e>f&&(f=a+e);return{layout:r,totalW:m+i,totalH:f+o+16}}_line(t,e,o,i,r){const c=document.createElementNS("http://www.w3.org/2000/svg","line");c.setAttribute("x1",String(e)),c.setAttribute("y1",String(o)),c.setAttribute("x2",String(i)),c.setAttribute("y2",String(r)),c.setAttribute("class","og-orgchart-line"),t.appendChild(c)}_render(){const{nodeWidth:t,nodeHeight:e,levelGap:o,columns:i}=this._opts,{layout:r,totalW:c,totalH:_}=this._calcLayout();this._container.innerHTML="";const m=document.createElement("div");m.className="og-orgchart-wrap",m.style.cssText=`width:${c}px;height:${_}px;`;const f=document.createElementNS("http://www.w3.org/2000/svg","svg");f.setAttribute("width",String(c)),f.setAttribute("height",String(_)),f.style.cssText="position:absolute;top:0;left:0;pointer-events:none;overflow:visible;";const u=p=>{for(const n of p){if(!n._expanded||!n.children.length)continue;const l=r.get(n._treeId),d=l.x+t/2,g=l.y+e,s=g+o/2,h=n.children;if(this._line(f,d,g,d,s),h.length>1){const x=r.get(h[0]._treeId),y=r.get(h[h.length-1]._treeId);this._line(f,x.x+t/2,s,y.x+t/2,s)}for(const x of h){const y=r.get(x._treeId),E=y.x+t/2;this._line(f,E,s,E,y.y)}u(h)}};u(this._roots),m.appendChild(f);const a=p=>{for(const n of p){const l=r.get(n._treeId);if(!l)continue;const d=document.createElement("div");d.className="og-orgchart-node",n._hasChildren&&d.classList.add("og-orgchart-node--branch"),n._expanded&&d.classList.add("og-orgchart-node--expanded"),this._selectedId===n._treeId&&d.classList.add("og-orgchart-node--selected"),d.style.cssText=`left:${l.x}px;top:${l.y}px;width:${t}px;height:${e}px;`;const g=document.createElement("div");g.className="og-orgchart-node-content";for(const s of i){const h=n.data[s.field],x=document.createElement("div");if(x.className="og-orgchart-col"+(s.className?" "+s.className:""),s.style){const y=typeof s.style=="function"?s.style(h,n.data):s.style;x.setAttribute("style",y)}if(s.renderer){const y=s.renderer(h,n.data);typeof y=="string"?x.innerHTML=y:x.appendChild(y)}else x.textContent=h??"";g.appendChild(x)}if(d.appendChild(g),n._hasChildren){const s=document.createElement("button");s.type="button",s.className="og-orgchart-toggle",s.setAttribute("aria-expanded",n._expanded?"true":"false"),s.setAttribute("aria-label",n._expanded?"접기":"펼치기");const h=document.createElement("i");h.setAttribute("aria-hidden","true"),h.className=n._expanded?"bi bi-dash-circle":"bi bi-plus-circle",s.appendChild(h),s.addEventListener("click",x=>{x.stopPropagation(),this._toggle(n._treeId)}),d.appendChild(s)}d.addEventListener("click",()=>{this._selectedId=n._treeId,this._opts.onNodeClick(n._treeId,n.data),this._container.querySelectorAll(".og-orgchart-node--selected").forEach(s=>s.classList.remove("og-orgchart-node--selected")),d.classList.add("og-orgchart-node--selected")}),m.appendChild(d),n._expanded&&n.children.length&&a(n.children)}};a(this._roots),this._container.appendChild(m)}}class N{static parse(t,e={}){var p,n;const{fieldMap:o={},trim:i=!0}=e,c=new DOMParser().parseFromString(t.trim(),"text/xml"),_=c.querySelector("parsererror");if(_)throw new Error(`XML 파싱 오류: ${(p=_.textContent)==null?void 0:p.trim()}`);const m=c.documentElement;let f=e.rowTag;if(!f){const l=e.rootTag?c.querySelector(e.rootTag):m;f=((n=l==null?void 0:l.children[0])==null?void 0:n.tagName)??"row"}const u=c.getElementsByTagName(f),a=[];for(let l=0;l<u.length;l++){const d=u[l],g={};for(const s of Array.from(d.attributes)){const h=o[s.name]??s.name;g[h]=i?s.value.trim():s.value}for(const s of Array.from(d.children)){const h=o[s.tagName]??s.tagName,x=s.textContent??"";g[h]=i?x.trim():x}a.push(g)}return a}static stringify(t,e={}){const{rootTag:o="rows",rowTag:i="row",mode:r="element",fieldMap:c={},declaration:_=!0,indent:m=2,nullAs:f="",excludeFields:u=[]}=e,a=" ".repeat(m),p=[];_&&p.push('<?xml version="1.0" encoding="UTF-8"?>'),p.push(`<${o}>`);for(const n of t){const l=Object.entries(n).filter(([d])=>!u.includes(d));if(r==="attribute"){const d=l.map(([g,s])=>{const h=c[g]??g,x=s==null?f:String(s);return`${h}="${this._escAttr(x)}"`}).join(" ");p.push(`${a}<${i}${d?" "+d:""} />`)}else{p.push(`${a}<${i}>`);for(const[d,g]of l){const s=c[d]??d,h=g==null?f:String(g);p.push(`${a}${a}<${s}>${this._escText(h)}</${s}>`)}p.push(`${a}</${i}>`)}}return p.push(`</${o}>`),p.join(`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const A=require("./OpenGrid-C6SK6VeK.cjs");class C{constructor(t,e,o,i={}){this._left=t,this._right=e;const r=document.createElement("div");r.className="og-shuttle",r.style.cssText=`display:flex;gap:6px;align-items:center;justify-content:center;flex-direction:${i.layout==="horizontal"?"row":"column"};`;const c=(m,f,u)=>{const a=document.createElement("button");return a.type="button",a.className="og-shuttle-btn",a.textContent=m,a.title=f,a.style.cssText="min-width:34px;height:30px;padding:0 8px;border:1px solid #bbb;border-radius:7px;background:#fff;cursor:pointer;font-size:14px;color:#444;line-height:1;box-shadow:0 1px 2px rgba(0,0,0,0.06);",a.addEventListener("mouseover",()=>{a.style.background="#f0f6ff",a.style.borderColor="#1976d2"}),a.addEventListener("mouseout",()=>{a.style.background="#fff",a.style.borderColor="#bbb"}),a.addEventListener("click",u),a},_=i.labels??{};r.appendChild(c(_.toRight??"▶","체크한 행을 오른쪽 그리드로 이동",()=>{this._left.moveCheckedTo(this._right)})),r.appendChild(c(_.toLeft??"◀","체크한 행을 왼쪽 그리드로 이동",()=>{this._right.moveCheckedTo(this._left)})),i.includeAll&&(r.appendChild(c(_.allRight??"⏩","왼쪽 전체를 오른쪽으로 이동",()=>{this._moveAll(this._left,this._right)})),r.appendChild(c(_.allLeft??"⏪","오른쪽 전체를 왼쪽으로 이동",()=>{this._moveAll(this._right,this._left)}))),o.appendChild(r),this._el=r}_moveAll(t,e){const o=t.getData().length;o>0&&t.moveRowsTo(e,Array.from({length:o},(i,r)=>r))}destroy(){this._el.remove()}}function T(b,t,e,o){return new C(b,t,e,o)}class v{constructor(t,e){this._data=[],this._roots=[],this._expandedKeys=new Set,this._selectedId=null,this._container=typeof t=="string"?document.querySelector(t):t,this._opts={nodeWidth:160,nodeHeight:72,levelGap:52,siblingGap:20,expandOnLoad:!0,onNodeClick:()=>{},...e},this._container.classList.add("og-orgchart")}setData(t){this._data=t;const{idField:e,parentIdField:o,expandOnLoad:i}=this._opts;i&&this._expandedKeys.size===0&&t.forEach(r=>this._expandedKeys.add(r[e])),this._roots=A.buildTree(t,{idField:e,parentIdField:o},this._expandedKeys),this._render()}setTheme(t){this._container.setAttribute("data-og-theme",t)}expandAll(){const t=e=>{for(const o of e)this._expandedKeys.add(o._treeId),o.children.length&&t(o.children)};t(this._roots),this._rebuild()}collapseAll(){this._expandedKeys.clear(),this._rebuild()}_toggle(t){this._expandedKeys.has(t)?this._expandedKeys.delete(t):this._expandedKeys.add(t),this._rebuild()}_rebuild(){const{idField:t,parentIdField:e}=this._opts;this._roots=A.buildTree(this._data,{idField:t,parentIdField:e},this._expandedKeys),this._render()}_calcLayout(){const{nodeWidth:t,nodeHeight:e,levelGap:o,siblingGap:i}=this._opts,r=new Map;let c=0;const _=u=>{const a=u._depth*(e+o),p=u._expanded?u.children:[];if(!p.length){const g=c;return c+=t+i,r.set(u._treeId,{x:g,y:a}),{minX:g,maxX:g}}let n=1/0,l=-1/0;for(const g of p){const{minX:s,maxX:h}=_(g);s<n&&(n=s),h>l&&(l=h)}const d=n+(l-n+t)/2-t/2;return r.set(u._treeId,{x:d,y:a}),{minX:n,maxX:l}};for(const u of this._roots)_(u);let m=0,f=0;for(const{x:u,y:a}of r.values())u+t>m&&(m=u+t),a+e>f&&(f=a+e);return{layout:r,totalW:m+i,totalH:f+o+16}}_line(t,e,o,i,r){const c=document.createElementNS("http://www.w3.org/2000/svg","line");c.setAttribute("x1",String(e)),c.setAttribute("y1",String(o)),c.setAttribute("x2",String(i)),c.setAttribute("y2",String(r)),c.setAttribute("class","og-orgchart-line"),t.appendChild(c)}_render(){const{nodeWidth:t,nodeHeight:e,levelGap:o,columns:i}=this._opts,{layout:r,totalW:c,totalH:_}=this._calcLayout();this._container.innerHTML="";const m=document.createElement("div");m.className="og-orgchart-wrap",m.style.cssText=`width:${c}px;height:${_}px;`;const f=document.createElementNS("http://www.w3.org/2000/svg","svg");f.setAttribute("width",String(c)),f.setAttribute("height",String(_)),f.style.cssText="position:absolute;top:0;left:0;pointer-events:none;overflow:visible;";const u=p=>{for(const n of p){if(!n._expanded||!n.children.length)continue;const l=r.get(n._treeId),d=l.x+t/2,g=l.y+e,s=g+o/2,h=n.children;if(this._line(f,d,g,d,s),h.length>1){const x=r.get(h[0]._treeId),y=r.get(h[h.length-1]._treeId);this._line(f,x.x+t/2,s,y.x+t/2,s)}for(const x of h){const y=r.get(x._treeId),E=y.x+t/2;this._line(f,E,s,E,y.y)}u(h)}};u(this._roots),m.appendChild(f);const a=p=>{for(const n of p){const l=r.get(n._treeId);if(!l)continue;const d=document.createElement("div");d.className="og-orgchart-node",n._hasChildren&&d.classList.add("og-orgchart-node--branch"),n._expanded&&d.classList.add("og-orgchart-node--expanded"),this._selectedId===n._treeId&&d.classList.add("og-orgchart-node--selected"),d.style.cssText=`left:${l.x}px;top:${l.y}px;width:${t}px;height:${e}px;`;const g=document.createElement("div");g.className="og-orgchart-node-content";for(const s of i){const h=n.data[s.field],x=document.createElement("div");if(x.className="og-orgchart-col"+(s.className?" "+s.className:""),s.style){const y=typeof s.style=="function"?s.style(h,n.data):s.style;x.setAttribute("style",y)}if(s.renderer){const y=s.renderer(h,n.data);typeof y=="string"?x.innerHTML=y:x.appendChild(y)}else x.textContent=h??"";g.appendChild(x)}if(d.appendChild(g),n._hasChildren){const s=document.createElement("button");s.type="button",s.className="og-orgchart-toggle",s.setAttribute("aria-expanded",n._expanded?"true":"false"),s.setAttribute("aria-label",n._expanded?"접기":"펼치기");const h=document.createElement("i");h.setAttribute("aria-hidden","true"),h.className=n._expanded?"bi bi-dash-circle":"bi bi-plus-circle",s.appendChild(h),s.addEventListener("click",x=>{x.stopPropagation(),this._toggle(n._treeId)}),d.appendChild(s)}d.addEventListener("click",()=>{this._selectedId=n._treeId,this._opts.onNodeClick(n._treeId,n.data),this._container.querySelectorAll(".og-orgchart-node--selected").forEach(s=>s.classList.remove("og-orgchart-node--selected")),d.classList.add("og-orgchart-node--selected")}),m.appendChild(d),n._expanded&&n.children.length&&a(n.children)}};a(this._roots),this._container.appendChild(m)}}class N{static parse(t,e={}){var p,n;const{fieldMap:o={},trim:i=!0}=e,c=new DOMParser().parseFromString(t.trim(),"text/xml"),_=c.querySelector("parsererror");if(_)throw new Error(`XML 파싱 오류: ${(p=_.textContent)==null?void 0:p.trim()}`);const m=c.documentElement;let f=e.rowTag;if(!f){const l=e.rootTag?c.querySelector(e.rootTag):m;f=((n=l==null?void 0:l.children[0])==null?void 0:n.tagName)??"row"}const u=c.getElementsByTagName(f),a=[];for(let l=0;l<u.length;l++){const d=u[l],g={};for(const s of Array.from(d.attributes)){const h=o[s.name]??s.name;g[h]=i?s.value.trim():s.value}for(const s of Array.from(d.children)){const h=o[s.tagName]??s.tagName,x=s.textContent??"";g[h]=i?x.trim():x}a.push(g)}return a}static stringify(t,e={}){const{rootTag:o="rows",rowTag:i="row",mode:r="element",fieldMap:c={},declaration:_=!0,indent:m=2,nullAs:f="",excludeFields:u=[]}=e,a=" ".repeat(m),p=[];_&&p.push('<?xml version="1.0" encoding="UTF-8"?>'),p.push(`<${o}>`);for(const n of t){const l=Object.entries(n).filter(([d])=>!u.includes(d));if(r==="attribute"){const d=l.map(([g,s])=>{const h=c[g]??g,x=s==null?f:String(s);return`${h}="${this._escAttr(x)}"`}).join(" ");p.push(`${a}<${i}${d?" "+d:""} />`)}else{p.push(`${a}<${i}>`);for(const[d,g]of l){const s=c[d]??d,h=g==null?f:String(g);p.push(`${a}${a}<${s}>${this._escText(h)}</${s}>`)}p.push(`${a}</${i}>`)}}return p.push(`</${o}>`),p.join(`
2
2
  `)}static parseSap(t){var m,f,u,a;const o=new DOMParser().parseFromString(t.trim(),"text/xml"),i={header:{},items:[],returns:[],raw:o},r=o.getElementsByTagName("DOCUMENTHEADER")[0];if(r)for(const p of Array.from(r.children))i.header[p.tagName]=((m=p.textContent)==null?void 0:m.trim())??"";const c=o.getElementsByTagName("RETURN");for(const p of Array.from(c)){const n={};for(const l of Array.from(p.children))n[l.tagName]=((f=l.textContent)==null?void 0:f.trim())??"";i.returns.push(n)}const _=["ACCOUNTGL","ACCOUNTRECEIVABLE","ACCOUNTPAYABLE","ITEMS"];for(const p of _){const n=o.getElementsByTagName(p)[0];if(!n)continue;const l=n.getElementsByTagName("ITEM"),d=l.length>0?Array.from(l):[n];for(const g of d){const s={};for(const h of Array.from(g.children))s[h.tagName]=((u=h.textContent)==null?void 0:u.trim())??"";i.items.push(s)}break}if(i.items.length===0){const p=o.documentElement,n=Array.from(p.children).filter(l=>l.hasAttribute("SEGMENT"));for(const l of n){if(l.tagName==="EDI_DC40")continue;const d={};for(const g of Array.from(l.children))d[g.tagName]=((a=g.textContent)==null?void 0:a.trim())??"";Object.keys(d).length>0&&i.items.push(d)}}return i}static stringifySap(t){const e=['<?xml version="1.0" encoding="UTF-8"?>',"<BAPI_CALL>"];if(t.BAPI_FUNCTION&&e.push(` <FUNCTION>${this._escText(t.BAPI_FUNCTION)}</FUNCTION>`),t.DOCUMENTHEADER&&typeof t.DOCUMENTHEADER=="object"){e.push(" <DOCUMENTHEADER>");for(const[i,r]of Object.entries(t.DOCUMENTHEADER))r!=null&&r!==""&&e.push(` <${i}>${this._escText(String(r))}</${i}>`);e.push(" </DOCUMENTHEADER>")}const o=Object.keys(t).find(i=>Array.isArray(t[i])&&!i.startsWith("_"));if(o){e.push(` <${o}>`);for(const i of t[o]){e.push(" <ITEM>");for(const[r,c]of Object.entries(i))c!=null&&c!==""&&!r.startsWith("_")&&e.push(` <${r}>${this._escText(String(c))}</${r}>`);e.push(" </ITEM>")}e.push(` </${o}>`)}return e.push("</BAPI_CALL>"),e.join(`
3
3
  `)}static stringifySapBatch(t){const e=['<?xml version="1.0" encoding="UTF-8"?>',`<BAPI_BATCH total="${t.documents.length}">`];return t.documents.forEach((o,i)=>{e.push(` <BAPI_CALL seq="${i+1}">`);const r=this.stringifySap(o).split(`
4
4
  `).filter(c=>!c.startsWith("<?xml")).map(c=>" "+c).join(`
package/dist/open-grid.js CHANGED
@@ -1,5 +1,5 @@
1
- import { b as E } from "./OpenGrid-doS5aFVl.js";
2
- import { O as L } from "./OpenGrid-doS5aFVl.js";
1
+ import { b as E } from "./OpenGrid-D7EJOVi1.js";
2
+ import { O as L } from "./OpenGrid-D7EJOVi1.js";
3
3
  class C {
4
4
  constructor(t, e, r, i = {}) {
5
5
  this._left = t, this._right = e;
@@ -1,9 +1,8 @@
1
1
  import { DataLayer } from './DataLayer.js';
2
2
  import { ColumnLayout } from './ColumnLayout.js';
3
3
  import { GridRenderer } from './GridRenderer.js';
4
- import { CellEditor } from './editors/CellEditor.js';
5
- import { ColumnDef } from './types.js';
6
-
4
+ import type { CellEditor } from './editors/CellEditor.js';
5
+ import type { ColumnDef } from './types.js';
7
6
  export interface CellEditDeps<T extends Record<string, any>> {
8
7
  data: DataLayer<T>;
9
8
  colLayout: ColumnLayout<T>;
@@ -1,8 +1,7 @@
1
- import { DataLayer } from './DataLayer.js';
2
- import { ColumnLayout } from './ColumnLayout.js';
3
- import { CellEditManager } from './CellEditManager.js';
4
- import { RowManager } from './RowManager.js';
5
-
1
+ import type { DataLayer } from './DataLayer.js';
2
+ import type { ColumnLayout } from './ColumnLayout.js';
3
+ import type { CellEditManager } from './CellEditManager.js';
4
+ import type { RowManager } from './RowManager.js';
6
5
  export interface CellEventDeps<T extends Record<string, any>> {
7
6
  getData: () => DataLayer<T>;
8
7
  getColLayout: () => ColumnLayout<T>;
@@ -1,5 +1,4 @@
1
- import { ColumnDef } from './types.js';
2
-
1
+ import type { ColumnDef } from './types.js';
3
2
  export declare function isToggleCol<T>(col: ColumnDef<T>): boolean;
4
3
  export declare function isSelectCol<T>(col: ColumnDef<T>): boolean;
5
4
  export declare function isRadioCol<T>(col: ColumnDef<T>): boolean;
@@ -1,5 +1,4 @@
1
- import { ColumnDef } from './types.js';
2
-
1
+ import type { ColumnDef } from './types.js';
3
2
  export interface FlatColumn<T = any> extends ColumnDef<T> {
4
3
  _colIndex: number;
5
4
  _depth: number;
@@ -1,5 +1,4 @@
1
- import { ContextMenuItem } from './types.js';
2
-
1
+ import type { ContextMenuItem } from './types.js';
3
2
  export type { ContextMenuItem };
4
3
  export interface ContextMenuActions {
5
4
  onSortAsc: () => void;
@@ -1,5 +1,4 @@
1
- import { OpenGrid } from './OpenGrid.js';
2
-
1
+ import type { OpenGrid } from './OpenGrid.js';
3
2
  export declare class CrossGridRegistry {
4
3
  private _map;
5
4
  register(bodyEl: HTMLElement, grid: OpenGrid<any>): void;
@@ -1,5 +1,4 @@
1
- import { SortItem, FilterItem } from './types.js';
2
-
1
+ import type { SortItem, FilterItem } from './types.js';
3
2
  type RowState = 'added' | 'edited' | 'removed' | 'none';
4
3
  export declare class DataLayer<T extends Record<string, any> = any> {
5
4
  private _data;
@@ -10,7 +9,10 @@ export declare class DataLayer<T extends Record<string, any> = any> {
10
9
  private _idMap;
11
10
  private _findQuery;
12
11
  private _findFields;
12
+ private _getStrategy;
13
13
  constructor(idField?: string);
14
+ /** Phase 2: OpenGrid 가 OverrideKernel.getStrategy 를 주입(슬롯 sortComparator/filterPredicate 도달용). */
15
+ setStrategyResolver(resolver: <F extends Function>(slot: string, fallback: F) => F): void;
14
16
  setData(data: T[]): void;
15
17
  getData(): T[];
16
18
  getOriginalData(): T[];
@@ -1,7 +1,6 @@
1
1
  import { ColumnLayout } from './ColumnLayout.js';
2
- import { WorksheetManager } from './WorksheetManager.js';
3
- import { ExportOptions } from './types.js';
4
-
2
+ import type { WorksheetManager } from './WorksheetManager.js';
3
+ import type { ExportOptions } from './types.js';
5
4
  export interface ExportDeps<T extends Record<string, any>> {
6
5
  getData: () => T[];
7
6
  getColLayout: () => ColumnLayout<T>;
@@ -10,6 +9,8 @@ export interface ExportDeps<T extends Record<string, any>> {
10
9
  getContainer: () => HTMLElement;
11
10
  getMaskEnabled: (field: string) => boolean;
12
11
  getWsManager: () => WorksheetManager<T> | null;
12
+ /** Phase 2: OverrideKernel.getStrategy 주입(슬롯 cellSerializer 도달용). */
13
+ getStrategy?: <F extends Function>(slot: string, fallback: F) => F;
13
14
  }
14
15
  export declare class ExportManager<T extends Record<string, any> = any> {
15
16
  private _d;
@@ -1,5 +1,4 @@
1
- import { FilterItem } from './types.js';
2
-
1
+ import type { FilterItem } from './types.js';
3
2
  export type FilterApplyFn = (field: string, items: FilterItem[]) => void;
4
3
  export type FilterClearFn = (field: string) => void;
5
4
  /**
@@ -1,5 +1,4 @@
1
- import { FilterItem } from './types.js';
2
-
1
+ import type { FilterItem } from './types.js';
3
2
  export interface FilterSelectOption {
4
3
  value: string;
5
4
  text: string;
@@ -1,8 +1,7 @@
1
- import { ColumnLayout } from './ColumnLayout.js';
2
- import { DataLayer } from './DataLayer.js';
3
- import { Pagination } from './Pagination.js';
4
- import { VirtualScroll } from './VirtualScroll.js';
5
-
1
+ import type { ColumnLayout } from './ColumnLayout.js';
2
+ import type { DataLayer } from './DataLayer.js';
3
+ import type { Pagination } from './Pagination.js';
4
+ import type { VirtualScroll } from './VirtualScroll.js';
6
5
  export interface FindBarDeps<T extends Record<string, any>> {
7
6
  getColLayout: () => ColumnLayout<T>;
8
7
  getData: () => DataLayer<T>;
@@ -1,11 +1,12 @@
1
1
  import { ColumnLayout } from './ColumnLayout.js';
2
-
3
2
  export interface FooterDeps<T extends Record<string, any>> {
4
3
  getData: () => T[];
5
4
  getColLayout: () => ColumnLayout<T>;
6
5
  getColWidths: () => number[];
7
6
  getOptions: () => any;
8
7
  getContainer: () => HTMLElement;
8
+ /** Phase 2: OverrideKernel.getStrategy 주입(슬롯 summaryOp 도달용). */
9
+ getStrategy?: <F extends Function>(slot: string, fallback: F) => F;
9
10
  }
10
11
  export declare class FooterManager<T extends Record<string, any> = any> {
11
12
  private _d;
@@ -1,5 +1,4 @@
1
- import { OGDecimal, OGDecimalLike } from './OGDecimal.js';
2
-
1
+ import { OGDecimal, type OGDecimalLike } from './OGDecimal.js';
3
2
  export type FormulaContext = Record<string, OGDecimalLike>;
4
3
  /**
5
4
  * 문자열 수식을 평가해 OGDecimal 결과를 반환한다.
@@ -1,8 +1,7 @@
1
1
  import { DataLayer } from './DataLayer.js';
2
2
  import { RowDragDrop } from './RowDragDrop.js';
3
3
  import { MergeEngine } from './MergeEngine.js';
4
- import { ColumnDef, SortItem } from './types.js';
5
-
4
+ import type { ColumnDef, SortItem } from './types.js';
6
5
  export interface RendererCallbacks {
7
6
  onHeaderClick: (field: string, shiftKey: boolean) => void;
8
7
  onCellClick: (ri: number, ci: number, e: MouseEvent) => void;
@@ -32,6 +31,8 @@ export declare class GridRenderer {
32
31
  get bodyWrapper(): HTMLElement;
33
32
  constructor(root: HTMLElement, opts: any, cbs: RendererCallbacks);
34
33
  updateSize(totalHeight: number, headerHeight: number): void;
34
+ /** 현재 렌더된 헤더 영역의 실제 높이(px). 헤더 줄바꿈으로 늘어난 높이를 레이아웃에 반영할 때 사용. */
35
+ getHeaderHeight(): number;
35
36
  renderHeader(headerRows: any[][], leaves: ColumnDef[], widths: number[], sortList: SortItem[], opts: any): void;
36
37
  renderBody(startIndex: number, endIndex: number, data: DataLayer<any>, leaves: ColumnDef[], widths: number[], opts: any, offsetY: number, totalHeight: number, selectedRows: Set<number>, checkedRows: Set<number>, groupFlatRows?: Array<any> | null, onGroupToggle?: (key: string) => void, onTreeToggle?: (nodeId: any) => void, extraOpts?: Record<string, any>, mergeEngine?: MergeEngine): void;
37
38
  getCellEl(rowIndex: number, colIndex: number): HTMLElement | undefined;
@@ -1,5 +1,4 @@
1
- import { OpenGridInstance } from './types.js';
2
-
1
+ import type { OpenGridInstance } from './types.js';
3
2
  export interface GridShuttleOptions {
4
3
  /** 버튼 배치 방향 (기본 'vertical') */
5
4
  layout?: 'vertical' | 'horizontal';
@@ -1,5 +1,4 @@
1
- import { SummaryOp } from './types.js';
2
-
1
+ import type { SummaryOp } from './types.js';
3
2
  export interface GroupRow<T = any> {
4
3
  _isGroup: true;
5
4
  _groupField: string;
@@ -25,8 +24,12 @@ export interface SummaryDef {
25
24
  format?: string;
26
25
  }
27
26
  declare function _isGroup<T>(item: any): item is GroupRow<T>;
28
- /** flat 데이터를 fields 기준으로 계층 그룹핑 */
29
- export declare function buildGroups<T extends Record<string, any>>(data: T[], fields: string[], summaryDefs?: SummaryDef[], expandedKeys?: Set<string>, getRowState?: (row: T) => string): GroupRow<T>[];
27
+ /**
28
+ * flat 데이터를 fields 기준으로 계층 그룹핑.
29
+ * Phase 2 슬롯 #5: groupKeyFn(getKey) — 미지정 시 default = `row[field]`(현행 단일 필드 키).
30
+ * GroupTreeManager 가 host.getStrategy 로 주입. ⚠️ 핫패스(행당) — 슬롯 예외 비격리.
31
+ */
32
+ export declare function buildGroups<T extends Record<string, any>>(data: T[], fields: string[], summaryDefs?: SummaryDef[], expandedKeys?: Set<string>, getRowState?: (row: T) => string, getKey?: (row: T, remainingFields: string[]) => any): GroupRow<T>[];
30
33
  /**
31
34
  * 숫자 포맷팅 (GroupEngine 내부 + 외부 공용)
32
35
  * '#,##0' → 천단위 콤마, 정수
@@ -1,8 +1,7 @@
1
1
  import { DataLayer } from './DataLayer.js';
2
2
  import { VirtualScroll } from './VirtualScroll.js';
3
- import { GroupRow } from './GroupEngine.js';
4
- import { TreeNode } from './TreeEngine.js';
5
-
3
+ import { type GroupRow } from './GroupEngine.js';
4
+ import { type TreeNode } from './TreeEngine.js';
6
5
  export interface GroupTreeDeps<T extends Record<string, any>> {
7
6
  getData: () => T[];
8
7
  getDataLayer: () => DataLayer<T>;
@@ -10,6 +9,8 @@ export interface GroupTreeDeps<T extends Record<string, any>> {
10
9
  getVs: () => VirtualScroll | null;
11
10
  doRenderFull: (totalRows: number) => void;
12
11
  doRender: () => void;
12
+ /** Phase 2: OverrideKernel.getStrategy 주입(슬롯 groupKeyFn 도달용). */
13
+ getStrategy?: <F extends Function>(slot: string, fallback: F) => F;
13
14
  }
14
15
  export declare class GroupTreeManager<T extends Record<string, any> = any> {
15
16
  private _groupFields;
@@ -29,6 +30,8 @@ export declare class GroupTreeManager<T extends Record<string, any> = any> {
29
30
  groupBy(fields: string[]): void;
30
31
  clearGroup(): void;
31
32
  expandAll(): void;
33
+ /** Phase 2 슬롯 #5: groupKeyFn resolver. default = undefined(→ GroupEngine 가 row[field] 사용). */
34
+ private _groupKeyFn;
32
35
  collapseAll(): void;
33
36
  handleGroupToggle(groupKey: string): void;
34
37
  rebuildGroups(): void;
@@ -1,8 +1,7 @@
1
- import { DataLayer } from './DataLayer.js';
2
- import { ColumnLayout } from './ColumnLayout.js';
3
- import { CellEditManager } from './CellEditManager.js';
4
- import { RowManager } from './RowManager.js';
5
-
1
+ import type { DataLayer } from './DataLayer.js';
2
+ import type { ColumnLayout } from './ColumnLayout.js';
3
+ import type { CellEditManager } from './CellEditManager.js';
4
+ import type { RowManager } from './RowManager.js';
6
5
  export interface KeyboardDeps<T extends Record<string, any>> {
7
6
  getEditMgr: () => CellEditManager<T>;
8
7
  getRowMgr: () => RowManager<T>;
@@ -1,8 +1,8 @@
1
1
  import { EventEmitter } from './EventEmitter.js';
2
- import { FilterSelectConfig } from './FilterSelect.js';
3
- import { MergeCell } from './MergeEngine.js';
4
- import { GridOptions, OpenGridInstance, ColumnDef, SortItem, FilterItem, ExportOptions, Position, TriggerHandler, TriggerEvent } from './types.js';
5
-
2
+ import type { FilterSelectConfig } from './FilterSelect.js';
3
+ import { type MergeCell } from './MergeEngine.js';
4
+ import type { OverrideLayer } from './OverrideKernel.js';
5
+ import type { GridOptions, OpenGridInstance, ColumnDef, SortItem, FilterItem, ExportOptions, Position, TriggerHandler, TriggerEvent, OverrideApi, OverrideCallOptions } from './types.js';
6
6
  export declare class OpenGrid<T extends Record<string, any> = any> extends EventEmitter implements OpenGridInstance<T> {
7
7
  private _container;
8
8
  private _options;
@@ -36,7 +36,25 @@ export declare class OpenGrid<T extends Record<string, any> = any> extends Event
36
36
  private _findMgr;
37
37
  private _grpMgr;
38
38
  private _cellEvt;
39
+ private _ovk;
40
+ /** 공개 override API (호출가능 + .strategy). 생성자 말미에 부착. */
41
+ override: OverrideApi<T>;
42
+ /** 정적 전역 override 레이어 (모든 신규 인스턴스에 생성자 말미 적용). */
43
+ private static _defaultOverrides;
44
+ /** 정적 전역 strategy 슬롯. */
45
+ private static _defaultStrategies;
46
+ /** 정적: 모든 신규 그리드에 적용될 override 레이어 등록. */
47
+ static defaultOverride(name: string, fn: OverrideLayer, opts?: OverrideCallOptions): typeof OpenGrid;
48
+ /** 정적 전역 defaults 네임스페이스 (strategy 슬롯). */
49
+ static defaults: {
50
+ strategy(slot: string, fn: Function): typeof OpenGrid;
51
+ };
39
52
  constructor(container: string | HTMLElement, options: GridOptions<T>);
53
+ restore(name: string): this;
54
+ restoreAll(): this;
55
+ hasOverride(name: string): boolean;
56
+ getOverrideNames(): string[];
57
+ getStrategy<F extends Function>(slot: string, fallback: F): F;
40
58
  private _mount;
41
59
  private _initContextMenu;
42
60
  openContextMenu(e: MouseEvent, items?: import('./types').ContextMenuItem[]): void;
@@ -63,6 +81,11 @@ export declare class OpenGrid<T extends Record<string, any> = any> extends Event
63
81
  private _onResize;
64
82
  private _recalcWidths;
65
83
  private _renderHeader;
84
+ /**
85
+ * 렌더된 헤더의 실제 높이를 측정해 본문(bodyWrap) 높이와 뷰포트 높이에 반영한다.
86
+ * 줄바꿈이 없는 헤더는 측정 높이가 headerHeight 이하라 기존 고정 동작과 동일하다.
87
+ */
88
+ private _syncHeaderLayout;
66
89
  private _doRender;
67
90
  private _handleGroupToggle;
68
91
  private _visRange;
@@ -0,0 +1,70 @@
1
+ /**
2
+ * OverrideKernel — `grid.override()` 확장 커널 (94 최종 종합본 반영)
3
+ *
4
+ * 핵심 원칙(94 §1 F1~F4):
5
+ * - F2: FIFO 합성 = `reduce` 좌측폴드. 먼저 등록 = 안쪽(원본 근접), 나중 등록 = 바깥.
6
+ * `[fn1,fn2,fn3]` → 실행 시 fn3(최후등록)이 최외곽, orig() 호출이 안쪽으로 전파.
7
+ * - F3: 재진입 가드 = 깊이 제한(maxDepth 기본 32) + 호출경로 사이클 감지(_callPath, 동일 name
8
+ * 재출현 시 차단) + 명시적 { reentrant:true } 옵트인. 정당한 cascade는 깊이 내 허용.
9
+ * - F4: 에러 격리 = 기본 strict=true(예외 전파). { onError:'fallback' } 옵트인 시에만
10
+ * 경고 후 orig fallback(멱등 가정, 롤백 불가).
11
+ *
12
+ * 본 커널은 단독 클래스로, OpenGrid 인스턴스를 host 로 받아 프로토타입 메서드를
13
+ * 인스턴스 프로퍼티로 shadowing(순수 런타임 래핑, C1-clean) 한다. 소스 본문 0줄 수정.
14
+ */
15
+ export interface OverrideKernelHost {
16
+ [name: string]: any;
17
+ }
18
+ /** override() 호출 시 레이어가 받는 함수 시그니처: 첫 인자는 안쪽(원본 근접) 함수. */
19
+ export type OverrideLayer = (orig: (...args: any[]) => any, ...args: any[]) => any;
20
+ export interface OverrideOptions {
21
+ /** 동일 name 재진입을 허용(정당한 재귀). 기본 false. */
22
+ reentrant?: boolean;
23
+ /** 'fallback' → 레이어 예외 시 경고 후 orig 실행(멱등 가정). 미지정 시 strict(전파). */
24
+ onError?: 'fallback';
25
+ }
26
+ export declare class OverrideKernel {
27
+ private _host;
28
+ /** name → this-bound 원본 함수 (최초 1회 보존) */
29
+ private _originals;
30
+ /** name → 최초 override 직전 host 가 own 프로퍼티로 메서드를 가졌는지(복구 방식 결정) */
31
+ private _hadOwn;
32
+ /** name → 레이어 목록(등록순, FIFO) */
33
+ private _stack;
34
+ /** slot → strategy 함수 */
35
+ private _strategies;
36
+ /** F3: 현재 호출 경로(메서드 name 스택) — 사이클 감지용 */
37
+ private _callPath;
38
+ /** F4: 기본 strict (예외 전파) */
39
+ private _strict;
40
+ /** F3: 재귀 깊이 제한 */
41
+ private _maxDepth;
42
+ constructor(host: OverrideKernelHost, opts?: {
43
+ strict?: boolean;
44
+ maxDepth?: number;
45
+ });
46
+ /**
47
+ * 메서드 override 등록.
48
+ * 1. 최초 1회 원본을 this-bound 로 보존.
49
+ * 2. 레이어 push (FIFO 등록순).
50
+ * 3. reduce 좌측폴드로 합성(나중 등록 = 최외곽).
51
+ * 4. 재진입 가드 + 에러 격리로 감싼 dispatcher 를 host[name] 인스턴스 프로퍼티에 할당.
52
+ */
53
+ override(name: string, fn: OverrideLayer, opts?: OverrideOptions): OverrideKernelHost;
54
+ /** 합성된 dispatcher 생성. override/restore 시점마다 재구성. */
55
+ private _buildDispatcher;
56
+ /** strategy 슬롯 등록 (Phase 2 매니저 배선용 API). */
57
+ strategy(slot: string, fn: Function): OverrideKernelHost;
58
+ /** strategy 슬롯 조회 — 미등록 시 fallback 반환 (매니저 read API). */
59
+ getStrategy<F extends Function>(slot: string, fallback: F): F;
60
+ /** 슬롯 등록 여부. */
61
+ hasStrategy(slot: string): boolean;
62
+ /** 단일 메서드 원본 복구. 없으면 no-op. */
63
+ restore(name: string): OverrideKernelHost;
64
+ /** 전체 복구 + strategy clear. destroy 시 호출. */
65
+ restoreAll(): OverrideKernelHost;
66
+ /** override 등록 여부. */
67
+ hasOverride(name: string): boolean;
68
+ /** override 등록된 메서드 이름 목록. */
69
+ getOverrideNames(): string[];
70
+ }
@@ -1,5 +1,4 @@
1
- import { ColumnDef } from './types.js';
2
-
1
+ import type { ColumnDef } from './types.js';
3
2
  export type PivotAgg = 'SUM' | 'COUNT' | 'AVG' | 'MIN' | 'MAX';
4
3
  export interface PivotConfig {
5
4
  rowFields: string[];
@@ -1,5 +1,4 @@
1
1
  import { DataLayer } from './DataLayer.js';
2
-
3
2
  export declare class RowManager<T extends Record<string, any> = any> {
4
3
  private _selectedRows;
5
4
  private _checkedRows;
@@ -1,9 +1,8 @@
1
- import { DataLayer } from './DataLayer.js';
2
- import { ColumnLayout } from './ColumnLayout.js';
3
- import { VirtualScroll } from './VirtualScroll.js';
4
- import { Pagination } from './Pagination.js';
5
- import { SortItem, FilterItem } from './types.js';
6
-
1
+ import type { DataLayer } from './DataLayer.js';
2
+ import type { ColumnLayout } from './ColumnLayout.js';
3
+ import type { VirtualScroll } from './VirtualScroll.js';
4
+ import type { Pagination } from './Pagination.js';
5
+ import type { SortItem, FilterItem } from './types.js';
7
6
  export interface SortFilterDeps<T extends Record<string, any>> {
8
7
  getData: () => DataLayer<T>;
9
8
  getColLayout: () => ColumnLayout<T>;
@@ -1,5 +1,4 @@
1
- import { TriggerContext, TriggerHandler, TriggerEvent } from './types.js';
2
-
1
+ import type { TriggerContext, TriggerHandler, TriggerEvent } from './types.js';
3
2
  export declare class TriggerManager {
4
3
  private _triggers;
5
4
  add(event: TriggerEvent | string, handler: TriggerHandler): void;
@@ -1,5 +1,4 @@
1
- import { ColumnDef, WorksheetState } from './types.js';
2
-
1
+ import type { ColumnDef, WorksheetState } from './types.js';
3
2
  export type SwitchCallback<T> = (name: string, state: WorksheetState<T>) => void;
4
3
  export declare class WorksheetManager<T extends Record<string, any> = any> {
5
4
  private _sheets;
@@ -1,6 +1,5 @@
1
- import { ColumnDef } from '../types.js';
2
- import { RenderContext } from '../renderers/CellRenderer.js';
3
-
1
+ import type { ColumnDef } from '../types.js';
2
+ import type { RenderContext } from '../renderers/CellRenderer.js';
4
3
  export { DateEditor } from './DateEditor.js';
5
4
  export { SelectEditor } from './SelectEditor.js';
6
5
  export interface EditorResult {
@@ -1,6 +1,5 @@
1
- import { CellEditor } from './CellEditor.js';
2
- import { RenderContext } from '../renderers/CellRenderer.js';
3
-
1
+ import type { CellEditor } from './CellEditor.js';
2
+ import type { RenderContext } from '../renderers/CellRenderer.js';
4
3
  export declare class DateEditor implements CellEditor {
5
4
  private input;
6
5
  private _container;
@@ -1,6 +1,5 @@
1
- import { CellEditor } from './CellEditor.js';
2
- import { RenderContext } from '../renderers/CellRenderer.js';
3
-
1
+ import type { CellEditor } from './CellEditor.js';
2
+ import type { RenderContext } from '../renderers/CellRenderer.js';
4
3
  export type SelectOption = string | {
5
4
  label?: string;
6
5
  text?: string;
@@ -1,5 +1,4 @@
1
- import { ColumnDef } from '../types.js';
2
-
1
+ import type { ColumnDef } from '../types.js';
3
2
  export interface RenderContext<T = any> {
4
3
  value: any;
5
4
  row: T;
@@ -12,6 +11,7 @@ export interface RenderContext<T = any> {
12
11
  export interface CellRenderer {
13
12
  render(ctx: RenderContext): HTMLElement;
14
13
  }
14
+ export declare function setDisplayFormatterResolver(resolver: ((value: any, field: string, row: any) => string | null) | null): void;
15
15
  /**
16
16
  * 컬럼에 formula가 있으면 OGDecimal로 평가해 표시값(string)을 반환.
17
17
  * formula가 없으면 null 반환 → 기존 ctx.value 사용.
@@ -28,8 +28,8 @@ export declare function resolveFormula(ctx: RenderContext): string | null;
28
28
  * '$#,##0;($#,##0)' 양수;음수 패턴 — 음수를 괄호로 (회계식)
29
29
  * currency: ISO 통화코드('KRW'|'USD'|'EUR'…) 지정 시 Intl.NumberFormat 로케일 통화 포맷 우선.
30
30
  */
31
- export declare function formatNumber(value: any, format?: string, precision?: number, currency?: string): string;
32
- export declare function formatDate(value: any, format?: string): string;
31
+ export declare function formatNumber(value: any, format?: string, precision?: number, currency?: string, field?: string, row?: any): string;
32
+ export declare function formatDate(value: any, format?: string, field?: string, row?: any): string;
33
33
  export declare class TextRenderer implements CellRenderer {
34
34
  render(ctx: RenderContext): HTMLElement;
35
35
  }