open-grid 0.2.0 → 0.3.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.
@@ -114,6 +114,10 @@
114
114
  font-family: var(--og-font-family);
115
115
  font-size: var(--og-font-size);
116
116
  color: var(--og-row-color);
117
+ /* host isolation: 호스트(WordPress 등) 테마의 line-height(1.7~1.9) 상속을
118
+ 차단한다. 모든 후손(행·셀·헤더 <th> 포함)으로 normal이 cascade되어,
119
+ 고정 행/헤더 높이가 호스트 본문 line-height에 desync되는 것을 막는다. */
120
+ line-height: normal;
117
121
  display: flex;
118
122
  flex-direction: column;
119
123
  overflow: hidden;
@@ -139,6 +143,11 @@
139
143
  table-layout: fixed;
140
144
  border-collapse: collapse;
141
145
  width: 100%;
146
+ /* host isolation: 헤더만 실제 <table>이라 호스트 table 스타일에 노출된다.
147
+ margin/border-spacing은 상속되지 않으므로 요소에 직접 0으로 고정해
148
+ 헤더 테이블이 바디(absolute flex div)와 어긋나지 않게 한다. */
149
+ margin: 0;
150
+ border-spacing: 0;
142
151
  }
143
152
 
144
153
  .og-header-cell {
@@ -155,6 +164,15 @@
155
164
  user-select: none;
156
165
  box-sizing: border-box;
157
166
  position: relative;
167
+ /* host isolation: <th>는 table-cell이라 호스트 th/td 규칙에 직접 노출된다
168
+ (element 셀렉터라 .og-container의 상속 line-height를 이김). 따라서 텍스트
169
+ 메트릭·잔여 보더를 헤더셀에 직접 고정해 호스트 th{line-height;font-size;border}
170
+ 침범을 차단한다. (padding/border-right/bottom/text-align은 이미 클래스 특이도로 방어됨) */
171
+ vertical-align: middle;
172
+ line-height: normal;
173
+ font-size: var(--og-font-size);
174
+ border-top: 0;
175
+ border-left: 0;
158
176
  font-weight: var(--og-header-font-weight);
159
177
  letter-spacing: var(--og-header-letter-spacing);
160
178
  transition: background var(--og-transition-fast), color var(--og-transition-fast);
@@ -262,6 +280,15 @@
262
280
  .og-cell--center { justify-content: center; }
263
281
  .og-cell--right { justify-content: flex-end; }
264
282
  .og-cell--editable { cursor: text; }
283
+ /* 줄바꿈 컬럼: nowrap+ellipsis 대신 여러 줄 표시.
284
+ 가상 스크롤은 고정 rowHeight 를 쓰므로, 줄바꿈은 rowHeight 를 키워 함께 사용한다. */
285
+ .og-cell--wrap {
286
+ white-space: normal;
287
+ text-overflow: clip;
288
+ overflow: hidden;
289
+ word-break: break-word;
290
+ line-height: 1.35;
291
+ }
265
292
 
266
293
  /* WCAG 2.2 2.4.13: 포커스 셀 outline 최소 3px, 대비 3:1 이상 */
267
294
  .og-cell.og-cell-focused {
@@ -898,8 +925,11 @@
898
925
  --og-cm-* 변수로 테마별 미세 조정 지원
899
926
  ============================================================ */
900
927
 
901
- /* 컨텍스트 메뉴 전용 변수 — 기본값은 그리드 변수 참조 */
902
- .og-container {
928
+ /* 컨텍스트 메뉴 전용 변수 — 기본값은 그리드 변수 참조.
929
+ .og-context-menu 도 포함: 메뉴가 document.body 에 부착될 때(임베드 호스트 격리)
930
+ 자기 자신에서 --og-cm-* 가 해석되도록 한다(menu 의 data-og-theme 가 --og-* 를 공급). */
931
+ .og-container,
932
+ .og-context-menu {
903
933
  --og-cm-bg: var(--og-row-bg);
904
934
  --og-cm-color: var(--og-row-color);
905
935
  --og-cm-border: var(--og-border-color);
@@ -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-CAodJLLm.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-CzP1mcZW.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-CfZy3e7Q.js";
3
+ import { O as L } from "./OpenGrid-Dult64jp.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-CAodJLLm.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-CzP1mcZW.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-CfZy3e7Q.js";
2
+ import { O as w } from "./OpenGrid-Dult64jp.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-CAodJLLm.cjs");class C{constructor(t,s){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:()=>{},...s},this._container.classList.add("og-orgchart")}setData(t){this._data=t;const{idField:s,parentIdField:c,expandOnLoad:o}=this._opts;o&&this._expandedKeys.size===0&&t.forEach(a=>this._expandedKeys.add(a[s])),this._roots=A.buildTree(t,{idField:s,parentIdField:c},this._expandedKeys),this._render()}setTheme(t){this._container.setAttribute("data-og-theme",t)}expandAll(){const t=s=>{for(const c of s)this._expandedKeys.add(c._treeId),c.children.length&&t(c.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:s}=this._opts;this._roots=A.buildTree(this._data,{idField:t,parentIdField:s},this._expandedKeys),this._render()}_calcLayout(){const{nodeWidth:t,nodeHeight:s,levelGap:c,siblingGap:o}=this._opts,a=new Map;let l=0;const x=f=>{const u=f._depth*(s+c),h=f._expanded?f.children:[];if(!h.length){const p=l;return l+=t+o,a.set(f._treeId,{x:p,y:u}),{minX:p,maxX:p}}let n=1/0,r=-1/0;for(const p of h){const{minX:e,maxX:d}=x(p);e<n&&(n=e),d>r&&(r=d)}const i=n+(r-n+t)/2-t/2;return a.set(f._treeId,{x:i,y:u}),{minX:n,maxX:r}};for(const f of this._roots)x(f);let _=0,g=0;for(const{x:f,y:u}of a.values())f+t>_&&(_=f+t),u+s>g&&(g=u+s);return{layout:a,totalW:_+o,totalH:g+c+16}}_line(t,s,c,o,a){const l=document.createElementNS("http://www.w3.org/2000/svg","line");l.setAttribute("x1",String(s)),l.setAttribute("y1",String(c)),l.setAttribute("x2",String(o)),l.setAttribute("y2",String(a)),l.setAttribute("class","og-orgchart-line"),t.appendChild(l)}_render(){const{nodeWidth:t,nodeHeight:s,levelGap:c,columns:o}=this._opts,{layout:a,totalW:l,totalH:x}=this._calcLayout();this._container.innerHTML="";const _=document.createElement("div");_.className="og-orgchart-wrap",_.style.cssText=`width:${l}px;height:${x}px;`;const g=document.createElementNS("http://www.w3.org/2000/svg","svg");g.setAttribute("width",String(l)),g.setAttribute("height",String(x)),g.style.cssText="position:absolute;top:0;left:0;pointer-events:none;overflow:visible;";const f=h=>{for(const n of h){if(!n._expanded||!n.children.length)continue;const r=a.get(n._treeId),i=r.x+t/2,p=r.y+s,e=p+c/2,d=n.children;if(this._line(g,i,p,i,e),d.length>1){const m=a.get(d[0]._treeId),y=a.get(d[d.length-1]._treeId);this._line(g,m.x+t/2,e,y.x+t/2,e)}for(const m of d){const y=a.get(m._treeId),E=y.x+t/2;this._line(g,E,e,E,y.y)}f(d)}};f(this._roots),_.appendChild(g);const u=h=>{for(const n of h){const r=a.get(n._treeId);if(!r)continue;const i=document.createElement("div");i.className="og-orgchart-node",n._hasChildren&&i.classList.add("og-orgchart-node--branch"),n._expanded&&i.classList.add("og-orgchart-node--expanded"),this._selectedId===n._treeId&&i.classList.add("og-orgchart-node--selected"),i.style.cssText=`left:${r.x}px;top:${r.y}px;width:${t}px;height:${s}px;`;const p=document.createElement("div");p.className="og-orgchart-node-content";for(const e of o){const d=n.data[e.field],m=document.createElement("div");if(m.className="og-orgchart-col"+(e.className?" "+e.className:""),e.style){const y=typeof e.style=="function"?e.style(d,n.data):e.style;m.setAttribute("style",y)}if(e.renderer){const y=e.renderer(d,n.data);typeof y=="string"?m.innerHTML=y:m.appendChild(y)}else m.textContent=d??"";p.appendChild(m)}if(i.appendChild(p),n._hasChildren){const e=document.createElement("button");e.type="button",e.className="og-orgchart-toggle",e.setAttribute("aria-expanded",n._expanded?"true":"false"),e.setAttribute("aria-label",n._expanded?"접기":"펼치기");const d=document.createElement("i");d.setAttribute("aria-hidden","true"),d.className=n._expanded?"bi bi-dash-circle":"bi bi-plus-circle",e.appendChild(d),e.addEventListener("click",m=>{m.stopPropagation(),this._toggle(n._treeId)}),i.appendChild(e)}i.addEventListener("click",()=>{this._selectedId=n._treeId,this._opts.onNodeClick(n._treeId,n.data),this._container.querySelectorAll(".og-orgchart-node--selected").forEach(e=>e.classList.remove("og-orgchart-node--selected")),i.classList.add("og-orgchart-node--selected")}),_.appendChild(i),n._expanded&&n.children.length&&u(n.children)}};u(this._roots),this._container.appendChild(_)}}class N{static parse(t,s={}){var h,n;const{fieldMap:c={},trim:o=!0}=s,l=new DOMParser().parseFromString(t.trim(),"text/xml"),x=l.querySelector("parsererror");if(x)throw new Error(`XML 파싱 오류: ${(h=x.textContent)==null?void 0:h.trim()}`);const _=l.documentElement;let g=s.rowTag;if(!g){const r=s.rootTag?l.querySelector(s.rootTag):_;g=((n=r==null?void 0:r.children[0])==null?void 0:n.tagName)??"row"}const f=l.getElementsByTagName(g),u=[];for(let r=0;r<f.length;r++){const i=f[r],p={};for(const e of Array.from(i.attributes)){const d=c[e.name]??e.name;p[d]=o?e.value.trim():e.value}for(const e of Array.from(i.children)){const d=c[e.tagName]??e.tagName,m=e.textContent??"";p[d]=o?m.trim():m}u.push(p)}return u}static stringify(t,s={}){const{rootTag:c="rows",rowTag:o="row",mode:a="element",fieldMap:l={},declaration:x=!0,indent:_=2,nullAs:g="",excludeFields:f=[]}=s,u=" ".repeat(_),h=[];x&&h.push('<?xml version="1.0" encoding="UTF-8"?>'),h.push(`<${c}>`);for(const n of t){const r=Object.entries(n).filter(([i])=>!f.includes(i));if(a==="attribute"){const i=r.map(([p,e])=>{const d=l[p]??p,m=e==null?g:String(e);return`${d}="${this._escAttr(m)}"`}).join(" ");h.push(`${u}<${o}${i?" "+i:""} />`)}else{h.push(`${u}<${o}>`);for(const[i,p]of r){const e=l[i]??i,d=p==null?g:String(p);h.push(`${u}${u}<${e}>${this._escText(d)}</${e}>`)}h.push(`${u}</${o}>`)}}return h.push(`</${c}>`),h.join(`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const A=require("./OpenGrid-CzP1mcZW.cjs");class C{constructor(t,s){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:()=>{},...s},this._container.classList.add("og-orgchart")}setData(t){this._data=t;const{idField:s,parentIdField:c,expandOnLoad:o}=this._opts;o&&this._expandedKeys.size===0&&t.forEach(a=>this._expandedKeys.add(a[s])),this._roots=A.buildTree(t,{idField:s,parentIdField:c},this._expandedKeys),this._render()}setTheme(t){this._container.setAttribute("data-og-theme",t)}expandAll(){const t=s=>{for(const c of s)this._expandedKeys.add(c._treeId),c.children.length&&t(c.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:s}=this._opts;this._roots=A.buildTree(this._data,{idField:t,parentIdField:s},this._expandedKeys),this._render()}_calcLayout(){const{nodeWidth:t,nodeHeight:s,levelGap:c,siblingGap:o}=this._opts,a=new Map;let l=0;const x=f=>{const u=f._depth*(s+c),h=f._expanded?f.children:[];if(!h.length){const p=l;return l+=t+o,a.set(f._treeId,{x:p,y:u}),{minX:p,maxX:p}}let n=1/0,r=-1/0;for(const p of h){const{minX:e,maxX:d}=x(p);e<n&&(n=e),d>r&&(r=d)}const i=n+(r-n+t)/2-t/2;return a.set(f._treeId,{x:i,y:u}),{minX:n,maxX:r}};for(const f of this._roots)x(f);let _=0,g=0;for(const{x:f,y:u}of a.values())f+t>_&&(_=f+t),u+s>g&&(g=u+s);return{layout:a,totalW:_+o,totalH:g+c+16}}_line(t,s,c,o,a){const l=document.createElementNS("http://www.w3.org/2000/svg","line");l.setAttribute("x1",String(s)),l.setAttribute("y1",String(c)),l.setAttribute("x2",String(o)),l.setAttribute("y2",String(a)),l.setAttribute("class","og-orgchart-line"),t.appendChild(l)}_render(){const{nodeWidth:t,nodeHeight:s,levelGap:c,columns:o}=this._opts,{layout:a,totalW:l,totalH:x}=this._calcLayout();this._container.innerHTML="";const _=document.createElement("div");_.className="og-orgchart-wrap",_.style.cssText=`width:${l}px;height:${x}px;`;const g=document.createElementNS("http://www.w3.org/2000/svg","svg");g.setAttribute("width",String(l)),g.setAttribute("height",String(x)),g.style.cssText="position:absolute;top:0;left:0;pointer-events:none;overflow:visible;";const f=h=>{for(const n of h){if(!n._expanded||!n.children.length)continue;const r=a.get(n._treeId),i=r.x+t/2,p=r.y+s,e=p+c/2,d=n.children;if(this._line(g,i,p,i,e),d.length>1){const m=a.get(d[0]._treeId),y=a.get(d[d.length-1]._treeId);this._line(g,m.x+t/2,e,y.x+t/2,e)}for(const m of d){const y=a.get(m._treeId),E=y.x+t/2;this._line(g,E,e,E,y.y)}f(d)}};f(this._roots),_.appendChild(g);const u=h=>{for(const n of h){const r=a.get(n._treeId);if(!r)continue;const i=document.createElement("div");i.className="og-orgchart-node",n._hasChildren&&i.classList.add("og-orgchart-node--branch"),n._expanded&&i.classList.add("og-orgchart-node--expanded"),this._selectedId===n._treeId&&i.classList.add("og-orgchart-node--selected"),i.style.cssText=`left:${r.x}px;top:${r.y}px;width:${t}px;height:${s}px;`;const p=document.createElement("div");p.className="og-orgchart-node-content";for(const e of o){const d=n.data[e.field],m=document.createElement("div");if(m.className="og-orgchart-col"+(e.className?" "+e.className:""),e.style){const y=typeof e.style=="function"?e.style(d,n.data):e.style;m.setAttribute("style",y)}if(e.renderer){const y=e.renderer(d,n.data);typeof y=="string"?m.innerHTML=y:m.appendChild(y)}else m.textContent=d??"";p.appendChild(m)}if(i.appendChild(p),n._hasChildren){const e=document.createElement("button");e.type="button",e.className="og-orgchart-toggle",e.setAttribute("aria-expanded",n._expanded?"true":"false"),e.setAttribute("aria-label",n._expanded?"접기":"펼치기");const d=document.createElement("i");d.setAttribute("aria-hidden","true"),d.className=n._expanded?"bi bi-dash-circle":"bi bi-plus-circle",e.appendChild(d),e.addEventListener("click",m=>{m.stopPropagation(),this._toggle(n._treeId)}),i.appendChild(e)}i.addEventListener("click",()=>{this._selectedId=n._treeId,this._opts.onNodeClick(n._treeId,n.data),this._container.querySelectorAll(".og-orgchart-node--selected").forEach(e=>e.classList.remove("og-orgchart-node--selected")),i.classList.add("og-orgchart-node--selected")}),_.appendChild(i),n._expanded&&n.children.length&&u(n.children)}};u(this._roots),this._container.appendChild(_)}}class N{static parse(t,s={}){var h,n;const{fieldMap:c={},trim:o=!0}=s,l=new DOMParser().parseFromString(t.trim(),"text/xml"),x=l.querySelector("parsererror");if(x)throw new Error(`XML 파싱 오류: ${(h=x.textContent)==null?void 0:h.trim()}`);const _=l.documentElement;let g=s.rowTag;if(!g){const r=s.rootTag?l.querySelector(s.rootTag):_;g=((n=r==null?void 0:r.children[0])==null?void 0:n.tagName)??"row"}const f=l.getElementsByTagName(g),u=[];for(let r=0;r<f.length;r++){const i=f[r],p={};for(const e of Array.from(i.attributes)){const d=c[e.name]??e.name;p[d]=o?e.value.trim():e.value}for(const e of Array.from(i.children)){const d=c[e.tagName]??e.tagName,m=e.textContent??"";p[d]=o?m.trim():m}u.push(p)}return u}static stringify(t,s={}){const{rootTag:c="rows",rowTag:o="row",mode:a="element",fieldMap:l={},declaration:x=!0,indent:_=2,nullAs:g="",excludeFields:f=[]}=s,u=" ".repeat(_),h=[];x&&h.push('<?xml version="1.0" encoding="UTF-8"?>'),h.push(`<${c}>`);for(const n of t){const r=Object.entries(n).filter(([i])=>!f.includes(i));if(a==="attribute"){const i=r.map(([p,e])=>{const d=l[p]??p,m=e==null?g:String(e);return`${d}="${this._escAttr(m)}"`}).join(" ");h.push(`${u}<${o}${i?" "+i:""} />`)}else{h.push(`${u}<${o}>`);for(const[i,p]of r){const e=l[i]??i,d=p==null?g:String(p);h.push(`${u}${u}<${e}>${this._escText(d)}</${e}>`)}h.push(`${u}</${o}>`)}}return h.push(`</${c}>`),h.join(`
2
2
  `)}static parseSap(t){var _,g,f,u;const c=new DOMParser().parseFromString(t.trim(),"text/xml"),o={header:{},items:[],returns:[],raw:c},a=c.getElementsByTagName("DOCUMENTHEADER")[0];if(a)for(const h of Array.from(a.children))o.header[h.tagName]=((_=h.textContent)==null?void 0:_.trim())??"";const l=c.getElementsByTagName("RETURN");for(const h of Array.from(l)){const n={};for(const r of Array.from(h.children))n[r.tagName]=((g=r.textContent)==null?void 0:g.trim())??"";o.returns.push(n)}const x=["ACCOUNTGL","ACCOUNTRECEIVABLE","ACCOUNTPAYABLE","ITEMS"];for(const h of x){const n=c.getElementsByTagName(h)[0];if(!n)continue;const r=n.getElementsByTagName("ITEM"),i=r.length>0?Array.from(r):[n];for(const p of i){const e={};for(const d of Array.from(p.children))e[d.tagName]=((f=d.textContent)==null?void 0:f.trim())??"";o.items.push(e)}break}if(o.items.length===0){const h=c.documentElement,n=Array.from(h.children).filter(r=>r.hasAttribute("SEGMENT"));for(const r of n){if(r.tagName==="EDI_DC40")continue;const i={};for(const p of Array.from(r.children))i[p.tagName]=((u=p.textContent)==null?void 0:u.trim())??"";Object.keys(i).length>0&&o.items.push(i)}}return o}static stringifySap(t){const s=['<?xml version="1.0" encoding="UTF-8"?>',"<BAPI_CALL>"];if(t.BAPI_FUNCTION&&s.push(` <FUNCTION>${this._escText(t.BAPI_FUNCTION)}</FUNCTION>`),t.DOCUMENTHEADER&&typeof t.DOCUMENTHEADER=="object"){s.push(" <DOCUMENTHEADER>");for(const[o,a]of Object.entries(t.DOCUMENTHEADER))a!=null&&a!==""&&s.push(` <${o}>${this._escText(String(a))}</${o}>`);s.push(" </DOCUMENTHEADER>")}const c=Object.keys(t).find(o=>Array.isArray(t[o])&&!o.startsWith("_"));if(c){s.push(` <${c}>`);for(const o of t[c]){s.push(" <ITEM>");for(const[a,l]of Object.entries(o))l!=null&&l!==""&&!a.startsWith("_")&&s.push(` <${a}>${this._escText(String(l))}</${a}>`);s.push(" </ITEM>")}s.push(` </${c}>`)}return s.push("</BAPI_CALL>"),s.join(`
3
3
  `)}static stringifySapBatch(t){const s=['<?xml version="1.0" encoding="UTF-8"?>',`<BAPI_BATCH total="${t.documents.length}">`];return t.documents.forEach((c,o)=>{s.push(` <BAPI_CALL seq="${o+1}">`);const a=this.stringifySap(c).split(`
4
4
  `).filter(l=>!l.startsWith("<?xml")).map(l=>" "+l).join(`
package/dist/open-grid.js CHANGED
@@ -1,5 +1,5 @@
1
- import { b as E } from "./OpenGrid-CfZy3e7Q.js";
2
- import { O as w } from "./OpenGrid-CfZy3e7Q.js";
1
+ import { b as E } from "./OpenGrid-Dult64jp.js";
2
+ import { O as w } from "./OpenGrid-Dult64jp.js";
3
3
  class N {
4
4
  constructor(t, s) {
5
5
  this._data = [], this._roots = [], this._expandedKeys = /* @__PURE__ */ new Set(), this._selectedId = null, this._container = typeof t == "string" ? document.querySelector(t) : t, this._opts = {
@@ -119,6 +119,8 @@ export interface ColumnDef<T = any> {
119
119
  resizable?: boolean;
120
120
  hidden?: boolean;
121
121
  frozen?: boolean;
122
+ /** 셀 줄바꿈: true 면 nowrap+ellipsis 대신 여러 줄로 표시(rowHeight 확대와 함께 사용) */
123
+ wrap?: boolean;
122
124
  colSpan?: boolean | ((row: T, rowIndex: number) => boolean);
123
125
  rowSpan?: boolean;
124
126
  children?: ColumnDef<T>[];
@@ -264,6 +266,7 @@ export interface GridOptions<T = any> {
264
266
  pageSize?: number;
265
267
  footer?: FooterDef[];
266
268
  footerPosition?: 'top' | 'bottom';
269
+ tooltips?: boolean;
267
270
  ariaLabel?: string;
268
271
  theme?: string;
269
272
  cssVars?: Record<string, string>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-grid",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "High-performance, framework-agnostic data grid with Vue 3 and React 18 support",
5
5
  "type": "module",
6
6
  "main": "dist/open-grid.cjs",