@j-solution/components 1.9.9 → 2.0.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.
@@ -5,6 +5,10 @@
5
5
  --j-space-3: 12px;
6
6
  --j-space-4: 16px;
7
7
  --j-control-h: 28px;
8
+ /* J-Component 밀도 토큰 override (기본값: packages/themes.css 참조) */
9
+ /* --j-ctl-h-sm: 1.75rem; */ /* 28px - 변경 시 주석 해제 */
10
+ /* --j-ctl-h-md: 2.25rem; */ /* 36px */
11
+ /* --j-ctl-h-lg: 2.5rem; */ /* 40px */
8
12
  --j-grid-header-h: 26px;
9
13
  --j-grid-filter-h: 22px;
10
14
  --j-grid-row-h: 24px;
@@ -3,5 +3,5 @@
3
3
  for (const [t_key, t_val] of t_opts)
4
4
  t_merged[t_key] = t_val;
5
5
  return t_merged;
6
- };,u=t(e.default,[["__scopeId","data-v-d682d093"]]);exports.default=u;
6
+ };,u=t(e.default,[["__scopeId","data-v-0bf35c89"]]);exports.default=u;
7
7
  //# sourceMappingURL=JGrid.vue.cjs.map
@@ -6,7 +6,7 @@ const r = (r_comp, r_opts) => {
6
6
  r_merged[r_key] = r_val;
7
7
  return r_merged;
8
8
  };
9
- const p = /* @__PURE__ */ r(o, [["__scopeId", "data-v-d682d093"]]);
9
+ const p = /* @__PURE__ */ r(o, [["__scopeId", "data-v-0bf35c89"]]);
10
10
  export {
11
11
  p as default
12
12
  };
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const l=require("vue"),F=require("ag-grid-vue3"),M=require("../../lib/utils.cjs"),f=require("ag-grid-community"),N=require("ag-grid-enterprise"),T=l.defineComponent({__name:"JGrid",props:{class:{},rowData:{},columnDefs:{},pagination:{type:Boolean,default:!0},checkbox:{type:Boolean,default:!1},rowNumbers:{type:Boolean,default:!0},floatingFilters:{type:Boolean,default:!0},floatingFilter:{type:Boolean,default:void 0},rowNumberWidth:{default:38},actionButtons:{default:void 0},summaryColumn:{type:Boolean,default:!1},hiddenColumn:{type:Boolean,default:!1},enableGrouping:{type:Boolean,default:!0},enablePivot:{type:Boolean,default:!1},enableExcelExport:{type:Boolean,default:!1},enableTreeData:{type:Boolean,default:!1},getDataPath:{type:Function,default:void 0},autoGroupColumnDef:{default:void 0},selectedRows:{default:()=>[]},columnHover:{type:Boolean,default:!0},enableColumnsToolPanel:{type:Boolean,default:!0},statusBar:{type:Boolean,default:!0},compactFooter:{type:Boolean,default:!1},rowGroupPanelShow:{default:"never"},groupDefaultExpanded:{default:-1},pivotPanelShow:{default:"never"},pivotMode:{type:Boolean,default:!1}},emits:["rowClicked","rowDoubleClicked","cellClicked","selectionChanged","cellValueChanged","gridReady","update:selectedRows"],setup(m,{expose:w,emit:b}){f.ModuleRegistry.registerModules([f.AllCommunityModule,N.AllEnterpriseModule]);const y=f.themeQuartz.withParams({cellHorizontalPaddingScale:1,columnBorder:!0,fontSize:13,headerFontWeight:500,headerVerticalPaddingScale:.45,iconSize:14,rowVerticalPaddingScale:.4}).withParams({browserColorScheme:"light"},"light").withParams({browserColorScheme:"dark"},"dark"),u=l.ref(typeof document<"u"&&document.documentElement.classList.contains("dark")),c=l.ref(null);let d=null;l.watch(u,t=>{c.value?.setAttribute("data-ag-theme-mode",t?"dark":"light")}),l.onMounted(()=>{c.value?.setAttribute("data-ag-theme-mode",u.value?"dark":"light"),d=new MutationObserver(()=>{u.value=document.documentElement.classList.contains("dark")}),d.observe(document.documentElement,{attributes:!0,attributeFilter:["class"]})}),l.onUnmounted(()=>{d?.disconnect()});const e=m,i=b,n=l.ref(null),g=l.ref(null),P=t=>{const r=e.actionButtons||[],s=t.data,v=r.filter(o=>o.show?o.show(s):!0);if(v.length===0)return"";const p=document.createElement("div");return p.className="flex items-center gap-1",v.forEach(o=>{const a=document.createElement("button");a.className=o.styletype==="danger"?"j-action-btn j-action-btn-danger":"j-action-btn",o.tooltip&&(a.title=o.tooltip),o.label?a.textContent=o.label:o.icon&&(o.icon==="pencil"?a.textContent="수정":o.icon==="trash2"||o.icon==="trash"?a.textContent="삭제":o.icon==="eye"?a.textContent="보기":o.icon==="copy"?a.textContent="복사":o.icon==="download"?a.textContent="다운로드":o.icon==="circleX"?a.textContent="비활성화":o.icon==="circleCheckBig"?a.textContent="활성화":a.textContent=o.icon),a.addEventListener("click",R=>{R.stopPropagation(),o.onClick(s)}),p.appendChild(a)}),p},h=l.computed(()=>!e.actionButtons||e.actionButtons.length===0?null:{colId:"actionButtons",headerName:"작업",field:"_actions",width:120,minWidth:80,maxWidth:200,lockPosition:"left",sortable:!1,filter:!1,resizable:!0,suppressNavigable:!0,suppressHeaderMenuButton:!0,cellRenderer:P,cellStyle:{display:"flex",justifyContent:"center",alignItems:"center"}}),C=l.computed(()=>{const t=[];return e.checkbox&&t.push({colId:"rowSelection",headerName:"",width:50,minWidth:50,maxWidth:50,lockPosition:"left",checkboxSelection:!0,headerCheckboxSelection:!0,sortable:!1,filter:!1,resizable:!1,suppressNavigable:!0,suppressHeaderMenuButton:!0,cellStyle:{display:"flex",justifyContent:"center",alignItems:"center"},onCellClicked:r=>{const s=r.event?.target;s&&!s.closest(".ag-checkbox-input-wrapper")&&r.node.setSelected(!r.node.isSelected())}}),h.value&&t.push(h.value),t.push(...e.columnDefs),t}),x=l.computed(()=>{const t=e.floatingFilter??e.floatingFilters;return{theme:y,pagination:e.pagination,defaultColDef:{filter:!0,floatingFilter:t},rowSelection:e.checkbox?"multiple":void 0,suppressRowClickSelection:e.checkbox,rowNumbers:e.rowNumbers?{minWidth:e.rowNumberWidth,width:e.rowNumberWidth}:!1,columnHoverHighlight:e.columnHover,sideBar:e.enableColumnsToolPanel||e.enableGrouping||e.enablePivot?{toolPanels:[{id:"columns",labelDefault:"Columns",labelKey:"columns",iconKey:"columns",toolPanel:"agColumnsToolPanel",toolPanelParams:{suppressRowGroups:!e.enableGrouping,suppressValues:!e.enablePivot,suppressPivots:!e.enablePivot,suppressPivotMode:!e.enablePivot}}],defaultToolPanel:""}:void 0,statusBar:e.statusBar&&!e.compactFooter?{statusPanels:[{statusPanel:"agTotalAndFilteredRowCountComponent",align:"left"},{statusPanel:"agSelectedRowCountComponent",align:"left"},{statusPanel:"agAggregationComponent",align:"right"}]}:void 0,rowGroupPanelShow:e.rowGroupPanelShow!=="never"?e.rowGroupPanelShow:void 0,pivotPanelShow:e.pivotPanelShow!=="never"?e.pivotPanelShow:void 0,pivotMode:e.pivotMode,groupDefaultExpanded:e.groupDefaultExpanded,suppressAggFuncInHeader:!1,treeData:e.enableTreeData||void 0,getDataPath:e.enableTreeData?e.getDataPath||(s=>s.path||[]):void 0,autoGroupColumnDef:e.enableTreeData&&e.autoGroupColumnDef?e.autoGroupColumnDef:void 0}});w({gridApi:n,gridColumnApi:g,exportToExcel:()=>{n.value&&e.enableExcelExport&&n.value.exportDataAsExcel({fileName:"grid-export.xlsx"})}});const k=t=>{n.value=t.api,g.value=t.api,i("gridReady",t)},B=t=>{i("rowClicked",t)},D=t=>{i("rowDoubleClicked",t)},S=t=>{i("cellClicked",t)},E=t=>{i("selectionChanged",t),i("update:selectedRows",t.api.getSelectedRows())},G=t=>{i("cellValueChanged",t)};return l.watch(()=>e.columnDefs,()=>{n.value&&n.value.setGridOption("columnDefs",C.value)},{deep:!0}),l.watch(()=>e.rowData,()=>{n.value&&n.value.setGridOption("rowData",e.rowData)},{deep:!0}),(t,r)=>(l.openBlock(),l.createElementBlock("div",{ref_key:"gridContainerRef",ref:c,class:l.normalizeClass(l.unref(M.cn)("ag-grid-container",e.class))},[l.createVNode(l.unref(F.AgGridVue),{"column-defs":C.value,"row-data":m.rowData,"grid-options":x.value,style:{height:"100%",width:"100%"},onGridReady:k,onRowClicked:B,onRowDoubleClicked:D,onCellClicked:S,onSelectionChanged:E,onCellValueChanged:G},null,8,["column-defs","row-data","grid-options"])],2))}});exports.default=T;
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const l=require("vue"),F=require("ag-grid-vue3"),M=require("../../lib/utils.cjs"),f=require("ag-grid-community"),N=require("ag-grid-enterprise"),T=l.defineComponent({__name:"JGrid",props:{class:{},rowData:{},columnDefs:{},pagination:{type:Boolean,default:!0},checkbox:{type:Boolean,default:!1},rowNumbers:{type:Boolean,default:!0},floatingFilters:{type:Boolean,default:!0},floatingFilter:{type:Boolean,default:void 0},rowNumberWidth:{default:38},actionButtons:{default:void 0},summaryColumn:{type:Boolean,default:!1},hiddenColumn:{type:Boolean,default:!1},enableGrouping:{type:Boolean,default:!0},enablePivot:{type:Boolean,default:!1},enableExcelExport:{type:Boolean,default:!1},enableTreeData:{type:Boolean,default:!1},getDataPath:{type:Function,default:void 0},autoGroupColumnDef:{default:void 0},selectedRows:{default:()=>[]},columnHover:{type:Boolean,default:!0},enableColumnsToolPanel:{type:Boolean,default:!0},statusBar:{type:Boolean,default:!0},compactFooter:{type:Boolean,default:!1},rowGroupPanelShow:{default:"never"},groupDefaultExpanded:{default:-1},pivotPanelShow:{default:"never"},pivotMode:{type:Boolean,default:!1}},emits:["rowClicked","rowDoubleClicked","cellClicked","selectionChanged","cellValueChanged","gridReady","update:selectedRows"],setup(m,{expose:w,emit:b}){f.ModuleRegistry.registerModules([f.AllCommunityModule,N.AllEnterpriseModule]);const y=f.themeQuartz.withParams({cellHorizontalPaddingScale:1,columnBorder:!0,fontSize:13,headerFontWeight:500,headerVerticalPaddingScale:.45,iconSize:14,rowVerticalPaddingScale:.4}).withParams({browserColorScheme:"light"},"light").withParams({browserColorScheme:"dark"},"dark"),u=l.ref(typeof document<"u"&&document.documentElement.classList.contains("dark")),c=l.ref(null);let d=null;l.watch(u,t=>{c.value?.setAttribute("data-ag-theme-mode",t?"dark":"light")}),l.onMounted(()=>{c.value?.setAttribute("data-ag-theme-mode",u.value?"dark":"light"),d=new MutationObserver(()=>{u.value=document.documentElement.classList.contains("dark")}),d.observe(document.documentElement,{attributes:!0,attributeFilter:["class"]})}),l.onUnmounted(()=>{d?.disconnect()});const e=m,i=b,n=l.ref(null),g=l.ref(null),P=t=>{const r=e.actionButtons||[],s=t.data,v=r.filter(o=>o.show?o.show(s):!0);if(v.length===0)return"";const p=document.createElement("div");return p.className="flex items-center gap-1",v.forEach(o=>{const a=document.createElement("button");a.className=o.styletype==="danger"?"j-action-btn j-action-btn-danger":"j-action-btn",o.tooltip&&(a.title=o.tooltip),o.label?a.textContent=o.label:o.icon&&(o.icon==="pencil"?a.textContent="수정":o.icon==="trash2"||o.icon==="trash"?a.textContent="삭제":o.icon==="eye"?a.textContent="보기":o.icon==="copy"?a.textContent="복사":o.icon==="download"?a.textContent="다운로드":o.icon==="circleX"?a.textContent="비활성화":o.icon==="circleCheckBig"?a.textContent="활성화":a.textContent=o.icon),a.addEventListener("click",R=>{R.stopPropagation(),o.onClick(s)}),p.appendChild(a)}),p},h=l.computed(()=>!e.actionButtons||e.actionButtons.length===0?null:{colId:"actionButtons",headerName:"작업",field:"_actions",width:120,minWidth:80,maxWidth:200,lockPosition:"left",sortable:!1,filter:!1,resizable:!0,suppressNavigable:!0,suppressHeaderMenuButton:!0,cellRenderer:P,cellStyle:{display:"flex",justifyContent:"center",alignItems:"center"}}),C=l.computed(()=>{const t=[];return e.checkbox&&t.push({colId:"rowSelection",headerName:"",width:40,minWidth:40,maxWidth:40,lockPosition:"left",checkboxSelection:!0,headerCheckboxSelection:!0,sortable:!1,filter:!1,resizable:!1,suppressNavigable:!0,suppressHeaderMenuButton:!0,cellStyle:{display:"flex",justifyContent:"center",alignItems:"center"},onCellClicked:r=>{const s=r.event?.target;s&&!s.closest(".ag-checkbox-input-wrapper")&&r.node.setSelected(!r.node.isSelected())}}),h.value&&t.push(h.value),t.push(...e.columnDefs),t}),x=l.computed(()=>{const t=e.floatingFilter??e.floatingFilters;return{theme:y,pagination:e.pagination,defaultColDef:{filter:!0,floatingFilter:t},rowSelection:e.checkbox?"multiple":void 0,suppressRowClickSelection:e.checkbox,rowNumbers:e.rowNumbers?{minWidth:e.rowNumberWidth,width:e.rowNumberWidth}:!1,columnHoverHighlight:e.columnHover,sideBar:e.enableColumnsToolPanel||e.enableGrouping||e.enablePivot?{toolPanels:[{id:"columns",labelDefault:"Columns",labelKey:"columns",iconKey:"columns",toolPanel:"agColumnsToolPanel",toolPanelParams:{suppressRowGroups:!e.enableGrouping,suppressValues:!e.enablePivot,suppressPivots:!e.enablePivot,suppressPivotMode:!e.enablePivot}}],defaultToolPanel:""}:void 0,statusBar:e.statusBar&&!e.compactFooter?{statusPanels:[{statusPanel:"agTotalAndFilteredRowCountComponent",align:"left"},{statusPanel:"agSelectedRowCountComponent",align:"left"},{statusPanel:"agAggregationComponent",align:"right"}]}:void 0,rowGroupPanelShow:e.rowGroupPanelShow!=="never"?e.rowGroupPanelShow:void 0,pivotPanelShow:e.pivotPanelShow!=="never"?e.pivotPanelShow:void 0,pivotMode:e.pivotMode,groupDefaultExpanded:e.groupDefaultExpanded,suppressAggFuncInHeader:!1,treeData:e.enableTreeData||void 0,getDataPath:e.enableTreeData?e.getDataPath||(s=>s.path||[]):void 0,autoGroupColumnDef:e.enableTreeData&&e.autoGroupColumnDef?e.autoGroupColumnDef:void 0}});w({gridApi:n,gridColumnApi:g,exportToExcel:()=>{n.value&&e.enableExcelExport&&n.value.exportDataAsExcel({fileName:"grid-export.xlsx"})}});const k=t=>{n.value=t.api,g.value=t.api,i("gridReady",t)},B=t=>{i("rowClicked",t)},D=t=>{i("rowDoubleClicked",t)},S=t=>{i("cellClicked",t)},E=t=>{i("selectionChanged",t),i("update:selectedRows",t.api.getSelectedRows())},G=t=>{i("cellValueChanged",t)};return l.watch(()=>e.columnDefs,()=>{n.value&&n.value.setGridOption("columnDefs",C.value)},{deep:!0}),l.watch(()=>e.rowData,()=>{n.value&&n.value.setGridOption("rowData",e.rowData)},{deep:!0}),(t,r)=>(l.openBlock(),l.createElementBlock("div",{ref_key:"gridContainerRef",ref:c,class:l.normalizeClass(l.unref(M.cn)("ag-grid-container",e.class))},[l.createVNode(l.unref(F.AgGridVue),{"column-defs":C.value,"row-data":m.rowData,"grid-options":x.value,style:{height:"100%",width:"100%"},onGridReady:k,onRowClicked:B,onRowDoubleClicked:D,onCellClicked:S,onSelectionChanged:E,onCellValueChanged:G},null,8,["column-defs","row-data","grid-options"])],2))}});exports.default=T;
2
2
  //# sourceMappingURL=JGrid.vue2.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"JGrid.vue2.cjs","sources":["../../../../src/components/atoms/JGrid.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch, onMounted, onUnmounted } from 'vue'\nimport { AgGridVue } from 'ag-grid-vue3'\nimport { cn } from '@/lib/utils'\nimport type {\n ColDef,\n GridOptions,\n RowClickedEvent,\n CellClickedEvent,\n SelectionChangedEvent,\n CellValueChangedEvent,\n RowDoubleClickedEvent,\n GridReadyEvent,\n ICellRendererParams,\n} from 'ag-grid-community'\nimport { ModuleRegistry, AllCommunityModule, themeQuartz } from 'ag-grid-community'\n// Enterprise 모듈 import (Grouping, Pivot, Excel Export 등)\nimport { AllEnterpriseModule } from 'ag-grid-enterprise'\n\n// AG Grid 모듈 등록 (Community + Enterprise)\nModuleRegistry.registerModules([AllCommunityModule, AllEnterpriseModule])\n\n// ── 공식 권장: data-ag-theme-mode attribute 기반 다크모드 전환 ─────────────────\n// AG Grid v33 공식 패턴:\n// 1. themeQuartz.withParams({...}, 'light').withParams({...}, 'dark') 로 두 모드 정의\n// 2. data-ag-theme-mode attribute 전환으로 모드 스위칭 (theme 객체 재생성 불필요)\n// 3. CSS --ag-* 변수 오버라이드로 앱 CSS 변수 참조 (getCSSVar 우회 불필요)\nconst jTheme = themeQuartz\n .withParams({\n // 공통 사이즈 (라이트/다크 모두 적용)\n cellHorizontalPaddingScale: 1,\n columnBorder: true,\n fontSize: 13,\n headerFontWeight: 500,\n headerVerticalPaddingScale: 0.45,\n iconSize: 14,\n rowVerticalPaddingScale: 0.4,\n })\n .withParams({\n browserColorScheme: 'light',\n }, 'light')\n .withParams({\n browserColorScheme: 'dark',\n }, 'dark')\n\n// JLIS는 .dark class 기반 다크모드이므로 MutationObserver로 감지 후 attribute 동기화\nconst isDark = ref(typeof document !== 'undefined' && document.documentElement.classList.contains('dark'))\nconst gridContainerRef = ref<HTMLElement | null>(null)\nlet darkObserver: MutationObserver | null = null\n\n// isDark 변경 → data-ag-theme-mode 동기화 (AG Grid가 내부적으로 반응)\nwatch(isDark, (dark) => {\n gridContainerRef.value?.setAttribute('data-ag-theme-mode', dark ? 'dark' : 'light')\n})\n\nonMounted(() => {\n // 초기 attribute 설정\n gridContainerRef.value?.setAttribute('data-ag-theme-mode', isDark.value ? 'dark' : 'light')\n\n // 이후 변경 감지\n darkObserver = new MutationObserver(() => {\n isDark.value = document.documentElement.classList.contains('dark')\n })\n darkObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] })\n})\n\nonUnmounted(() => {\n darkObserver?.disconnect()\n})\n\n/**\n * Action Button 정의 타입\n */\nexport type ActionButton = {\n /** 버튼 아이콘 이름 (lucide) - label이 없을 때 기본 텍스트 생성에 사용 */\n icon?: string\n /** 버튼 라벨 */\n label?: string\n /** 툴팁 텍스트 */\n tooltip?: string\n /** 버튼 클릭 핸들러 (rowData 전달) */\n onClick: (rowData: any) => void\n /** 버튼 스타일 타입 */\n styletype?: 'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'danger'\n /** 조건부 표시 함수 (rowData를 받아 boolean 반환) */\n show?: (rowData: any) => boolean\n}\n\nconst props = withDefaults(\n defineProps<{\n /** 추가 클래스 (외부 커스터마이징용) */\n class?: string\n /** 그리드에 표시할 데이터 배열 */\n rowData: any[]\n /** 컬럼 정의 배열 */\n columnDefs: ColDef[]\n /** 페이지네이션 활성화 여부 */\n pagination?: boolean\n /** 체크박스 선택 활성화 여부 */\n checkbox?: boolean\n /** 행 번호 표시 여부 (Enterprise) */\n rowNumbers?: boolean\n /** 플로팅 필터 표시 여부 */\n floatingFilters?: boolean\n /** 플로팅 필터 표시 여부 (하위호환 alias) */\n floatingFilter?: boolean\n /** 행 번호 컬럼 너비(px) */\n rowNumberWidth?: number\n /** 행별 액션 버튼 목록 */\n actionButtons?: ActionButton[]\n /** 요약 컬럼 표시 여부 */\n summaryColumn?: boolean\n /** 숨김 컬럼 관리 활성화 여부 */\n hiddenColumn?: boolean\n /** 그룹핑 기능 활성화 여부 (Enterprise) */\n enableGrouping?: boolean\n /** 피벗 기능 활성화 여부 (Enterprise) */\n enablePivot?: boolean\n /** Excel 내보내기 기능 활성화 여부 (Enterprise) */\n enableExcelExport?: boolean\n /** Tree Data 기능 활성화 여부 (Enterprise) */\n enableTreeData?: boolean\n /** Tree Data 계층 경로를 반환하는 함수 */\n getDataPath?: (data: any) => (string | number)[]\n /** Tree Data 그룹 컬럼 정의 */\n autoGroupColumnDef?: ColDef\n /** 선택된 행 데이터 배열 (v-model:selected-rows) */\n selectedRows?: any[]\n /** 컬럼 호버 하이라이트 활성화 여부 */\n columnHover?: boolean\n /** Columns Tool Panel 활성화 여부 (Enterprise) */\n enableColumnsToolPanel?: boolean\n /** Status Bar 활성화 여부 (Enterprise) */\n statusBar?: boolean\n /** 간소화된 Footer 모드 (Status Bar 제거, Pagination만 표시) */\n compactFooter?: boolean\n /** Row Group Panel 표시 여부 ('always' | 'onlyWhenGrouping' | 'never') */\n rowGroupPanelShow?: 'always' | 'onlyWhenGrouping' | 'never'\n /** 그룹 기본 확장 레벨 (-1: 모두 닫힘, 0: 첫 레벨만, 1: 2레벨까지...) */\n groupDefaultExpanded?: number\n /** Pivot Mode Panel 표시 여부 */\n pivotPanelShow?: 'always' | 'onlyWhenPivoting' | 'never'\n /** Pivot Mode 활성화 여부 */\n pivotMode?: boolean\n }>(),\n {\n pagination: true,\n checkbox: false,\n rowNumbers: true,\n floatingFilters: true,\n floatingFilter: undefined,\n rowNumberWidth: 38,\n actionButtons: undefined,\n summaryColumn: false,\n hiddenColumn: false,\n enableGrouping: true,\n enablePivot: false,\n enableExcelExport: false,\n enableTreeData: false,\n getDataPath: undefined,\n autoGroupColumnDef: undefined,\n selectedRows: () => [],\n columnHover: true,\n enableColumnsToolPanel: true,\n statusBar: true,\n compactFooter: false,\n rowGroupPanelShow: 'never',\n groupDefaultExpanded: -1,\n pivotPanelShow: 'never',\n pivotMode: false,\n },\n)\n\nconst emit = defineEmits<{\n /** 행 클릭 이벤트 */\n rowClicked: [event: RowClickedEvent]\n /** 행 더블클릭 이벤트 */\n rowDoubleClicked: [event: RowDoubleClickedEvent]\n /** 셀 클릭 이벤트 */\n cellClicked: [event: CellClickedEvent]\n /** 선택 변경 이벤트 (체크박스 등) */\n selectionChanged: [event: SelectionChangedEvent]\n /** 셀 값 변경 이벤트 */\n cellValueChanged: [event: CellValueChangedEvent]\n /** 그리드 준비 완료 이벤트 */\n gridReady: [event: GridReadyEvent]\n /** 선택된 행 변경 이벤트 (v-model:selected-rows) */\n 'update:selectedRows': [rows: any[]]\n}>()\n\n// ag-Grid 인스턴스 참조\nconst gridApi = ref<any>(null)\nconst gridColumnApi = ref<any>(null)\n\n// Action Buttons Cell Renderer - 함수형으로 DOM 직접 생성\nconst ActionButtonsCellRenderer = (params: ICellRendererParams) => {\n const buttons = props.actionButtons || []\n const rowData = params.data\n \n // 표시할 버튼 필터링\n const visibleButtons = buttons.filter(btn => {\n if (btn.show) {\n return btn.show(rowData)\n }\n return true\n })\n \n if (visibleButtons.length === 0) {\n return ''\n }\n \n // 컨테이너 div 생성\n const container = document.createElement('div')\n container.className = 'flex items-center gap-1'\n \n // 각 버튼 생성\n visibleButtons.forEach((btn) => {\n const button = document.createElement('button')\n button.className = btn.styletype === 'danger' ? 'j-action-btn j-action-btn-danger' : 'j-action-btn'\n \n // tooltip\n if (btn.tooltip) {\n button.title = btn.tooltip\n }\n \n // 라벨 추가 (텍스트 버튼)\n if (btn.label) {\n button.textContent = btn.label\n } else if (btn.icon) {\n // 라벨이 없으면 기본 텍스트 생성\n if (btn.icon === 'pencil') {\n button.textContent = '수정'\n } else if (btn.icon === 'trash2' || btn.icon === 'trash') {\n button.textContent = '삭제'\n } else if (btn.icon === 'eye') {\n button.textContent = '보기'\n } else if (btn.icon === 'copy') {\n button.textContent = '복사'\n } else if (btn.icon === 'download') {\n button.textContent = '다운로드'\n } else if (btn.icon === 'circleX') {\n button.textContent = '비활성화'\n } else if (btn.icon === 'circleCheckBig') {\n button.textContent = '활성화'\n } else {\n button.textContent = btn.icon\n }\n }\n \n // 클릭 이벤트\n button.addEventListener('click', (e) => {\n e.stopPropagation()\n btn.onClick(rowData)\n })\n \n container.appendChild(button)\n })\n \n return container\n}\n\n// Action Buttons 컬럼 정의\nconst actionButtonsColumn = computed<ColDef | null>(() => {\n if (!props.actionButtons || props.actionButtons.length === 0) {\n return null\n }\n \n return {\n colId: 'actionButtons',\n headerName: '작업',\n field: '_actions',\n width: 120,\n minWidth: 80,\n maxWidth: 200,\n lockPosition: 'left' as const,\n sortable: false,\n filter: false,\n resizable: true,\n suppressNavigable: true,\n suppressHeaderMenuButton: true,\n cellRenderer: ActionButtonsCellRenderer,\n cellStyle: { display: 'flex', justifyContent: 'center', alignItems: 'center' },\n }\n})\n\n// checkbox 활성화 및 추가 컬럼 처리\nconst processedColumnDefs = computed(() => {\n const columns: ColDef[] = []\n \n // 1. Checkbox 컬럼 (최우선)\n if (props.checkbox) {\n columns.push({\n colId: 'rowSelection',\n headerName: '',\n // field와 valueGetter 제거 - AG Grid 공식 방식\n width: 50,\n minWidth: 50,\n maxWidth: 50,\n lockPosition: 'left' as const,\n checkboxSelection: true,\n headerCheckboxSelection: true,\n sortable: false,\n filter: false,\n resizable: false,\n suppressNavigable: true,\n suppressHeaderMenuButton: true,\n cellStyle: { display: 'flex', justifyContent: 'center', alignItems: 'center' },\n // 체크박스 셀 클릭 시 토글 (checkbox 아이콘 클릭은 AG Grid 기본 처리)\n onCellClicked: (params: CellClickedEvent) => {\n const target = (params.event as MouseEvent)?.target as HTMLElement\n if (target && !target.closest('.ag-checkbox-input-wrapper')) {\n params.node.setSelected(!params.node.isSelected())\n }\n },\n })\n }\n \n // 2. Action Buttons 컬럼\n if (actionButtonsColumn.value) {\n columns.push(actionButtonsColumn.value)\n }\n \n // 3. 사용자 정의 컬럼들 (Row Numbers는 AG Grid가 자동으로 추가)\n columns.push(...props.columnDefs)\n \n return columns\n})\n\n// Grid 옵션 설정\nconst gridOptions = computed<GridOptions>(() => {\n const useFloatingFilters = props.floatingFilter ?? props.floatingFilters\n const options: GridOptions = {\n theme: jTheme,\n pagination: props.pagination,\n defaultColDef: {\n filter: true,\n floatingFilter: useFloatingFilters,\n },\n rowSelection: props.checkbox ? 'multiple' : undefined,\n // 체크박스 모드일 때 row 클릭으로 선택이 변경되지 않도록 설정\n // 체크박스만으로 선택을 제어하도록 함\n suppressRowClickSelection: props.checkbox,\n\n // Row Numbers (Enterprise) - AG Grid 표준 방식\n rowNumbers: props.rowNumbers\n ? ({\n minWidth: props.rowNumberWidth,\n width: props.rowNumberWidth,\n } as any)\n : false,\n\n // Column Hover Highlight\n columnHoverHighlight: props.columnHover,\n\n // Enterprise 기능 옵션\n sideBar: props.enableColumnsToolPanel || props.enableGrouping || props.enablePivot ? {\n toolPanels: [\n {\n id: 'columns',\n labelDefault: 'Columns',\n labelKey: 'columns',\n iconKey: 'columns',\n toolPanel: 'agColumnsToolPanel',\n toolPanelParams: {\n suppressRowGroups: !props.enableGrouping,\n suppressValues: !props.enablePivot,\n suppressPivots: !props.enablePivot,\n suppressPivotMode: !props.enablePivot,\n },\n },\n ],\n defaultToolPanel: '', // 초기에는 접힌 상태\n } : undefined,\n\n // Status Bar (Enterprise)\n // compactFooter 모드에서는 Status Bar 비활성화\n statusBar: (props.statusBar && !props.compactFooter) ? {\n statusPanels: [\n { statusPanel: 'agTotalAndFilteredRowCountComponent', align: 'left' as const },\n { statusPanel: 'agSelectedRowCountComponent', align: 'left' as const },\n { statusPanel: 'agAggregationComponent', align: 'right' as const },\n ],\n } : undefined,\n\n // Row Group Panel 설정 (Enterprise) - 그리드 상단에 드래그 영역 표시\n rowGroupPanelShow: props.rowGroupPanelShow !== 'never' ? props.rowGroupPanelShow : undefined,\n\n // Pivot Panel 설정 (Enterprise) - 피벗 모드용 드래그 영역\n pivotPanelShow: props.pivotPanelShow !== 'never' ? props.pivotPanelShow : undefined,\n \n // Pivot Mode 활성화\n pivotMode: props.pivotMode,\n\n // 그룹핑 기본 설정\n groupDefaultExpanded: props.groupDefaultExpanded,\n suppressAggFuncInHeader: false,\n\n // Tree Data 설정 (Enterprise)\n treeData: props.enableTreeData || undefined,\n getDataPath: props.enableTreeData\n ? (props.getDataPath || ((data: any) => data.path || []))\n : undefined,\n autoGroupColumnDef: props.enableTreeData && props.autoGroupColumnDef\n ? props.autoGroupColumnDef\n : undefined,\n }\n\n return options\n})\n\n// Excel 내보내기 함수 (외부에서 사용 가능하도록 expose)\nconst exportToExcel = () => {\n if (gridApi.value && props.enableExcelExport) {\n gridApi.value.exportDataAsExcel({\n fileName: 'grid-export.xlsx',\n })\n }\n}\n\n// 그리드 API를 외부에 노출\ndefineExpose({\n gridApi,\n gridColumnApi,\n exportToExcel,\n})\n\n// Grid ready 이벤트 핸들러\nconst onGridReady = (params: GridReadyEvent) => {\n gridApi.value = params.api\n gridColumnApi.value = params.api // v34에서 columnApi는 deprecated\n emit('gridReady', params)\n}\n\n// 행 클릭 이벤트 핸들러\nconst onRowClicked = (event: RowClickedEvent) => {\n emit('rowClicked', event)\n}\n\n// 행 더블클릭 이벤트 핸들러\nconst onRowDoubleClicked = (event: RowDoubleClickedEvent) => {\n emit('rowDoubleClicked', event)\n}\n\n// 셀 클릭 이벤트 핸들러\nconst onCellClicked = (event: CellClickedEvent) => {\n emit('cellClicked', event)\n}\n\n// 선택 변경 이벤트 핸들러\nconst onSelectionChanged = (event: SelectionChangedEvent) => {\n emit('selectionChanged', event)\n emit('update:selectedRows', event.api.getSelectedRows())\n}\n\n// 셀 값 변경 이벤트 핸들러\nconst onCellValueChanged = (event: CellValueChangedEvent) => {\n emit('cellValueChanged', event)\n}\n\n// columnDefs 변경 감지\nwatch(\n () => props.columnDefs,\n () => {\n if (gridApi.value) {\n gridApi.value.setGridOption('columnDefs', processedColumnDefs.value)\n }\n },\n { deep: true },\n)\n\n// rowData 변경 감지\nwatch(\n () => props.rowData,\n () => {\n if (gridApi.value) {\n gridApi.value.setGridOption('rowData', props.rowData)\n }\n },\n { deep: true },\n)\n</script>\n\n<template>\n <div ref=\"gridContainerRef\" :class=\"cn('ag-grid-container', props.class)\">\n <AgGridVue\n :column-defs=\"processedColumnDefs\"\n :row-data=\"rowData\"\n :grid-options=\"gridOptions\"\n style=\"height: 100%; width: 100%\"\n @grid-ready=\"onGridReady\"\n @row-clicked=\"onRowClicked\"\n @row-double-clicked=\"onRowDoubleClicked\"\n @cell-clicked=\"onCellClicked\"\n @selection-changed=\"onSelectionChanged\"\n @cell-value-changed=\"onCellValueChanged\"\n />\n </div>\n</template>\n\n<style scoped>\n.ag-grid-container {\n width: 100%;\n height: 100%;\n\n /*\n * App CSS 변수 → AG Grid CSS 변수 브릿지\n * CSS 변수는 런타임 해소 → .dark 클래스 전환 시 라이트/다크 자동 대응\n * AG Grid v33 공식 CSS variable 명칭 사용\n */\n --ag-background-color: hsl(var(--background));\n --ag-foreground-color: hsl(var(--foreground));\n --ag-border-color: hsl(var(--border));\n --ag-header-background-color: hsl(var(--muted));\n --ag-header-foreground-color: hsl(var(--foreground));\n --ag-odd-row-background-color: hsl(var(--card));\n --ag-row-hover-color: hsl(var(--accent));\n --ag-selected-row-background-color: hsl(var(--primary) / 0.12);\n --ag-accent-color: hsl(var(--primary));\n --ag-header-height: var(--j-grid-header-h);\n --ag-row-height: var(--j-grid-row-h);\n}\n\n/* ============================================\n COMPACT FOOTER: Status Bar + Pagination 통합\n ============================================ */\n\n/* Status Bar 높이 줄이기 */\n:deep(.ag-status-bar) {\n min-height: var(--j-grid-footer-h) !important;\n height: var(--j-grid-footer-h) !important;\n padding: 0 12px !important;\n border-top: 1px solid var(--ag-border-color);\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-size: 0.8125rem;\n}\n\n/* Status Bar 컴포넌트들 높이 조정 */\n:deep(.ag-status-bar-left),\n:deep(.ag-status-bar-center),\n:deep(.ag-status-bar-right) {\n height: var(--j-grid-footer-h);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n/* Status Bar 패널들 간격 조정 */\n:deep(.ag-status-panel) {\n padding: 0;\n height: var(--j-grid-footer-h);\n display: flex;\n align-items: center;\n}\n\n/* Pagination Panel 높이 줄이기 */\n:deep(.ag-paging-panel) {\n min-height: var(--j-grid-footer-h) !important;\n height: var(--j-grid-footer-h) !important;\n padding: 0 12px !important;\n border-top: 1px solid var(--ag-border-color);\n font-size: 0.8125rem;\n}\n\n/* Pagination 컴포넌트들 높이 조정 */\n:deep(.ag-paging-page-size),\n:deep(.ag-paging-row-summary-panel) {\n height: var(--j-grid-footer-h);\n display: flex;\n align-items: center;\n}\n\n/* Page Size Selector 높이 조정 */\n:deep(.ag-paging-page-size .ag-picker-field) {\n height: 24px;\n min-height: 24px;\n}\n\n:deep(.ag-paging-page-size .ag-picker-field-wrapper) {\n height: 24px;\n padding: 0 4px;\n}\n\n/* Pagination 버튼들 높이 조정 */\n:deep(.ag-paging-button) {\n height: 24px;\n width: 24px;\n padding: 2px;\n}\n\n/* Pagination 버튼 래퍼 */\n:deep(.ag-paging-button-wrapper) {\n height: var(--j-grid-footer-h);\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n/* Row summary 텍스트 */\n:deep(.ag-paging-row-summary-panel-number) {\n line-height: var(--j-grid-footer-h);\n}\n\n/* ========================================\n 패턴 9: AG-Grid 스타일 향상\n ======================================== */\n\n:deep(.ag-root-wrapper) {\n border: 1px solid hsl(var(--border));\n border-radius: 0.375rem;\n}\n\n/* ── Header ──────────────────────────────────────────────────────────── */\n:deep(.ag-header) {\n border-bottom: 1px solid hsl(var(--border));\n font-weight: 500;\n}\n\n:deep(.ag-header-row:not(.ag-floating-filter)) {\n min-height: var(--j-grid-header-h) !important;\n height: var(--j-grid-header-h) !important;\n}\n\n:deep(.ag-header-cell) {\n color: hsl(var(--foreground));\n font-size: 0.75rem;\n padding: 0 0.5rem;\n display: flex;\n align-items: center;\n}\n\n/* Floating Filter: 헤더와 명확히 구분되는 입력 영역 */\n:deep(.ag-header-row.ag-floating-filter) {\n min-height: var(--j-grid-filter-h) !important;\n height: var(--j-grid-filter-h) !important;\n background-color: hsl(var(--card));\n border-top: 1px solid hsl(var(--border) / 0.5);\n}\n\n:deep(.ag-floating-filter-body) {\n min-height: var(--j-grid-filter-h);\n}\n\n:deep(.ag-floating-filter-body input),\n:deep(.ag-floating-filter-body .ag-input-field-input),\n:deep(.ag-floating-filter-body .ag-picker-field-wrapper) {\n height: 20px !important;\n min-height: 20px !important;\n font-size: 0.75rem;\n background-color: hsl(var(--background)) !important;\n border: 1px solid hsl(var(--border)) !important;\n border-radius: 3px !important;\n padding: 0 4px !important;\n color: hsl(var(--foreground)) !important;\n}\n\n:deep(.ag-floating-filter-body input:focus),\n:deep(.ag-floating-filter-body .ag-input-field-input:focus) {\n border-color: hsl(var(--primary)) !important;\n outline: none !important;\n}\n\n/* ── Rows ────────────────────────────────────────────────────────────── */\n:deep(.ag-row) {\n min-height: var(--j-grid-row-h) !important;\n height: var(--j-grid-row-h) !important;\n transition: background-color 0.15s ease, box-shadow 0.15s ease;\n cursor: pointer;\n}\n\n/* 선택된 행: 왼쪽 accent 기둥으로 명확한 시각적 선택 표시 (라이트/다크 모두) */\n:deep(.ag-row-selected) {\n box-shadow: inset 3px 0 0 hsl(var(--primary));\n}\n\n/* ── Cells ───────────────────────────────────────────────────────────── */\n:deep(.ag-cell) {\n line-height: 1.2;\n padding: 0 0.5rem;\n font-size: 0.75rem;\n border-bottom: 1px solid hsl(var(--border) / 0.5);\n}\n\n:deep(.ag-cell-wrapper) {\n align-items: center;\n}\n\n:deep(.ag-cell-value) {\n display: flex;\n align-items: center;\n min-height: 100%;\n}\n\n/* 행 클릭 시 row number 셀에 보이는 강한 포커스 음영 제거 */\n:deep(.ag-cell.ag-row-number-cell.ag-cell-focus),\n:deep(.ag-cell.ag-row-number-cell:focus-within) {\n box-shadow: none !important;\n outline: none !important;\n}\n\n/* 셀 포커스 */\n:deep(.ag-cell.ag-cell-focus) {\n box-shadow: inset 0 0 0 1px hsl(var(--ring) / 0.5) !important;\n}\n\n/* 인라인 편집 셀 */\n:deep(.ag-cell-inline-editing) {\n padding: 0 !important;\n border: none !important;\n box-shadow: inset 0 0 0 1px hsl(var(--primary)) !important;\n background-color: hsl(var(--background)) !important;\n}\n\n:deep(.ag-cell-inline-editing .ag-cell-editor),\n:deep(.ag-cell-inline-editing .ag-text-field-input-wrapper),\n:deep(.ag-cell-inline-editing .ag-input-field) {\n height: 100% !important;\n border: none !important;\n outline: none !important;\n background: transparent !important;\n box-shadow: none !important;\n}\n\n:deep(.ag-cell-inline-editing input) {\n height: 100% !important;\n width: 100% !important;\n padding: 0 0.5rem !important;\n border: none !important;\n outline: none !important;\n background: transparent !important;\n box-shadow: none !important;\n font-size: 0.75rem !important;\n color: hsl(var(--foreground)) !important;\n}\n\n/* ── Checkbox 셀 중앙정렬 ────────────────────────────────────────────── */\n:deep(.ag-cell-wrapper:has(.ag-selection-checkbox)) {\n justify-content: center;\n}\n\n:deep(.ag-header-cell:has(.ag-header-select-all) .ag-header-cell-comp-wrapper) {\n justify-content: center;\n}\n\n/* ── Action Buttons (JS 렌더러 — CSS variable 브릿지) ─────────────────── */\n:deep(.j-action-btn) {\n padding: 1px 8px;\n font-size: 0.75rem;\n border: 1px solid hsl(var(--border));\n border-radius: 3px;\n background-color: hsl(var(--card));\n color: hsl(var(--foreground));\n cursor: pointer;\n transition: background-color 0.15s ease;\n white-space: nowrap;\n}\n\n:deep(.j-action-btn:hover) {\n background-color: hsl(var(--accent));\n}\n\n:deep(.j-action-btn-danger) {\n border-color: hsl(var(--destructive) / 0.4);\n background-color: hsl(var(--destructive) / 0.06);\n color: hsl(var(--destructive));\n}\n\n:deep(.j-action-btn-danger:hover) {\n background-color: hsl(var(--destructive) / 0.15);\n}\n\n/* ── Pagination ──────────────────────────────────────────────────────── */\n:deep(.ag-paging-panel) {\n border-top: 1px solid hsl(var(--border));\n}\n</style>\n"],"names":["ModuleRegistry","AllCommunityModule","AllEnterpriseModule","jTheme","themeQuartz","isDark","ref","gridContainerRef","darkObserver","watch","dark","onMounted","onUnmounted","props","__props","emit","__emit","gridApi","gridColumnApi","ActionButtonsCellRenderer","params","buttons","rowData","visibleButtons","btn","container","button","e","actionButtonsColumn","computed","processedColumnDefs","columns","target","gridOptions","useFloatingFilters","data","__expose","onGridReady","onRowClicked","event","onRowDoubleClicked","onCellClicked","onSelectionChanged","onCellValueChanged","_createElementBlock","_normalizeClass","_unref","cn","_createVNode","AgGridVue"],"mappings":"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"}
1
+ {"version":3,"file":"JGrid.vue2.cjs","sources":["../../../../src/components/atoms/JGrid.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch, onMounted, onUnmounted } from 'vue'\nimport { AgGridVue } from 'ag-grid-vue3'\nimport { cn } from '@/lib/utils'\nimport type {\n ColDef,\n GridOptions,\n RowClickedEvent,\n CellClickedEvent,\n SelectionChangedEvent,\n CellValueChangedEvent,\n RowDoubleClickedEvent,\n GridReadyEvent,\n ICellRendererParams,\n} from 'ag-grid-community'\nimport { ModuleRegistry, AllCommunityModule, themeQuartz } from 'ag-grid-community'\n// Enterprise 모듈 import (Grouping, Pivot, Excel Export 등)\nimport { AllEnterpriseModule } from 'ag-grid-enterprise'\n\n// AG Grid 모듈 등록 (Community + Enterprise)\nModuleRegistry.registerModules([AllCommunityModule, AllEnterpriseModule])\n\n// ── 공식 권장: data-ag-theme-mode attribute 기반 다크모드 전환 ─────────────────\n// AG Grid v33 공식 패턴:\n// 1. themeQuartz.withParams({...}, 'light').withParams({...}, 'dark') 로 두 모드 정의\n// 2. data-ag-theme-mode attribute 전환으로 모드 스위칭 (theme 객체 재생성 불필요)\n// 3. CSS --ag-* 변수 오버라이드로 앱 CSS 변수 참조 (getCSSVar 우회 불필요)\nconst jTheme = themeQuartz\n .withParams({\n // 공통 사이즈 (라이트/다크 모두 적용)\n cellHorizontalPaddingScale: 1,\n columnBorder: true,\n fontSize: 13,\n headerFontWeight: 500,\n headerVerticalPaddingScale: 0.45,\n iconSize: 14,\n rowVerticalPaddingScale: 0.4,\n })\n .withParams({\n browserColorScheme: 'light',\n }, 'light')\n .withParams({\n browserColorScheme: 'dark',\n }, 'dark')\n\n// JLIS는 .dark class 기반 다크모드이므로 MutationObserver로 감지 후 attribute 동기화\nconst isDark = ref(typeof document !== 'undefined' && document.documentElement.classList.contains('dark'))\nconst gridContainerRef = ref<HTMLElement | null>(null)\nlet darkObserver: MutationObserver | null = null\n\n// isDark 변경 → data-ag-theme-mode 동기화 (AG Grid가 내부적으로 반응)\nwatch(isDark, (dark) => {\n gridContainerRef.value?.setAttribute('data-ag-theme-mode', dark ? 'dark' : 'light')\n})\n\nonMounted(() => {\n // 초기 attribute 설정\n gridContainerRef.value?.setAttribute('data-ag-theme-mode', isDark.value ? 'dark' : 'light')\n\n // 이후 변경 감지\n darkObserver = new MutationObserver(() => {\n isDark.value = document.documentElement.classList.contains('dark')\n })\n darkObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] })\n})\n\nonUnmounted(() => {\n darkObserver?.disconnect()\n})\n\n/**\n * Action Button 정의 타입\n */\nexport type ActionButton = {\n /** 버튼 아이콘 이름 (lucide) - label이 없을 때 기본 텍스트 생성에 사용 */\n icon?: string\n /** 버튼 라벨 */\n label?: string\n /** 툴팁 텍스트 */\n tooltip?: string\n /** 버튼 클릭 핸들러 (rowData 전달) */\n onClick: (rowData: any) => void\n /** 버튼 스타일 타입 */\n styletype?: 'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'danger'\n /** 조건부 표시 함수 (rowData를 받아 boolean 반환) */\n show?: (rowData: any) => boolean\n}\n\nconst props = withDefaults(\n defineProps<{\n /** 추가 클래스 (외부 커스터마이징용) */\n class?: string\n /** 그리드에 표시할 데이터 배열 */\n rowData: any[]\n /** 컬럼 정의 배열 */\n columnDefs: ColDef[]\n /** 페이지네이션 활성화 여부 */\n pagination?: boolean\n /** 체크박스 선택 활성화 여부 */\n checkbox?: boolean\n /** 행 번호 표시 여부 (Enterprise) */\n rowNumbers?: boolean\n /** 플로팅 필터 표시 여부 */\n floatingFilters?: boolean\n /** 플로팅 필터 표시 여부 (하위호환 alias) */\n floatingFilter?: boolean\n /** 행 번호 컬럼 너비(px) */\n rowNumberWidth?: number\n /** 행별 액션 버튼 목록 */\n actionButtons?: ActionButton[]\n /** 요약 컬럼 표시 여부 */\n summaryColumn?: boolean\n /** 숨김 컬럼 관리 활성화 여부 */\n hiddenColumn?: boolean\n /** 그룹핑 기능 활성화 여부 (Enterprise) */\n enableGrouping?: boolean\n /** 피벗 기능 활성화 여부 (Enterprise) */\n enablePivot?: boolean\n /** Excel 내보내기 기능 활성화 여부 (Enterprise) */\n enableExcelExport?: boolean\n /** Tree Data 기능 활성화 여부 (Enterprise) */\n enableTreeData?: boolean\n /** Tree Data 계층 경로를 반환하는 함수 */\n getDataPath?: (data: any) => (string | number)[]\n /** Tree Data 그룹 컬럼 정의 */\n autoGroupColumnDef?: ColDef\n /** 선택된 행 데이터 배열 (v-model:selected-rows) */\n selectedRows?: any[]\n /** 컬럼 호버 하이라이트 활성화 여부 */\n columnHover?: boolean\n /** Columns Tool Panel 활성화 여부 (Enterprise) */\n enableColumnsToolPanel?: boolean\n /** Status Bar 활성화 여부 (Enterprise) */\n statusBar?: boolean\n /** 간소화된 Footer 모드 (Status Bar 제거, Pagination만 표시) */\n compactFooter?: boolean\n /** Row Group Panel 표시 여부 ('always' | 'onlyWhenGrouping' | 'never') */\n rowGroupPanelShow?: 'always' | 'onlyWhenGrouping' | 'never'\n /** 그룹 기본 확장 레벨 (-1: 모두 닫힘, 0: 첫 레벨만, 1: 2레벨까지...) */\n groupDefaultExpanded?: number\n /** Pivot Mode Panel 표시 여부 */\n pivotPanelShow?: 'always' | 'onlyWhenPivoting' | 'never'\n /** Pivot Mode 활성화 여부 */\n pivotMode?: boolean\n }>(),\n {\n pagination: true,\n checkbox: false,\n rowNumbers: true,\n floatingFilters: true,\n floatingFilter: undefined,\n rowNumberWidth: 38,\n actionButtons: undefined,\n summaryColumn: false,\n hiddenColumn: false,\n enableGrouping: true,\n enablePivot: false,\n enableExcelExport: false,\n enableTreeData: false,\n getDataPath: undefined,\n autoGroupColumnDef: undefined,\n selectedRows: () => [],\n columnHover: true,\n enableColumnsToolPanel: true,\n statusBar: true,\n compactFooter: false,\n rowGroupPanelShow: 'never',\n groupDefaultExpanded: -1,\n pivotPanelShow: 'never',\n pivotMode: false,\n },\n)\n\nconst emit = defineEmits<{\n /** 행 클릭 이벤트 */\n rowClicked: [event: RowClickedEvent]\n /** 행 더블클릭 이벤트 */\n rowDoubleClicked: [event: RowDoubleClickedEvent]\n /** 셀 클릭 이벤트 */\n cellClicked: [event: CellClickedEvent]\n /** 선택 변경 이벤트 (체크박스 등) */\n selectionChanged: [event: SelectionChangedEvent]\n /** 셀 값 변경 이벤트 */\n cellValueChanged: [event: CellValueChangedEvent]\n /** 그리드 준비 완료 이벤트 */\n gridReady: [event: GridReadyEvent]\n /** 선택된 행 변경 이벤트 (v-model:selected-rows) */\n 'update:selectedRows': [rows: any[]]\n}>()\n\n// ag-Grid 인스턴스 참조\nconst gridApi = ref<any>(null)\nconst gridColumnApi = ref<any>(null)\n\n// Action Buttons Cell Renderer - 함수형으로 DOM 직접 생성\nconst ActionButtonsCellRenderer = (params: ICellRendererParams) => {\n const buttons = props.actionButtons || []\n const rowData = params.data\n \n // 표시할 버튼 필터링\n const visibleButtons = buttons.filter(btn => {\n if (btn.show) {\n return btn.show(rowData)\n }\n return true\n })\n \n if (visibleButtons.length === 0) {\n return ''\n }\n \n // 컨테이너 div 생성\n const container = document.createElement('div')\n container.className = 'flex items-center gap-1'\n \n // 각 버튼 생성\n visibleButtons.forEach((btn) => {\n const button = document.createElement('button')\n button.className = btn.styletype === 'danger' ? 'j-action-btn j-action-btn-danger' : 'j-action-btn'\n \n // tooltip\n if (btn.tooltip) {\n button.title = btn.tooltip\n }\n \n // 라벨 추가 (텍스트 버튼)\n if (btn.label) {\n button.textContent = btn.label\n } else if (btn.icon) {\n // 라벨이 없으면 기본 텍스트 생성\n if (btn.icon === 'pencil') {\n button.textContent = '수정'\n } else if (btn.icon === 'trash2' || btn.icon === 'trash') {\n button.textContent = '삭제'\n } else if (btn.icon === 'eye') {\n button.textContent = '보기'\n } else if (btn.icon === 'copy') {\n button.textContent = '복사'\n } else if (btn.icon === 'download') {\n button.textContent = '다운로드'\n } else if (btn.icon === 'circleX') {\n button.textContent = '비활성화'\n } else if (btn.icon === 'circleCheckBig') {\n button.textContent = '활성화'\n } else {\n button.textContent = btn.icon\n }\n }\n \n // 클릭 이벤트\n button.addEventListener('click', (e) => {\n e.stopPropagation()\n btn.onClick(rowData)\n })\n \n container.appendChild(button)\n })\n \n return container\n}\n\n// Action Buttons 컬럼 정의\nconst actionButtonsColumn = computed<ColDef | null>(() => {\n if (!props.actionButtons || props.actionButtons.length === 0) {\n return null\n }\n \n return {\n colId: 'actionButtons',\n headerName: '작업',\n field: '_actions',\n width: 120,\n minWidth: 80,\n maxWidth: 200,\n lockPosition: 'left' as const,\n sortable: false,\n filter: false,\n resizable: true,\n suppressNavigable: true,\n suppressHeaderMenuButton: true,\n cellRenderer: ActionButtonsCellRenderer,\n cellStyle: { display: 'flex', justifyContent: 'center', alignItems: 'center' },\n }\n})\n\n// checkbox 활성화 및 추가 컬럼 처리\nconst processedColumnDefs = computed(() => {\n const columns: ColDef[] = []\n \n // 1. Checkbox 컬럼 (최우선)\n if (props.checkbox) {\n columns.push({\n colId: 'rowSelection',\n headerName: '',\n // field와 valueGetter 제거 - AG Grid 공식 방식\n width: 40,\n minWidth: 40,\n maxWidth: 40,\n lockPosition: 'left' as const,\n checkboxSelection: true,\n headerCheckboxSelection: true,\n sortable: false,\n filter: false,\n resizable: false,\n suppressNavigable: true,\n suppressHeaderMenuButton: true,\n cellStyle: { display: 'flex', justifyContent: 'center', alignItems: 'center' },\n // 체크박스 셀 클릭 시 토글 (checkbox 아이콘 클릭은 AG Grid 기본 처리)\n onCellClicked: (params: CellClickedEvent) => {\n const target = (params.event as MouseEvent)?.target as HTMLElement\n if (target && !target.closest('.ag-checkbox-input-wrapper')) {\n params.node.setSelected(!params.node.isSelected())\n }\n },\n })\n }\n \n // 2. Action Buttons 컬럼\n if (actionButtonsColumn.value) {\n columns.push(actionButtonsColumn.value)\n }\n \n // 3. 사용자 정의 컬럼들 (Row Numbers는 AG Grid가 자동으로 추가)\n columns.push(...props.columnDefs)\n \n return columns\n})\n\n// Grid 옵션 설정\nconst gridOptions = computed<GridOptions>(() => {\n const useFloatingFilters = props.floatingFilter ?? props.floatingFilters\n const options: GridOptions = {\n theme: jTheme,\n pagination: props.pagination,\n defaultColDef: {\n filter: true,\n floatingFilter: useFloatingFilters,\n },\n rowSelection: props.checkbox ? 'multiple' : undefined,\n // 체크박스 모드일 때 row 클릭으로 선택이 변경되지 않도록 설정\n // 체크박스만으로 선택을 제어하도록 함\n suppressRowClickSelection: props.checkbox,\n\n // Row Numbers (Enterprise) - AG Grid 표준 방식\n rowNumbers: props.rowNumbers\n ? ({\n minWidth: props.rowNumberWidth,\n width: props.rowNumberWidth,\n } as any)\n : false,\n\n // Column Hover Highlight\n columnHoverHighlight: props.columnHover,\n\n // Enterprise 기능 옵션\n sideBar: props.enableColumnsToolPanel || props.enableGrouping || props.enablePivot ? {\n toolPanels: [\n {\n id: 'columns',\n labelDefault: 'Columns',\n labelKey: 'columns',\n iconKey: 'columns',\n toolPanel: 'agColumnsToolPanel',\n toolPanelParams: {\n suppressRowGroups: !props.enableGrouping,\n suppressValues: !props.enablePivot,\n suppressPivots: !props.enablePivot,\n suppressPivotMode: !props.enablePivot,\n },\n },\n ],\n defaultToolPanel: '', // 초기에는 접힌 상태\n } : undefined,\n\n // Status Bar (Enterprise)\n // compactFooter 모드에서는 Status Bar 비활성화\n statusBar: (props.statusBar && !props.compactFooter) ? {\n statusPanels: [\n { statusPanel: 'agTotalAndFilteredRowCountComponent', align: 'left' as const },\n { statusPanel: 'agSelectedRowCountComponent', align: 'left' as const },\n { statusPanel: 'agAggregationComponent', align: 'right' as const },\n ],\n } : undefined,\n\n // Row Group Panel 설정 (Enterprise) - 그리드 상단에 드래그 영역 표시\n rowGroupPanelShow: props.rowGroupPanelShow !== 'never' ? props.rowGroupPanelShow : undefined,\n\n // Pivot Panel 설정 (Enterprise) - 피벗 모드용 드래그 영역\n pivotPanelShow: props.pivotPanelShow !== 'never' ? props.pivotPanelShow : undefined,\n \n // Pivot Mode 활성화\n pivotMode: props.pivotMode,\n\n // 그룹핑 기본 설정\n groupDefaultExpanded: props.groupDefaultExpanded,\n suppressAggFuncInHeader: false,\n\n // Tree Data 설정 (Enterprise)\n treeData: props.enableTreeData || undefined,\n getDataPath: props.enableTreeData\n ? (props.getDataPath || ((data: any) => data.path || []))\n : undefined,\n autoGroupColumnDef: props.enableTreeData && props.autoGroupColumnDef\n ? props.autoGroupColumnDef\n : undefined,\n }\n\n return options\n})\n\n// Excel 내보내기 함수 (외부에서 사용 가능하도록 expose)\nconst exportToExcel = () => {\n if (gridApi.value && props.enableExcelExport) {\n gridApi.value.exportDataAsExcel({\n fileName: 'grid-export.xlsx',\n })\n }\n}\n\n// 그리드 API를 외부에 노출\ndefineExpose({\n gridApi,\n gridColumnApi,\n exportToExcel,\n})\n\n// Grid ready 이벤트 핸들러\nconst onGridReady = (params: GridReadyEvent) => {\n gridApi.value = params.api\n gridColumnApi.value = params.api // v34에서 columnApi는 deprecated\n emit('gridReady', params)\n}\n\n// 행 클릭 이벤트 핸들러\nconst onRowClicked = (event: RowClickedEvent) => {\n emit('rowClicked', event)\n}\n\n// 행 더블클릭 이벤트 핸들러\nconst onRowDoubleClicked = (event: RowDoubleClickedEvent) => {\n emit('rowDoubleClicked', event)\n}\n\n// 셀 클릭 이벤트 핸들러\nconst onCellClicked = (event: CellClickedEvent) => {\n emit('cellClicked', event)\n}\n\n// 선택 변경 이벤트 핸들러\nconst onSelectionChanged = (event: SelectionChangedEvent) => {\n emit('selectionChanged', event)\n emit('update:selectedRows', event.api.getSelectedRows())\n}\n\n// 셀 값 변경 이벤트 핸들러\nconst onCellValueChanged = (event: CellValueChangedEvent) => {\n emit('cellValueChanged', event)\n}\n\n// columnDefs 변경 감지\nwatch(\n () => props.columnDefs,\n () => {\n if (gridApi.value) {\n gridApi.value.setGridOption('columnDefs', processedColumnDefs.value)\n }\n },\n { deep: true },\n)\n\n// rowData 변경 감지\nwatch(\n () => props.rowData,\n () => {\n if (gridApi.value) {\n gridApi.value.setGridOption('rowData', props.rowData)\n }\n },\n { deep: true },\n)\n</script>\n\n<template>\n <div ref=\"gridContainerRef\" :class=\"cn('ag-grid-container', props.class)\">\n <AgGridVue\n :column-defs=\"processedColumnDefs\"\n :row-data=\"rowData\"\n :grid-options=\"gridOptions\"\n style=\"height: 100%; width: 100%\"\n @grid-ready=\"onGridReady\"\n @row-clicked=\"onRowClicked\"\n @row-double-clicked=\"onRowDoubleClicked\"\n @cell-clicked=\"onCellClicked\"\n @selection-changed=\"onSelectionChanged\"\n @cell-value-changed=\"onCellValueChanged\"\n />\n </div>\n</template>\n\n<style scoped>\n.ag-grid-container {\n width: 100%;\n height: 100%;\n\n /*\n * App CSS 변수 → AG Grid CSS 변수 브릿지\n * CSS 변수는 런타임 해소 → .dark 클래스 전환 시 라이트/다크 자동 대응\n * AG Grid v33 공식 CSS variable 명칭 사용\n */\n --ag-background-color: hsl(var(--background));\n --ag-foreground-color: hsl(var(--foreground));\n --ag-border-color: hsl(var(--border));\n --ag-header-background-color: hsl(var(--muted));\n --ag-header-foreground-color: hsl(var(--foreground));\n --ag-odd-row-background-color: hsl(var(--card));\n --ag-row-hover-color: hsl(var(--accent));\n --ag-selected-row-background-color: hsl(var(--primary) / 0.12);\n --ag-accent-color: hsl(var(--primary));\n --ag-header-height: var(--j-grid-header-h);\n --ag-row-height: var(--j-grid-row-h);\n}\n\n/* ============================================\n COMPACT FOOTER: Status Bar + Pagination 통합\n ============================================ */\n\n/* Status Bar 높이 줄이기 */\n:deep(.ag-status-bar) {\n min-height: var(--j-grid-footer-h) !important;\n height: var(--j-grid-footer-h) !important;\n padding: 0 12px !important;\n border-top: 1px solid var(--ag-border-color);\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-size: 0.8125rem;\n}\n\n/* Status Bar 컴포넌트들 높이 조정 */\n:deep(.ag-status-bar-left),\n:deep(.ag-status-bar-center),\n:deep(.ag-status-bar-right) {\n height: var(--j-grid-footer-h);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n/* Status Bar 패널들 간격 조정 */\n:deep(.ag-status-panel) {\n padding: 0;\n height: var(--j-grid-footer-h);\n display: flex;\n align-items: center;\n}\n\n/* Pagination Panel 높이 줄이기 */\n:deep(.ag-paging-panel) {\n min-height: var(--j-grid-footer-h) !important;\n height: var(--j-grid-footer-h) !important;\n padding: 0 12px !important;\n border-top: 1px solid var(--ag-border-color);\n font-size: 0.8125rem;\n}\n\n/* Pagination 컴포넌트들 높이 조정 */\n:deep(.ag-paging-page-size),\n:deep(.ag-paging-row-summary-panel) {\n height: var(--j-grid-footer-h);\n display: flex;\n align-items: center;\n}\n\n/* Page Size Selector 높이 조정 */\n:deep(.ag-paging-page-size .ag-picker-field) {\n height: 24px;\n min-height: 24px;\n}\n\n:deep(.ag-paging-page-size .ag-picker-field-wrapper) {\n height: 24px;\n padding: 0 4px;\n}\n\n/* Pagination 버튼들 높이 조정 */\n:deep(.ag-paging-button) {\n height: 24px;\n width: 24px;\n padding: 2px;\n}\n\n/* Pagination 버튼 래퍼 */\n:deep(.ag-paging-button-wrapper) {\n height: var(--j-grid-footer-h);\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n/* Row summary 텍스트 */\n:deep(.ag-paging-row-summary-panel-number) {\n line-height: var(--j-grid-footer-h);\n}\n\n/* ========================================\n 패턴 9: AG-Grid 스타일 향상\n ======================================== */\n\n:deep(.ag-root-wrapper) {\n border: 1px solid hsl(var(--border));\n border-radius: 0.375rem;\n}\n\n/* ── Header ──────────────────────────────────────────────────────────── */\n:deep(.ag-header) {\n border-bottom: 1px solid hsl(var(--border));\n font-weight: 500;\n}\n\n:deep(.ag-header-row:not(.ag-floating-filter)) {\n min-height: var(--j-grid-header-h) !important;\n height: var(--j-grid-header-h) !important;\n}\n\n:deep(.ag-header-cell) {\n color: hsl(var(--foreground));\n font-size: 0.75rem;\n padding: 0 0.5rem;\n display: flex;\n align-items: center;\n}\n\n/* Floating Filter: 헤더와 명확히 구분되는 입력 영역 */\n:deep(.ag-header-row.ag-floating-filter) {\n min-height: var(--j-grid-filter-h) !important;\n height: var(--j-grid-filter-h) !important;\n background-color: hsl(var(--card));\n border-top: 1px solid hsl(var(--border) / 0.5);\n}\n\n:deep(.ag-floating-filter-body) {\n min-height: var(--j-grid-filter-h);\n}\n\n:deep(.ag-floating-filter-body input),\n:deep(.ag-floating-filter-body .ag-input-field-input),\n:deep(.ag-floating-filter-body .ag-picker-field-wrapper) {\n height: 20px !important;\n min-height: 20px !important;\n font-size: 0.75rem;\n background-color: hsl(var(--background)) !important;\n border: 1px solid hsl(var(--border)) !important;\n border-radius: 3px !important;\n padding: 0 4px !important;\n color: hsl(var(--foreground)) !important;\n}\n\n:deep(.ag-floating-filter-body input:focus),\n:deep(.ag-floating-filter-body .ag-input-field-input:focus) {\n border-color: hsl(var(--primary)) !important;\n outline: none !important;\n}\n\n/* ── Rows ────────────────────────────────────────────────────────────── */\n:deep(.ag-row) {\n min-height: var(--j-grid-row-h) !important;\n height: var(--j-grid-row-h) !important;\n transition: background-color 0.15s ease, box-shadow 0.15s ease;\n cursor: pointer;\n}\n\n/* 선택된 행: 왼쪽 accent 기둥으로 명확한 시각적 선택 표시 (라이트/다크 모두) */\n:deep(.ag-row-selected) {\n box-shadow: inset 3px 0 0 hsl(var(--primary));\n}\n\n/* ── Cells ───────────────────────────────────────────────────────────── */\n:deep(.ag-cell) {\n line-height: 1.2;\n padding: 0 0.5rem;\n font-size: 0.75rem;\n border-bottom: 1px solid hsl(var(--border) / 0.5);\n}\n\n:deep(.ag-cell-wrapper) {\n align-items: center;\n}\n\n:deep(.ag-cell-value) {\n display: flex;\n align-items: center;\n min-height: 100%;\n}\n\n/* 행 클릭 시 row number 셀에 보이는 강한 포커스 음영 제거 */\n:deep(.ag-cell.ag-row-number-cell.ag-cell-focus),\n:deep(.ag-cell.ag-row-number-cell:focus-within) {\n box-shadow: none !important;\n outline: none !important;\n}\n\n/* 셀 포커스 */\n:deep(.ag-cell.ag-cell-focus) {\n box-shadow: inset 0 0 0 1px hsl(var(--ring) / 0.5) !important;\n}\n\n/* 인라인 편집 셀 */\n:deep(.ag-cell-inline-editing) {\n padding: 0 !important;\n border: none !important;\n box-shadow: inset 0 0 0 1px hsl(var(--primary)) !important;\n background-color: hsl(var(--background)) !important;\n}\n\n:deep(.ag-cell-inline-editing .ag-cell-editor),\n:deep(.ag-cell-inline-editing .ag-text-field-input-wrapper),\n:deep(.ag-cell-inline-editing .ag-input-field) {\n height: 100% !important;\n border: none !important;\n outline: none !important;\n background: transparent !important;\n box-shadow: none !important;\n}\n\n:deep(.ag-cell-inline-editing input) {\n height: 100% !important;\n width: 100% !important;\n padding: 0 0.5rem !important;\n border: none !important;\n outline: none !important;\n background: transparent !important;\n box-shadow: none !important;\n font-size: 0.75rem !important;\n color: hsl(var(--foreground)) !important;\n}\n\n/* ── Checkbox 셀 중앙정렬 (col-id 속성 선택자 — 신뢰할 수 있는 접근) ── */\n:deep(.ag-cell[col-id=\"rowSelection\"]) {\n display: flex !important;\n justify-content: center !important;\n align-items: center !important;\n padding: 0 !important;\n}\n:deep(.ag-cell[col-id=\"rowSelection\"] .ag-cell-wrapper) {\n width: auto !important;\n flex: none !important;\n}\n:deep(.ag-header-cell[col-id=\"rowSelection\"] .ag-header-cell-comp-wrapper) {\n justify-content: center !important;\n}\n\n/* ── Action Buttons (JS 렌더러 — CSS variable 브릿지) ─────────────────── */\n:deep(.j-action-btn) {\n padding: 1px 8px;\n font-size: 0.75rem;\n border: 1px solid hsl(var(--border));\n border-radius: 3px;\n background-color: hsl(var(--card));\n color: hsl(var(--foreground));\n cursor: pointer;\n transition: background-color 0.15s ease;\n white-space: nowrap;\n}\n\n:deep(.j-action-btn:hover) {\n background-color: hsl(var(--accent));\n}\n\n:deep(.j-action-btn-danger) {\n border-color: hsl(var(--destructive) / 0.4);\n background-color: hsl(var(--destructive) / 0.06);\n color: hsl(var(--destructive));\n}\n\n:deep(.j-action-btn-danger:hover) {\n background-color: hsl(var(--destructive) / 0.15);\n}\n\n/* ── Pagination ──────────────────────────────────────────────────────── */\n:deep(.ag-paging-panel) {\n border-top: 1px solid hsl(var(--border));\n}\n</style>\n"],"names":["ModuleRegistry","AllCommunityModule","AllEnterpriseModule","jTheme","themeQuartz","isDark","ref","gridContainerRef","darkObserver","watch","dark","onMounted","onUnmounted","props","__props","emit","__emit","gridApi","gridColumnApi","ActionButtonsCellRenderer","params","buttons","rowData","visibleButtons","btn","container","button","e","actionButtonsColumn","computed","processedColumnDefs","columns","target","gridOptions","useFloatingFilters","data","__expose","onGridReady","onRowClicked","event","onRowDoubleClicked","onCellClicked","onSelectionChanged","onCellValueChanged","_createElementBlock","_normalizeClass","_unref","cn","_createVNode","AgGridVue"],"mappings":"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"}
@@ -93,9 +93,9 @@ const Z = /* @__PURE__ */ A({
93
93
  colId: "rowSelection",
94
94
  headerName: "",
95
95
  // field와 valueGetter 제거 - AG Grid 공식 방식
96
- width: 50,
97
- minWidth: 50,
98
- maxWidth: 50,
96
+ width: 40,
97
+ minWidth: 40,
98
+ maxWidth: 40,
99
99
  lockPosition: "left",
100
100
  checkboxSelection: !0,
101
101
  headerCheckboxSelection: !0,
@@ -1 +1 @@
1
- {"version":3,"file":"JGrid.vue2.js","sources":["../../../../src/components/atoms/JGrid.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch, onMounted, onUnmounted } from 'vue'\nimport { AgGridVue } from 'ag-grid-vue3'\nimport { cn } from '@/lib/utils'\nimport type {\n ColDef,\n GridOptions,\n RowClickedEvent,\n CellClickedEvent,\n SelectionChangedEvent,\n CellValueChangedEvent,\n RowDoubleClickedEvent,\n GridReadyEvent,\n ICellRendererParams,\n} from 'ag-grid-community'\nimport { ModuleRegistry, AllCommunityModule, themeQuartz } from 'ag-grid-community'\n// Enterprise 모듈 import (Grouping, Pivot, Excel Export 등)\nimport { AllEnterpriseModule } from 'ag-grid-enterprise'\n\n// AG Grid 모듈 등록 (Community + Enterprise)\nModuleRegistry.registerModules([AllCommunityModule, AllEnterpriseModule])\n\n// ── 공식 권장: data-ag-theme-mode attribute 기반 다크모드 전환 ─────────────────\n// AG Grid v33 공식 패턴:\n// 1. themeQuartz.withParams({...}, 'light').withParams({...}, 'dark') 로 두 모드 정의\n// 2. data-ag-theme-mode attribute 전환으로 모드 스위칭 (theme 객체 재생성 불필요)\n// 3. CSS --ag-* 변수 오버라이드로 앱 CSS 변수 참조 (getCSSVar 우회 불필요)\nconst jTheme = themeQuartz\n .withParams({\n // 공통 사이즈 (라이트/다크 모두 적용)\n cellHorizontalPaddingScale: 1,\n columnBorder: true,\n fontSize: 13,\n headerFontWeight: 500,\n headerVerticalPaddingScale: 0.45,\n iconSize: 14,\n rowVerticalPaddingScale: 0.4,\n })\n .withParams({\n browserColorScheme: 'light',\n }, 'light')\n .withParams({\n browserColorScheme: 'dark',\n }, 'dark')\n\n// JLIS는 .dark class 기반 다크모드이므로 MutationObserver로 감지 후 attribute 동기화\nconst isDark = ref(typeof document !== 'undefined' && document.documentElement.classList.contains('dark'))\nconst gridContainerRef = ref<HTMLElement | null>(null)\nlet darkObserver: MutationObserver | null = null\n\n// isDark 변경 → data-ag-theme-mode 동기화 (AG Grid가 내부적으로 반응)\nwatch(isDark, (dark) => {\n gridContainerRef.value?.setAttribute('data-ag-theme-mode', dark ? 'dark' : 'light')\n})\n\nonMounted(() => {\n // 초기 attribute 설정\n gridContainerRef.value?.setAttribute('data-ag-theme-mode', isDark.value ? 'dark' : 'light')\n\n // 이후 변경 감지\n darkObserver = new MutationObserver(() => {\n isDark.value = document.documentElement.classList.contains('dark')\n })\n darkObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] })\n})\n\nonUnmounted(() => {\n darkObserver?.disconnect()\n})\n\n/**\n * Action Button 정의 타입\n */\nexport type ActionButton = {\n /** 버튼 아이콘 이름 (lucide) - label이 없을 때 기본 텍스트 생성에 사용 */\n icon?: string\n /** 버튼 라벨 */\n label?: string\n /** 툴팁 텍스트 */\n tooltip?: string\n /** 버튼 클릭 핸들러 (rowData 전달) */\n onClick: (rowData: any) => void\n /** 버튼 스타일 타입 */\n styletype?: 'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'danger'\n /** 조건부 표시 함수 (rowData를 받아 boolean 반환) */\n show?: (rowData: any) => boolean\n}\n\nconst props = withDefaults(\n defineProps<{\n /** 추가 클래스 (외부 커스터마이징용) */\n class?: string\n /** 그리드에 표시할 데이터 배열 */\n rowData: any[]\n /** 컬럼 정의 배열 */\n columnDefs: ColDef[]\n /** 페이지네이션 활성화 여부 */\n pagination?: boolean\n /** 체크박스 선택 활성화 여부 */\n checkbox?: boolean\n /** 행 번호 표시 여부 (Enterprise) */\n rowNumbers?: boolean\n /** 플로팅 필터 표시 여부 */\n floatingFilters?: boolean\n /** 플로팅 필터 표시 여부 (하위호환 alias) */\n floatingFilter?: boolean\n /** 행 번호 컬럼 너비(px) */\n rowNumberWidth?: number\n /** 행별 액션 버튼 목록 */\n actionButtons?: ActionButton[]\n /** 요약 컬럼 표시 여부 */\n summaryColumn?: boolean\n /** 숨김 컬럼 관리 활성화 여부 */\n hiddenColumn?: boolean\n /** 그룹핑 기능 활성화 여부 (Enterprise) */\n enableGrouping?: boolean\n /** 피벗 기능 활성화 여부 (Enterprise) */\n enablePivot?: boolean\n /** Excel 내보내기 기능 활성화 여부 (Enterprise) */\n enableExcelExport?: boolean\n /** Tree Data 기능 활성화 여부 (Enterprise) */\n enableTreeData?: boolean\n /** Tree Data 계층 경로를 반환하는 함수 */\n getDataPath?: (data: any) => (string | number)[]\n /** Tree Data 그룹 컬럼 정의 */\n autoGroupColumnDef?: ColDef\n /** 선택된 행 데이터 배열 (v-model:selected-rows) */\n selectedRows?: any[]\n /** 컬럼 호버 하이라이트 활성화 여부 */\n columnHover?: boolean\n /** Columns Tool Panel 활성화 여부 (Enterprise) */\n enableColumnsToolPanel?: boolean\n /** Status Bar 활성화 여부 (Enterprise) */\n statusBar?: boolean\n /** 간소화된 Footer 모드 (Status Bar 제거, Pagination만 표시) */\n compactFooter?: boolean\n /** Row Group Panel 표시 여부 ('always' | 'onlyWhenGrouping' | 'never') */\n rowGroupPanelShow?: 'always' | 'onlyWhenGrouping' | 'never'\n /** 그룹 기본 확장 레벨 (-1: 모두 닫힘, 0: 첫 레벨만, 1: 2레벨까지...) */\n groupDefaultExpanded?: number\n /** Pivot Mode Panel 표시 여부 */\n pivotPanelShow?: 'always' | 'onlyWhenPivoting' | 'never'\n /** Pivot Mode 활성화 여부 */\n pivotMode?: boolean\n }>(),\n {\n pagination: true,\n checkbox: false,\n rowNumbers: true,\n floatingFilters: true,\n floatingFilter: undefined,\n rowNumberWidth: 38,\n actionButtons: undefined,\n summaryColumn: false,\n hiddenColumn: false,\n enableGrouping: true,\n enablePivot: false,\n enableExcelExport: false,\n enableTreeData: false,\n getDataPath: undefined,\n autoGroupColumnDef: undefined,\n selectedRows: () => [],\n columnHover: true,\n enableColumnsToolPanel: true,\n statusBar: true,\n compactFooter: false,\n rowGroupPanelShow: 'never',\n groupDefaultExpanded: -1,\n pivotPanelShow: 'never',\n pivotMode: false,\n },\n)\n\nconst emit = defineEmits<{\n /** 행 클릭 이벤트 */\n rowClicked: [event: RowClickedEvent]\n /** 행 더블클릭 이벤트 */\n rowDoubleClicked: [event: RowDoubleClickedEvent]\n /** 셀 클릭 이벤트 */\n cellClicked: [event: CellClickedEvent]\n /** 선택 변경 이벤트 (체크박스 등) */\n selectionChanged: [event: SelectionChangedEvent]\n /** 셀 값 변경 이벤트 */\n cellValueChanged: [event: CellValueChangedEvent]\n /** 그리드 준비 완료 이벤트 */\n gridReady: [event: GridReadyEvent]\n /** 선택된 행 변경 이벤트 (v-model:selected-rows) */\n 'update:selectedRows': [rows: any[]]\n}>()\n\n// ag-Grid 인스턴스 참조\nconst gridApi = ref<any>(null)\nconst gridColumnApi = ref<any>(null)\n\n// Action Buttons Cell Renderer - 함수형으로 DOM 직접 생성\nconst ActionButtonsCellRenderer = (params: ICellRendererParams) => {\n const buttons = props.actionButtons || []\n const rowData = params.data\n \n // 표시할 버튼 필터링\n const visibleButtons = buttons.filter(btn => {\n if (btn.show) {\n return btn.show(rowData)\n }\n return true\n })\n \n if (visibleButtons.length === 0) {\n return ''\n }\n \n // 컨테이너 div 생성\n const container = document.createElement('div')\n container.className = 'flex items-center gap-1'\n \n // 각 버튼 생성\n visibleButtons.forEach((btn) => {\n const button = document.createElement('button')\n button.className = btn.styletype === 'danger' ? 'j-action-btn j-action-btn-danger' : 'j-action-btn'\n \n // tooltip\n if (btn.tooltip) {\n button.title = btn.tooltip\n }\n \n // 라벨 추가 (텍스트 버튼)\n if (btn.label) {\n button.textContent = btn.label\n } else if (btn.icon) {\n // 라벨이 없으면 기본 텍스트 생성\n if (btn.icon === 'pencil') {\n button.textContent = '수정'\n } else if (btn.icon === 'trash2' || btn.icon === 'trash') {\n button.textContent = '삭제'\n } else if (btn.icon === 'eye') {\n button.textContent = '보기'\n } else if (btn.icon === 'copy') {\n button.textContent = '복사'\n } else if (btn.icon === 'download') {\n button.textContent = '다운로드'\n } else if (btn.icon === 'circleX') {\n button.textContent = '비활성화'\n } else if (btn.icon === 'circleCheckBig') {\n button.textContent = '활성화'\n } else {\n button.textContent = btn.icon\n }\n }\n \n // 클릭 이벤트\n button.addEventListener('click', (e) => {\n e.stopPropagation()\n btn.onClick(rowData)\n })\n \n container.appendChild(button)\n })\n \n return container\n}\n\n// Action Buttons 컬럼 정의\nconst actionButtonsColumn = computed<ColDef | null>(() => {\n if (!props.actionButtons || props.actionButtons.length === 0) {\n return null\n }\n \n return {\n colId: 'actionButtons',\n headerName: '작업',\n field: '_actions',\n width: 120,\n minWidth: 80,\n maxWidth: 200,\n lockPosition: 'left' as const,\n sortable: false,\n filter: false,\n resizable: true,\n suppressNavigable: true,\n suppressHeaderMenuButton: true,\n cellRenderer: ActionButtonsCellRenderer,\n cellStyle: { display: 'flex', justifyContent: 'center', alignItems: 'center' },\n }\n})\n\n// checkbox 활성화 및 추가 컬럼 처리\nconst processedColumnDefs = computed(() => {\n const columns: ColDef[] = []\n \n // 1. Checkbox 컬럼 (최우선)\n if (props.checkbox) {\n columns.push({\n colId: 'rowSelection',\n headerName: '',\n // field와 valueGetter 제거 - AG Grid 공식 방식\n width: 50,\n minWidth: 50,\n maxWidth: 50,\n lockPosition: 'left' as const,\n checkboxSelection: true,\n headerCheckboxSelection: true,\n sortable: false,\n filter: false,\n resizable: false,\n suppressNavigable: true,\n suppressHeaderMenuButton: true,\n cellStyle: { display: 'flex', justifyContent: 'center', alignItems: 'center' },\n // 체크박스 셀 클릭 시 토글 (checkbox 아이콘 클릭은 AG Grid 기본 처리)\n onCellClicked: (params: CellClickedEvent) => {\n const target = (params.event as MouseEvent)?.target as HTMLElement\n if (target && !target.closest('.ag-checkbox-input-wrapper')) {\n params.node.setSelected(!params.node.isSelected())\n }\n },\n })\n }\n \n // 2. Action Buttons 컬럼\n if (actionButtonsColumn.value) {\n columns.push(actionButtonsColumn.value)\n }\n \n // 3. 사용자 정의 컬럼들 (Row Numbers는 AG Grid가 자동으로 추가)\n columns.push(...props.columnDefs)\n \n return columns\n})\n\n// Grid 옵션 설정\nconst gridOptions = computed<GridOptions>(() => {\n const useFloatingFilters = props.floatingFilter ?? props.floatingFilters\n const options: GridOptions = {\n theme: jTheme,\n pagination: props.pagination,\n defaultColDef: {\n filter: true,\n floatingFilter: useFloatingFilters,\n },\n rowSelection: props.checkbox ? 'multiple' : undefined,\n // 체크박스 모드일 때 row 클릭으로 선택이 변경되지 않도록 설정\n // 체크박스만으로 선택을 제어하도록 함\n suppressRowClickSelection: props.checkbox,\n\n // Row Numbers (Enterprise) - AG Grid 표준 방식\n rowNumbers: props.rowNumbers\n ? ({\n minWidth: props.rowNumberWidth,\n width: props.rowNumberWidth,\n } as any)\n : false,\n\n // Column Hover Highlight\n columnHoverHighlight: props.columnHover,\n\n // Enterprise 기능 옵션\n sideBar: props.enableColumnsToolPanel || props.enableGrouping || props.enablePivot ? {\n toolPanels: [\n {\n id: 'columns',\n labelDefault: 'Columns',\n labelKey: 'columns',\n iconKey: 'columns',\n toolPanel: 'agColumnsToolPanel',\n toolPanelParams: {\n suppressRowGroups: !props.enableGrouping,\n suppressValues: !props.enablePivot,\n suppressPivots: !props.enablePivot,\n suppressPivotMode: !props.enablePivot,\n },\n },\n ],\n defaultToolPanel: '', // 초기에는 접힌 상태\n } : undefined,\n\n // Status Bar (Enterprise)\n // compactFooter 모드에서는 Status Bar 비활성화\n statusBar: (props.statusBar && !props.compactFooter) ? {\n statusPanels: [\n { statusPanel: 'agTotalAndFilteredRowCountComponent', align: 'left' as const },\n { statusPanel: 'agSelectedRowCountComponent', align: 'left' as const },\n { statusPanel: 'agAggregationComponent', align: 'right' as const },\n ],\n } : undefined,\n\n // Row Group Panel 설정 (Enterprise) - 그리드 상단에 드래그 영역 표시\n rowGroupPanelShow: props.rowGroupPanelShow !== 'never' ? props.rowGroupPanelShow : undefined,\n\n // Pivot Panel 설정 (Enterprise) - 피벗 모드용 드래그 영역\n pivotPanelShow: props.pivotPanelShow !== 'never' ? props.pivotPanelShow : undefined,\n \n // Pivot Mode 활성화\n pivotMode: props.pivotMode,\n\n // 그룹핑 기본 설정\n groupDefaultExpanded: props.groupDefaultExpanded,\n suppressAggFuncInHeader: false,\n\n // Tree Data 설정 (Enterprise)\n treeData: props.enableTreeData || undefined,\n getDataPath: props.enableTreeData\n ? (props.getDataPath || ((data: any) => data.path || []))\n : undefined,\n autoGroupColumnDef: props.enableTreeData && props.autoGroupColumnDef\n ? props.autoGroupColumnDef\n : undefined,\n }\n\n return options\n})\n\n// Excel 내보내기 함수 (외부에서 사용 가능하도록 expose)\nconst exportToExcel = () => {\n if (gridApi.value && props.enableExcelExport) {\n gridApi.value.exportDataAsExcel({\n fileName: 'grid-export.xlsx',\n })\n }\n}\n\n// 그리드 API를 외부에 노출\ndefineExpose({\n gridApi,\n gridColumnApi,\n exportToExcel,\n})\n\n// Grid ready 이벤트 핸들러\nconst onGridReady = (params: GridReadyEvent) => {\n gridApi.value = params.api\n gridColumnApi.value = params.api // v34에서 columnApi는 deprecated\n emit('gridReady', params)\n}\n\n// 행 클릭 이벤트 핸들러\nconst onRowClicked = (event: RowClickedEvent) => {\n emit('rowClicked', event)\n}\n\n// 행 더블클릭 이벤트 핸들러\nconst onRowDoubleClicked = (event: RowDoubleClickedEvent) => {\n emit('rowDoubleClicked', event)\n}\n\n// 셀 클릭 이벤트 핸들러\nconst onCellClicked = (event: CellClickedEvent) => {\n emit('cellClicked', event)\n}\n\n// 선택 변경 이벤트 핸들러\nconst onSelectionChanged = (event: SelectionChangedEvent) => {\n emit('selectionChanged', event)\n emit('update:selectedRows', event.api.getSelectedRows())\n}\n\n// 셀 값 변경 이벤트 핸들러\nconst onCellValueChanged = (event: CellValueChangedEvent) => {\n emit('cellValueChanged', event)\n}\n\n// columnDefs 변경 감지\nwatch(\n () => props.columnDefs,\n () => {\n if (gridApi.value) {\n gridApi.value.setGridOption('columnDefs', processedColumnDefs.value)\n }\n },\n { deep: true },\n)\n\n// rowData 변경 감지\nwatch(\n () => props.rowData,\n () => {\n if (gridApi.value) {\n gridApi.value.setGridOption('rowData', props.rowData)\n }\n },\n { deep: true },\n)\n</script>\n\n<template>\n <div ref=\"gridContainerRef\" :class=\"cn('ag-grid-container', props.class)\">\n <AgGridVue\n :column-defs=\"processedColumnDefs\"\n :row-data=\"rowData\"\n :grid-options=\"gridOptions\"\n style=\"height: 100%; width: 100%\"\n @grid-ready=\"onGridReady\"\n @row-clicked=\"onRowClicked\"\n @row-double-clicked=\"onRowDoubleClicked\"\n @cell-clicked=\"onCellClicked\"\n @selection-changed=\"onSelectionChanged\"\n @cell-value-changed=\"onCellValueChanged\"\n />\n </div>\n</template>\n\n<style scoped>\n.ag-grid-container {\n width: 100%;\n height: 100%;\n\n /*\n * App CSS 변수 → AG Grid CSS 변수 브릿지\n * CSS 변수는 런타임 해소 → .dark 클래스 전환 시 라이트/다크 자동 대응\n * AG Grid v33 공식 CSS variable 명칭 사용\n */\n --ag-background-color: hsl(var(--background));\n --ag-foreground-color: hsl(var(--foreground));\n --ag-border-color: hsl(var(--border));\n --ag-header-background-color: hsl(var(--muted));\n --ag-header-foreground-color: hsl(var(--foreground));\n --ag-odd-row-background-color: hsl(var(--card));\n --ag-row-hover-color: hsl(var(--accent));\n --ag-selected-row-background-color: hsl(var(--primary) / 0.12);\n --ag-accent-color: hsl(var(--primary));\n --ag-header-height: var(--j-grid-header-h);\n --ag-row-height: var(--j-grid-row-h);\n}\n\n/* ============================================\n COMPACT FOOTER: Status Bar + Pagination 통합\n ============================================ */\n\n/* Status Bar 높이 줄이기 */\n:deep(.ag-status-bar) {\n min-height: var(--j-grid-footer-h) !important;\n height: var(--j-grid-footer-h) !important;\n padding: 0 12px !important;\n border-top: 1px solid var(--ag-border-color);\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-size: 0.8125rem;\n}\n\n/* Status Bar 컴포넌트들 높이 조정 */\n:deep(.ag-status-bar-left),\n:deep(.ag-status-bar-center),\n:deep(.ag-status-bar-right) {\n height: var(--j-grid-footer-h);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n/* Status Bar 패널들 간격 조정 */\n:deep(.ag-status-panel) {\n padding: 0;\n height: var(--j-grid-footer-h);\n display: flex;\n align-items: center;\n}\n\n/* Pagination Panel 높이 줄이기 */\n:deep(.ag-paging-panel) {\n min-height: var(--j-grid-footer-h) !important;\n height: var(--j-grid-footer-h) !important;\n padding: 0 12px !important;\n border-top: 1px solid var(--ag-border-color);\n font-size: 0.8125rem;\n}\n\n/* Pagination 컴포넌트들 높이 조정 */\n:deep(.ag-paging-page-size),\n:deep(.ag-paging-row-summary-panel) {\n height: var(--j-grid-footer-h);\n display: flex;\n align-items: center;\n}\n\n/* Page Size Selector 높이 조정 */\n:deep(.ag-paging-page-size .ag-picker-field) {\n height: 24px;\n min-height: 24px;\n}\n\n:deep(.ag-paging-page-size .ag-picker-field-wrapper) {\n height: 24px;\n padding: 0 4px;\n}\n\n/* Pagination 버튼들 높이 조정 */\n:deep(.ag-paging-button) {\n height: 24px;\n width: 24px;\n padding: 2px;\n}\n\n/* Pagination 버튼 래퍼 */\n:deep(.ag-paging-button-wrapper) {\n height: var(--j-grid-footer-h);\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n/* Row summary 텍스트 */\n:deep(.ag-paging-row-summary-panel-number) {\n line-height: var(--j-grid-footer-h);\n}\n\n/* ========================================\n 패턴 9: AG-Grid 스타일 향상\n ======================================== */\n\n:deep(.ag-root-wrapper) {\n border: 1px solid hsl(var(--border));\n border-radius: 0.375rem;\n}\n\n/* ── Header ──────────────────────────────────────────────────────────── */\n:deep(.ag-header) {\n border-bottom: 1px solid hsl(var(--border));\n font-weight: 500;\n}\n\n:deep(.ag-header-row:not(.ag-floating-filter)) {\n min-height: var(--j-grid-header-h) !important;\n height: var(--j-grid-header-h) !important;\n}\n\n:deep(.ag-header-cell) {\n color: hsl(var(--foreground));\n font-size: 0.75rem;\n padding: 0 0.5rem;\n display: flex;\n align-items: center;\n}\n\n/* Floating Filter: 헤더와 명확히 구분되는 입력 영역 */\n:deep(.ag-header-row.ag-floating-filter) {\n min-height: var(--j-grid-filter-h) !important;\n height: var(--j-grid-filter-h) !important;\n background-color: hsl(var(--card));\n border-top: 1px solid hsl(var(--border) / 0.5);\n}\n\n:deep(.ag-floating-filter-body) {\n min-height: var(--j-grid-filter-h);\n}\n\n:deep(.ag-floating-filter-body input),\n:deep(.ag-floating-filter-body .ag-input-field-input),\n:deep(.ag-floating-filter-body .ag-picker-field-wrapper) {\n height: 20px !important;\n min-height: 20px !important;\n font-size: 0.75rem;\n background-color: hsl(var(--background)) !important;\n border: 1px solid hsl(var(--border)) !important;\n border-radius: 3px !important;\n padding: 0 4px !important;\n color: hsl(var(--foreground)) !important;\n}\n\n:deep(.ag-floating-filter-body input:focus),\n:deep(.ag-floating-filter-body .ag-input-field-input:focus) {\n border-color: hsl(var(--primary)) !important;\n outline: none !important;\n}\n\n/* ── Rows ────────────────────────────────────────────────────────────── */\n:deep(.ag-row) {\n min-height: var(--j-grid-row-h) !important;\n height: var(--j-grid-row-h) !important;\n transition: background-color 0.15s ease, box-shadow 0.15s ease;\n cursor: pointer;\n}\n\n/* 선택된 행: 왼쪽 accent 기둥으로 명확한 시각적 선택 표시 (라이트/다크 모두) */\n:deep(.ag-row-selected) {\n box-shadow: inset 3px 0 0 hsl(var(--primary));\n}\n\n/* ── Cells ───────────────────────────────────────────────────────────── */\n:deep(.ag-cell) {\n line-height: 1.2;\n padding: 0 0.5rem;\n font-size: 0.75rem;\n border-bottom: 1px solid hsl(var(--border) / 0.5);\n}\n\n:deep(.ag-cell-wrapper) {\n align-items: center;\n}\n\n:deep(.ag-cell-value) {\n display: flex;\n align-items: center;\n min-height: 100%;\n}\n\n/* 행 클릭 시 row number 셀에 보이는 강한 포커스 음영 제거 */\n:deep(.ag-cell.ag-row-number-cell.ag-cell-focus),\n:deep(.ag-cell.ag-row-number-cell:focus-within) {\n box-shadow: none !important;\n outline: none !important;\n}\n\n/* 셀 포커스 */\n:deep(.ag-cell.ag-cell-focus) {\n box-shadow: inset 0 0 0 1px hsl(var(--ring) / 0.5) !important;\n}\n\n/* 인라인 편집 셀 */\n:deep(.ag-cell-inline-editing) {\n padding: 0 !important;\n border: none !important;\n box-shadow: inset 0 0 0 1px hsl(var(--primary)) !important;\n background-color: hsl(var(--background)) !important;\n}\n\n:deep(.ag-cell-inline-editing .ag-cell-editor),\n:deep(.ag-cell-inline-editing .ag-text-field-input-wrapper),\n:deep(.ag-cell-inline-editing .ag-input-field) {\n height: 100% !important;\n border: none !important;\n outline: none !important;\n background: transparent !important;\n box-shadow: none !important;\n}\n\n:deep(.ag-cell-inline-editing input) {\n height: 100% !important;\n width: 100% !important;\n padding: 0 0.5rem !important;\n border: none !important;\n outline: none !important;\n background: transparent !important;\n box-shadow: none !important;\n font-size: 0.75rem !important;\n color: hsl(var(--foreground)) !important;\n}\n\n/* ── Checkbox 셀 중앙정렬 ────────────────────────────────────────────── */\n:deep(.ag-cell-wrapper:has(.ag-selection-checkbox)) {\n justify-content: center;\n}\n\n:deep(.ag-header-cell:has(.ag-header-select-all) .ag-header-cell-comp-wrapper) {\n justify-content: center;\n}\n\n/* ── Action Buttons (JS 렌더러 — CSS variable 브릿지) ─────────────────── */\n:deep(.j-action-btn) {\n padding: 1px 8px;\n font-size: 0.75rem;\n border: 1px solid hsl(var(--border));\n border-radius: 3px;\n background-color: hsl(var(--card));\n color: hsl(var(--foreground));\n cursor: pointer;\n transition: background-color 0.15s ease;\n white-space: nowrap;\n}\n\n:deep(.j-action-btn:hover) {\n background-color: hsl(var(--accent));\n}\n\n:deep(.j-action-btn-danger) {\n border-color: hsl(var(--destructive) / 0.4);\n background-color: hsl(var(--destructive) / 0.06);\n color: hsl(var(--destructive));\n}\n\n:deep(.j-action-btn-danger:hover) {\n background-color: hsl(var(--destructive) / 0.15);\n}\n\n/* ── Pagination ──────────────────────────────────────────────────────── */\n:deep(.ag-paging-panel) {\n border-top: 1px solid hsl(var(--border));\n}\n</style>\n"],"names":["ModuleRegistry","AllCommunityModule","AllEnterpriseModule","jTheme","themeQuartz","isDark","ref","gridContainerRef","darkObserver","watch","dark","onMounted","onUnmounted","props","__props","emit","__emit","gridApi","gridColumnApi","ActionButtonsCellRenderer","params","buttons","rowData","visibleButtons","btn","container","button","e","actionButtonsColumn","computed","processedColumnDefs","columns","target","gridOptions","useFloatingFilters","data","__expose","onGridReady","onRowClicked","event","onRowDoubleClicked","onCellClicked","onSelectionChanged","onCellValueChanged","_createElementBlock","_normalizeClass","_unref","cn","_createVNode","AgGridVue"],"mappings":"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"}
1
+ {"version":3,"file":"JGrid.vue2.js","sources":["../../../../src/components/atoms/JGrid.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch, onMounted, onUnmounted } from 'vue'\nimport { AgGridVue } from 'ag-grid-vue3'\nimport { cn } from '@/lib/utils'\nimport type {\n ColDef,\n GridOptions,\n RowClickedEvent,\n CellClickedEvent,\n SelectionChangedEvent,\n CellValueChangedEvent,\n RowDoubleClickedEvent,\n GridReadyEvent,\n ICellRendererParams,\n} from 'ag-grid-community'\nimport { ModuleRegistry, AllCommunityModule, themeQuartz } from 'ag-grid-community'\n// Enterprise 모듈 import (Grouping, Pivot, Excel Export 등)\nimport { AllEnterpriseModule } from 'ag-grid-enterprise'\n\n// AG Grid 모듈 등록 (Community + Enterprise)\nModuleRegistry.registerModules([AllCommunityModule, AllEnterpriseModule])\n\n// ── 공식 권장: data-ag-theme-mode attribute 기반 다크모드 전환 ─────────────────\n// AG Grid v33 공식 패턴:\n// 1. themeQuartz.withParams({...}, 'light').withParams({...}, 'dark') 로 두 모드 정의\n// 2. data-ag-theme-mode attribute 전환으로 모드 스위칭 (theme 객체 재생성 불필요)\n// 3. CSS --ag-* 변수 오버라이드로 앱 CSS 변수 참조 (getCSSVar 우회 불필요)\nconst jTheme = themeQuartz\n .withParams({\n // 공통 사이즈 (라이트/다크 모두 적용)\n cellHorizontalPaddingScale: 1,\n columnBorder: true,\n fontSize: 13,\n headerFontWeight: 500,\n headerVerticalPaddingScale: 0.45,\n iconSize: 14,\n rowVerticalPaddingScale: 0.4,\n })\n .withParams({\n browserColorScheme: 'light',\n }, 'light')\n .withParams({\n browserColorScheme: 'dark',\n }, 'dark')\n\n// JLIS는 .dark class 기반 다크모드이므로 MutationObserver로 감지 후 attribute 동기화\nconst isDark = ref(typeof document !== 'undefined' && document.documentElement.classList.contains('dark'))\nconst gridContainerRef = ref<HTMLElement | null>(null)\nlet darkObserver: MutationObserver | null = null\n\n// isDark 변경 → data-ag-theme-mode 동기화 (AG Grid가 내부적으로 반응)\nwatch(isDark, (dark) => {\n gridContainerRef.value?.setAttribute('data-ag-theme-mode', dark ? 'dark' : 'light')\n})\n\nonMounted(() => {\n // 초기 attribute 설정\n gridContainerRef.value?.setAttribute('data-ag-theme-mode', isDark.value ? 'dark' : 'light')\n\n // 이후 변경 감지\n darkObserver = new MutationObserver(() => {\n isDark.value = document.documentElement.classList.contains('dark')\n })\n darkObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] })\n})\n\nonUnmounted(() => {\n darkObserver?.disconnect()\n})\n\n/**\n * Action Button 정의 타입\n */\nexport type ActionButton = {\n /** 버튼 아이콘 이름 (lucide) - label이 없을 때 기본 텍스트 생성에 사용 */\n icon?: string\n /** 버튼 라벨 */\n label?: string\n /** 툴팁 텍스트 */\n tooltip?: string\n /** 버튼 클릭 핸들러 (rowData 전달) */\n onClick: (rowData: any) => void\n /** 버튼 스타일 타입 */\n styletype?: 'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'danger'\n /** 조건부 표시 함수 (rowData를 받아 boolean 반환) */\n show?: (rowData: any) => boolean\n}\n\nconst props = withDefaults(\n defineProps<{\n /** 추가 클래스 (외부 커스터마이징용) */\n class?: string\n /** 그리드에 표시할 데이터 배열 */\n rowData: any[]\n /** 컬럼 정의 배열 */\n columnDefs: ColDef[]\n /** 페이지네이션 활성화 여부 */\n pagination?: boolean\n /** 체크박스 선택 활성화 여부 */\n checkbox?: boolean\n /** 행 번호 표시 여부 (Enterprise) */\n rowNumbers?: boolean\n /** 플로팅 필터 표시 여부 */\n floatingFilters?: boolean\n /** 플로팅 필터 표시 여부 (하위호환 alias) */\n floatingFilter?: boolean\n /** 행 번호 컬럼 너비(px) */\n rowNumberWidth?: number\n /** 행별 액션 버튼 목록 */\n actionButtons?: ActionButton[]\n /** 요약 컬럼 표시 여부 */\n summaryColumn?: boolean\n /** 숨김 컬럼 관리 활성화 여부 */\n hiddenColumn?: boolean\n /** 그룹핑 기능 활성화 여부 (Enterprise) */\n enableGrouping?: boolean\n /** 피벗 기능 활성화 여부 (Enterprise) */\n enablePivot?: boolean\n /** Excel 내보내기 기능 활성화 여부 (Enterprise) */\n enableExcelExport?: boolean\n /** Tree Data 기능 활성화 여부 (Enterprise) */\n enableTreeData?: boolean\n /** Tree Data 계층 경로를 반환하는 함수 */\n getDataPath?: (data: any) => (string | number)[]\n /** Tree Data 그룹 컬럼 정의 */\n autoGroupColumnDef?: ColDef\n /** 선택된 행 데이터 배열 (v-model:selected-rows) */\n selectedRows?: any[]\n /** 컬럼 호버 하이라이트 활성화 여부 */\n columnHover?: boolean\n /** Columns Tool Panel 활성화 여부 (Enterprise) */\n enableColumnsToolPanel?: boolean\n /** Status Bar 활성화 여부 (Enterprise) */\n statusBar?: boolean\n /** 간소화된 Footer 모드 (Status Bar 제거, Pagination만 표시) */\n compactFooter?: boolean\n /** Row Group Panel 표시 여부 ('always' | 'onlyWhenGrouping' | 'never') */\n rowGroupPanelShow?: 'always' | 'onlyWhenGrouping' | 'never'\n /** 그룹 기본 확장 레벨 (-1: 모두 닫힘, 0: 첫 레벨만, 1: 2레벨까지...) */\n groupDefaultExpanded?: number\n /** Pivot Mode Panel 표시 여부 */\n pivotPanelShow?: 'always' | 'onlyWhenPivoting' | 'never'\n /** Pivot Mode 활성화 여부 */\n pivotMode?: boolean\n }>(),\n {\n pagination: true,\n checkbox: false,\n rowNumbers: true,\n floatingFilters: true,\n floatingFilter: undefined,\n rowNumberWidth: 38,\n actionButtons: undefined,\n summaryColumn: false,\n hiddenColumn: false,\n enableGrouping: true,\n enablePivot: false,\n enableExcelExport: false,\n enableTreeData: false,\n getDataPath: undefined,\n autoGroupColumnDef: undefined,\n selectedRows: () => [],\n columnHover: true,\n enableColumnsToolPanel: true,\n statusBar: true,\n compactFooter: false,\n rowGroupPanelShow: 'never',\n groupDefaultExpanded: -1,\n pivotPanelShow: 'never',\n pivotMode: false,\n },\n)\n\nconst emit = defineEmits<{\n /** 행 클릭 이벤트 */\n rowClicked: [event: RowClickedEvent]\n /** 행 더블클릭 이벤트 */\n rowDoubleClicked: [event: RowDoubleClickedEvent]\n /** 셀 클릭 이벤트 */\n cellClicked: [event: CellClickedEvent]\n /** 선택 변경 이벤트 (체크박스 등) */\n selectionChanged: [event: SelectionChangedEvent]\n /** 셀 값 변경 이벤트 */\n cellValueChanged: [event: CellValueChangedEvent]\n /** 그리드 준비 완료 이벤트 */\n gridReady: [event: GridReadyEvent]\n /** 선택된 행 변경 이벤트 (v-model:selected-rows) */\n 'update:selectedRows': [rows: any[]]\n}>()\n\n// ag-Grid 인스턴스 참조\nconst gridApi = ref<any>(null)\nconst gridColumnApi = ref<any>(null)\n\n// Action Buttons Cell Renderer - 함수형으로 DOM 직접 생성\nconst ActionButtonsCellRenderer = (params: ICellRendererParams) => {\n const buttons = props.actionButtons || []\n const rowData = params.data\n \n // 표시할 버튼 필터링\n const visibleButtons = buttons.filter(btn => {\n if (btn.show) {\n return btn.show(rowData)\n }\n return true\n })\n \n if (visibleButtons.length === 0) {\n return ''\n }\n \n // 컨테이너 div 생성\n const container = document.createElement('div')\n container.className = 'flex items-center gap-1'\n \n // 각 버튼 생성\n visibleButtons.forEach((btn) => {\n const button = document.createElement('button')\n button.className = btn.styletype === 'danger' ? 'j-action-btn j-action-btn-danger' : 'j-action-btn'\n \n // tooltip\n if (btn.tooltip) {\n button.title = btn.tooltip\n }\n \n // 라벨 추가 (텍스트 버튼)\n if (btn.label) {\n button.textContent = btn.label\n } else if (btn.icon) {\n // 라벨이 없으면 기본 텍스트 생성\n if (btn.icon === 'pencil') {\n button.textContent = '수정'\n } else if (btn.icon === 'trash2' || btn.icon === 'trash') {\n button.textContent = '삭제'\n } else if (btn.icon === 'eye') {\n button.textContent = '보기'\n } else if (btn.icon === 'copy') {\n button.textContent = '복사'\n } else if (btn.icon === 'download') {\n button.textContent = '다운로드'\n } else if (btn.icon === 'circleX') {\n button.textContent = '비활성화'\n } else if (btn.icon === 'circleCheckBig') {\n button.textContent = '활성화'\n } else {\n button.textContent = btn.icon\n }\n }\n \n // 클릭 이벤트\n button.addEventListener('click', (e) => {\n e.stopPropagation()\n btn.onClick(rowData)\n })\n \n container.appendChild(button)\n })\n \n return container\n}\n\n// Action Buttons 컬럼 정의\nconst actionButtonsColumn = computed<ColDef | null>(() => {\n if (!props.actionButtons || props.actionButtons.length === 0) {\n return null\n }\n \n return {\n colId: 'actionButtons',\n headerName: '작업',\n field: '_actions',\n width: 120,\n minWidth: 80,\n maxWidth: 200,\n lockPosition: 'left' as const,\n sortable: false,\n filter: false,\n resizable: true,\n suppressNavigable: true,\n suppressHeaderMenuButton: true,\n cellRenderer: ActionButtonsCellRenderer,\n cellStyle: { display: 'flex', justifyContent: 'center', alignItems: 'center' },\n }\n})\n\n// checkbox 활성화 및 추가 컬럼 처리\nconst processedColumnDefs = computed(() => {\n const columns: ColDef[] = []\n \n // 1. Checkbox 컬럼 (최우선)\n if (props.checkbox) {\n columns.push({\n colId: 'rowSelection',\n headerName: '',\n // field와 valueGetter 제거 - AG Grid 공식 방식\n width: 40,\n minWidth: 40,\n maxWidth: 40,\n lockPosition: 'left' as const,\n checkboxSelection: true,\n headerCheckboxSelection: true,\n sortable: false,\n filter: false,\n resizable: false,\n suppressNavigable: true,\n suppressHeaderMenuButton: true,\n cellStyle: { display: 'flex', justifyContent: 'center', alignItems: 'center' },\n // 체크박스 셀 클릭 시 토글 (checkbox 아이콘 클릭은 AG Grid 기본 처리)\n onCellClicked: (params: CellClickedEvent) => {\n const target = (params.event as MouseEvent)?.target as HTMLElement\n if (target && !target.closest('.ag-checkbox-input-wrapper')) {\n params.node.setSelected(!params.node.isSelected())\n }\n },\n })\n }\n \n // 2. Action Buttons 컬럼\n if (actionButtonsColumn.value) {\n columns.push(actionButtonsColumn.value)\n }\n \n // 3. 사용자 정의 컬럼들 (Row Numbers는 AG Grid가 자동으로 추가)\n columns.push(...props.columnDefs)\n \n return columns\n})\n\n// Grid 옵션 설정\nconst gridOptions = computed<GridOptions>(() => {\n const useFloatingFilters = props.floatingFilter ?? props.floatingFilters\n const options: GridOptions = {\n theme: jTheme,\n pagination: props.pagination,\n defaultColDef: {\n filter: true,\n floatingFilter: useFloatingFilters,\n },\n rowSelection: props.checkbox ? 'multiple' : undefined,\n // 체크박스 모드일 때 row 클릭으로 선택이 변경되지 않도록 설정\n // 체크박스만으로 선택을 제어하도록 함\n suppressRowClickSelection: props.checkbox,\n\n // Row Numbers (Enterprise) - AG Grid 표준 방식\n rowNumbers: props.rowNumbers\n ? ({\n minWidth: props.rowNumberWidth,\n width: props.rowNumberWidth,\n } as any)\n : false,\n\n // Column Hover Highlight\n columnHoverHighlight: props.columnHover,\n\n // Enterprise 기능 옵션\n sideBar: props.enableColumnsToolPanel || props.enableGrouping || props.enablePivot ? {\n toolPanels: [\n {\n id: 'columns',\n labelDefault: 'Columns',\n labelKey: 'columns',\n iconKey: 'columns',\n toolPanel: 'agColumnsToolPanel',\n toolPanelParams: {\n suppressRowGroups: !props.enableGrouping,\n suppressValues: !props.enablePivot,\n suppressPivots: !props.enablePivot,\n suppressPivotMode: !props.enablePivot,\n },\n },\n ],\n defaultToolPanel: '', // 초기에는 접힌 상태\n } : undefined,\n\n // Status Bar (Enterprise)\n // compactFooter 모드에서는 Status Bar 비활성화\n statusBar: (props.statusBar && !props.compactFooter) ? {\n statusPanels: [\n { statusPanel: 'agTotalAndFilteredRowCountComponent', align: 'left' as const },\n { statusPanel: 'agSelectedRowCountComponent', align: 'left' as const },\n { statusPanel: 'agAggregationComponent', align: 'right' as const },\n ],\n } : undefined,\n\n // Row Group Panel 설정 (Enterprise) - 그리드 상단에 드래그 영역 표시\n rowGroupPanelShow: props.rowGroupPanelShow !== 'never' ? props.rowGroupPanelShow : undefined,\n\n // Pivot Panel 설정 (Enterprise) - 피벗 모드용 드래그 영역\n pivotPanelShow: props.pivotPanelShow !== 'never' ? props.pivotPanelShow : undefined,\n \n // Pivot Mode 활성화\n pivotMode: props.pivotMode,\n\n // 그룹핑 기본 설정\n groupDefaultExpanded: props.groupDefaultExpanded,\n suppressAggFuncInHeader: false,\n\n // Tree Data 설정 (Enterprise)\n treeData: props.enableTreeData || undefined,\n getDataPath: props.enableTreeData\n ? (props.getDataPath || ((data: any) => data.path || []))\n : undefined,\n autoGroupColumnDef: props.enableTreeData && props.autoGroupColumnDef\n ? props.autoGroupColumnDef\n : undefined,\n }\n\n return options\n})\n\n// Excel 내보내기 함수 (외부에서 사용 가능하도록 expose)\nconst exportToExcel = () => {\n if (gridApi.value && props.enableExcelExport) {\n gridApi.value.exportDataAsExcel({\n fileName: 'grid-export.xlsx',\n })\n }\n}\n\n// 그리드 API를 외부에 노출\ndefineExpose({\n gridApi,\n gridColumnApi,\n exportToExcel,\n})\n\n// Grid ready 이벤트 핸들러\nconst onGridReady = (params: GridReadyEvent) => {\n gridApi.value = params.api\n gridColumnApi.value = params.api // v34에서 columnApi는 deprecated\n emit('gridReady', params)\n}\n\n// 행 클릭 이벤트 핸들러\nconst onRowClicked = (event: RowClickedEvent) => {\n emit('rowClicked', event)\n}\n\n// 행 더블클릭 이벤트 핸들러\nconst onRowDoubleClicked = (event: RowDoubleClickedEvent) => {\n emit('rowDoubleClicked', event)\n}\n\n// 셀 클릭 이벤트 핸들러\nconst onCellClicked = (event: CellClickedEvent) => {\n emit('cellClicked', event)\n}\n\n// 선택 변경 이벤트 핸들러\nconst onSelectionChanged = (event: SelectionChangedEvent) => {\n emit('selectionChanged', event)\n emit('update:selectedRows', event.api.getSelectedRows())\n}\n\n// 셀 값 변경 이벤트 핸들러\nconst onCellValueChanged = (event: CellValueChangedEvent) => {\n emit('cellValueChanged', event)\n}\n\n// columnDefs 변경 감지\nwatch(\n () => props.columnDefs,\n () => {\n if (gridApi.value) {\n gridApi.value.setGridOption('columnDefs', processedColumnDefs.value)\n }\n },\n { deep: true },\n)\n\n// rowData 변경 감지\nwatch(\n () => props.rowData,\n () => {\n if (gridApi.value) {\n gridApi.value.setGridOption('rowData', props.rowData)\n }\n },\n { deep: true },\n)\n</script>\n\n<template>\n <div ref=\"gridContainerRef\" :class=\"cn('ag-grid-container', props.class)\">\n <AgGridVue\n :column-defs=\"processedColumnDefs\"\n :row-data=\"rowData\"\n :grid-options=\"gridOptions\"\n style=\"height: 100%; width: 100%\"\n @grid-ready=\"onGridReady\"\n @row-clicked=\"onRowClicked\"\n @row-double-clicked=\"onRowDoubleClicked\"\n @cell-clicked=\"onCellClicked\"\n @selection-changed=\"onSelectionChanged\"\n @cell-value-changed=\"onCellValueChanged\"\n />\n </div>\n</template>\n\n<style scoped>\n.ag-grid-container {\n width: 100%;\n height: 100%;\n\n /*\n * App CSS 변수 → AG Grid CSS 변수 브릿지\n * CSS 변수는 런타임 해소 → .dark 클래스 전환 시 라이트/다크 자동 대응\n * AG Grid v33 공식 CSS variable 명칭 사용\n */\n --ag-background-color: hsl(var(--background));\n --ag-foreground-color: hsl(var(--foreground));\n --ag-border-color: hsl(var(--border));\n --ag-header-background-color: hsl(var(--muted));\n --ag-header-foreground-color: hsl(var(--foreground));\n --ag-odd-row-background-color: hsl(var(--card));\n --ag-row-hover-color: hsl(var(--accent));\n --ag-selected-row-background-color: hsl(var(--primary) / 0.12);\n --ag-accent-color: hsl(var(--primary));\n --ag-header-height: var(--j-grid-header-h);\n --ag-row-height: var(--j-grid-row-h);\n}\n\n/* ============================================\n COMPACT FOOTER: Status Bar + Pagination 통합\n ============================================ */\n\n/* Status Bar 높이 줄이기 */\n:deep(.ag-status-bar) {\n min-height: var(--j-grid-footer-h) !important;\n height: var(--j-grid-footer-h) !important;\n padding: 0 12px !important;\n border-top: 1px solid var(--ag-border-color);\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-size: 0.8125rem;\n}\n\n/* Status Bar 컴포넌트들 높이 조정 */\n:deep(.ag-status-bar-left),\n:deep(.ag-status-bar-center),\n:deep(.ag-status-bar-right) {\n height: var(--j-grid-footer-h);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n/* Status Bar 패널들 간격 조정 */\n:deep(.ag-status-panel) {\n padding: 0;\n height: var(--j-grid-footer-h);\n display: flex;\n align-items: center;\n}\n\n/* Pagination Panel 높이 줄이기 */\n:deep(.ag-paging-panel) {\n min-height: var(--j-grid-footer-h) !important;\n height: var(--j-grid-footer-h) !important;\n padding: 0 12px !important;\n border-top: 1px solid var(--ag-border-color);\n font-size: 0.8125rem;\n}\n\n/* Pagination 컴포넌트들 높이 조정 */\n:deep(.ag-paging-page-size),\n:deep(.ag-paging-row-summary-panel) {\n height: var(--j-grid-footer-h);\n display: flex;\n align-items: center;\n}\n\n/* Page Size Selector 높이 조정 */\n:deep(.ag-paging-page-size .ag-picker-field) {\n height: 24px;\n min-height: 24px;\n}\n\n:deep(.ag-paging-page-size .ag-picker-field-wrapper) {\n height: 24px;\n padding: 0 4px;\n}\n\n/* Pagination 버튼들 높이 조정 */\n:deep(.ag-paging-button) {\n height: 24px;\n width: 24px;\n padding: 2px;\n}\n\n/* Pagination 버튼 래퍼 */\n:deep(.ag-paging-button-wrapper) {\n height: var(--j-grid-footer-h);\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n/* Row summary 텍스트 */\n:deep(.ag-paging-row-summary-panel-number) {\n line-height: var(--j-grid-footer-h);\n}\n\n/* ========================================\n 패턴 9: AG-Grid 스타일 향상\n ======================================== */\n\n:deep(.ag-root-wrapper) {\n border: 1px solid hsl(var(--border));\n border-radius: 0.375rem;\n}\n\n/* ── Header ──────────────────────────────────────────────────────────── */\n:deep(.ag-header) {\n border-bottom: 1px solid hsl(var(--border));\n font-weight: 500;\n}\n\n:deep(.ag-header-row:not(.ag-floating-filter)) {\n min-height: var(--j-grid-header-h) !important;\n height: var(--j-grid-header-h) !important;\n}\n\n:deep(.ag-header-cell) {\n color: hsl(var(--foreground));\n font-size: 0.75rem;\n padding: 0 0.5rem;\n display: flex;\n align-items: center;\n}\n\n/* Floating Filter: 헤더와 명확히 구분되는 입력 영역 */\n:deep(.ag-header-row.ag-floating-filter) {\n min-height: var(--j-grid-filter-h) !important;\n height: var(--j-grid-filter-h) !important;\n background-color: hsl(var(--card));\n border-top: 1px solid hsl(var(--border) / 0.5);\n}\n\n:deep(.ag-floating-filter-body) {\n min-height: var(--j-grid-filter-h);\n}\n\n:deep(.ag-floating-filter-body input),\n:deep(.ag-floating-filter-body .ag-input-field-input),\n:deep(.ag-floating-filter-body .ag-picker-field-wrapper) {\n height: 20px !important;\n min-height: 20px !important;\n font-size: 0.75rem;\n background-color: hsl(var(--background)) !important;\n border: 1px solid hsl(var(--border)) !important;\n border-radius: 3px !important;\n padding: 0 4px !important;\n color: hsl(var(--foreground)) !important;\n}\n\n:deep(.ag-floating-filter-body input:focus),\n:deep(.ag-floating-filter-body .ag-input-field-input:focus) {\n border-color: hsl(var(--primary)) !important;\n outline: none !important;\n}\n\n/* ── Rows ────────────────────────────────────────────────────────────── */\n:deep(.ag-row) {\n min-height: var(--j-grid-row-h) !important;\n height: var(--j-grid-row-h) !important;\n transition: background-color 0.15s ease, box-shadow 0.15s ease;\n cursor: pointer;\n}\n\n/* 선택된 행: 왼쪽 accent 기둥으로 명확한 시각적 선택 표시 (라이트/다크 모두) */\n:deep(.ag-row-selected) {\n box-shadow: inset 3px 0 0 hsl(var(--primary));\n}\n\n/* ── Cells ───────────────────────────────────────────────────────────── */\n:deep(.ag-cell) {\n line-height: 1.2;\n padding: 0 0.5rem;\n font-size: 0.75rem;\n border-bottom: 1px solid hsl(var(--border) / 0.5);\n}\n\n:deep(.ag-cell-wrapper) {\n align-items: center;\n}\n\n:deep(.ag-cell-value) {\n display: flex;\n align-items: center;\n min-height: 100%;\n}\n\n/* 행 클릭 시 row number 셀에 보이는 강한 포커스 음영 제거 */\n:deep(.ag-cell.ag-row-number-cell.ag-cell-focus),\n:deep(.ag-cell.ag-row-number-cell:focus-within) {\n box-shadow: none !important;\n outline: none !important;\n}\n\n/* 셀 포커스 */\n:deep(.ag-cell.ag-cell-focus) {\n box-shadow: inset 0 0 0 1px hsl(var(--ring) / 0.5) !important;\n}\n\n/* 인라인 편집 셀 */\n:deep(.ag-cell-inline-editing) {\n padding: 0 !important;\n border: none !important;\n box-shadow: inset 0 0 0 1px hsl(var(--primary)) !important;\n background-color: hsl(var(--background)) !important;\n}\n\n:deep(.ag-cell-inline-editing .ag-cell-editor),\n:deep(.ag-cell-inline-editing .ag-text-field-input-wrapper),\n:deep(.ag-cell-inline-editing .ag-input-field) {\n height: 100% !important;\n border: none !important;\n outline: none !important;\n background: transparent !important;\n box-shadow: none !important;\n}\n\n:deep(.ag-cell-inline-editing input) {\n height: 100% !important;\n width: 100% !important;\n padding: 0 0.5rem !important;\n border: none !important;\n outline: none !important;\n background: transparent !important;\n box-shadow: none !important;\n font-size: 0.75rem !important;\n color: hsl(var(--foreground)) !important;\n}\n\n/* ── Checkbox 셀 중앙정렬 (col-id 속성 선택자 — 신뢰할 수 있는 접근) ── */\n:deep(.ag-cell[col-id=\"rowSelection\"]) {\n display: flex !important;\n justify-content: center !important;\n align-items: center !important;\n padding: 0 !important;\n}\n:deep(.ag-cell[col-id=\"rowSelection\"] .ag-cell-wrapper) {\n width: auto !important;\n flex: none !important;\n}\n:deep(.ag-header-cell[col-id=\"rowSelection\"] .ag-header-cell-comp-wrapper) {\n justify-content: center !important;\n}\n\n/* ── Action Buttons (JS 렌더러 — CSS variable 브릿지) ─────────────────── */\n:deep(.j-action-btn) {\n padding: 1px 8px;\n font-size: 0.75rem;\n border: 1px solid hsl(var(--border));\n border-radius: 3px;\n background-color: hsl(var(--card));\n color: hsl(var(--foreground));\n cursor: pointer;\n transition: background-color 0.15s ease;\n white-space: nowrap;\n}\n\n:deep(.j-action-btn:hover) {\n background-color: hsl(var(--accent));\n}\n\n:deep(.j-action-btn-danger) {\n border-color: hsl(var(--destructive) / 0.4);\n background-color: hsl(var(--destructive) / 0.06);\n color: hsl(var(--destructive));\n}\n\n:deep(.j-action-btn-danger:hover) {\n background-color: hsl(var(--destructive) / 0.15);\n}\n\n/* ── Pagination ──────────────────────────────────────────────────────── */\n:deep(.ag-paging-panel) {\n border-top: 1px solid hsl(var(--border));\n}\n</style>\n"],"names":["ModuleRegistry","AllCommunityModule","AllEnterpriseModule","jTheme","themeQuartz","isDark","ref","gridContainerRef","darkObserver","watch","dark","onMounted","onUnmounted","props","__props","emit","__emit","gridApi","gridColumnApi","ActionButtonsCellRenderer","params","buttons","rowData","visibleButtons","btn","container","button","e","actionButtonsColumn","computed","processedColumnDefs","columns","target","gridOptions","useFloatingFilters","data","__expose","onGridReady","onRowClicked","event","onRowDoubleClicked","onCellClicked","onSelectionChanged","onCellValueChanged","_createElementBlock","_normalizeClass","_unref","cn","_createVNode","AgGridVue"],"mappings":"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"}
@@ -3,5 +3,5 @@
3
3
  for (const [t_key, t_val] of t_opts)
4
4
  t_merged[t_key] = t_val;
5
5
  return t_merged;
6
- };,u=t(e.default,[["__scopeId","data-v-f600f461"]]);exports.default=u;
6
+ };,u=t(e.default,[["__scopeId","data-v-807f93ad"]]);exports.default=u;
7
7
  //# sourceMappingURL=JFormField.vue.cjs.map
@@ -6,8 +6,8 @@ const r = (r_comp, r_opts) => {
6
6
  r_merged[r_key] = r_val;
7
7
  return r_merged;
8
8
  };
9
- const p = /* @__PURE__ */ r(o, [["__scopeId", "data-v-f600f461"]]);
9
+ const f = /* @__PURE__ */ r(o, [["__scopeId", "data-v-807f93ad"]]);
10
10
  export {
11
- p as default
11
+ f as default
12
12
  };
13
13
  //# sourceMappingURL=JFormField.vue.js.map
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue");require("../shadcn/index.cjs");require("lucide-vue-next");;/* empty css */const h=require("../atoms/JInput.vue.cjs"),F=require("../atoms/JTextarea.vue.cjs"),N=require("../atoms/JCheckbox.vue.cjs"),z=require("../atoms/JCombo.vue.cjs"),T=require("../atoms/JSearchCombo.vue.cjs"),S=require("../atoms/JRadio.vue.cjs"),J=require("../atoms/JSwitch.vue.cjs"),M=require("../atoms/JDatepicker.vue.cjs");require("md-editor-v3");;/* empty css */;/* empty css */const P=require("../../lib/utils.cjs");require("../shadcn/badge-variants.cjs");require("@vueuse/core");require("reka-ui");;/* empty css */require("../shadcn/avatar-variants.cjs");require("dompurify");;/* empty css */require("ag-grid-vue3");require("ag-grid-community");require("ag-grid-enterprise");;/* empty css */;/* empty css */;/* empty css */require("vue-sonner");const i=require("../shadcn/FieldGroup.vue.cjs"),g=require("../shadcn/Field.vue.cjs"),k=require("../shadcn/FieldLabel.vue.cjs"),x=require("../shadcn/FieldContent.vue.cjs"),E=require("../shadcn/FieldDescription.vue.cjs"),L=require("../shadcn/FieldError.vue.cjs"),j={key:0,class:"text-destructive ml-1"},A=e.defineComponent({__name:"JFormField",props:{class:{},label:{},description:{},errorMsg:{},type:{default:"input"},inlineLabel:{},orientation:{default:"vertical"},labelAlign:{default:"left"},labelWidth:{default:void 0},id:{},modelValue:{},placeholder:{},disabled:{type:Boolean},readonly:{type:Boolean},required:{type:Boolean},name:{},styleType:{},inputType:{},options:{},multiple:{type:Boolean},radioDirection:{default:"horizontal"}},emits:["update:modelValue","change","focus","blur"],setup(r,{expose:q,emit:C}){const b=["class","label","description","errorMsg","type","inlineLabel","orientation","labelAlign","labelWidth","radioDirection"],t=r,u=C,o=e.ref(""),c=e.computed(()=>t.errorMsg||o.value),s=e.computed(()=>{const l={},a=t;for(const n in a)b.includes(n)||(l[n]=a[n]);if(t.inputType&&t.type==="input"&&(l.type=t.inputType,delete l.inputType,!t.placeholder)){const n={text:"텍스트를 입력하세요",email:"이메일을 입력하세요",password:"비밀번호를 입력하세요",tel:"전화번호를 입력하세요",url:"URL을 입력하세요",number:"숫자를 입력하세요",search:"검색어를 입력하세요",date:"날짜를 선택하세요",time:"시간을 선택하세요","datetime-local":"날짜와 시간을 선택하세요",month:"월을 선택하세요",week:"주를 선택하세요"};l.placeholder=n[t.inputType]||"입력하세요"}return t.radioDirection&&t.type==="radio"&&(l.styletype=t.radioDirection,delete l.radioDirection),l}),d=l=>{if(!t.required)return;o.value="";const a=l!==void 0?l:t.modelValue;t.type==="checkbox"||t.type==="switch"?a!=="Y"&&(o.value="필수 항목입니다."):(a==null||a==="")&&(o.value="필수 입력 항목입니다.")},B=e.computed(()=>!(t.modelValue!==null&&t.modelValue!==void 0&&t.modelValue!=="")),p=l=>{u("update:modelValue",l),d(l)},f=l=>{u("change",l),d(l)},_=l=>{u("focus",l)},m=l=>{B.value&&d(),u("blur",l)},V=e.computed(()=>{const l={left:"justify-start",middle:"justify-center",right:"justify-end"},a={left:"text-left",middle:"text-center",right:"text-right"};return t.orientation==="horizontal"?l[t.labelAlign]:a[t.labelAlign]}),w=e.computed(()=>{switch(t.styleType){case"md":return"form-density-md";case"lg":return"form-density-lg";default:return"form-density-sm"}}),v=e.computed(()=>{const l="h-[var(--ctl-h)] leading-[var(--ctl-h)]";return t.type==="datepicker"?`${l} max-w-xs`:t.type==="radio"&&t.radioDirection==="vertical"?"":l}),D={input:h.default,textarea:F.default,checkbox:N.default,switch:J.default,combo:z.default,radio:S.default,searchCombo:T.default,datepicker:M.default},y=e.computed(()=>D[t.type]||h.default);return q({clearError:()=>{o.value=""}}),(l,a)=>(e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(e.unref(P.cn)("space-y-2 flex-1 min-w-0",w.value,t.class))},[e.createVNode(e.unref(i.default),null,{default:e.withCtx(()=>[e.createVNode(e.unref(g.default),{class:e.normalizeClass([r.orientation==="horizontal"?"grid grid-cols-[var(--label-w,8rem)_1fr] items-start space-y-0 gap-2":"space-y-1 gap-1"]),style:e.normalizeStyle(r.orientation==="horizontal"&&r.labelWidth?`--label-w:${r.labelWidth};`:"")},{default:e.withCtx(()=>[e.createVNode(e.unref(k.default),{for:r.id,class:e.normalizeClass(["text-xs",r.orientation==="horizontal"?"flex items-center h-[var(--ctl-h)] w-full":"flex items-center",V.value])},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(r.label)+" ",1),r.required?(e.openBlock(),e.createElementBlock("span",j,"*")):e.createCommentVNode("",!0)]),_:1},8,["for","class"]),e.createVNode(e.unref(x.default),{class:e.normalizeClass([r.orientation==="horizontal"?"min-h-[var(--ctl-h)] flex flex-col justify-start gap-0.5 mt-0":"space-y-2 gap-0"])},{default:e.withCtx(()=>[r.type==="checkbox"||r.type==="switch"?(e.openBlock(),e.createBlock(e.unref(i.default),{key:0,"data-slot":"checkbox-group"},{default:e.withCtx(()=>[e.createVNode(e.unref(g.default),{orientation:"horizontal",class:"flex gap-2 space-y-0 h-[var(--ctl-h)] leading-[var(--ctl-h)] items-center"},{default:e.withCtx(()=>[(e.openBlock(),e.createBlock(e.resolveDynamicComponent(y.value),e.mergeProps(s.value,{"onUpdate:modelValue":p,onChange:f,onFocus:_,onBlur:m}),null,16)),r.inlineLabel?(e.openBlock(),e.createBlock(e.unref(k.default),{key:0,for:r.id,class:"text-xs font-normal m-0 h-[var(--ctl-h)] leading-[var(--ctl-h)]"},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(r.inlineLabel),1)]),_:1},8,["for"])):e.createCommentVNode("",!0)]),_:1})]),_:1})):r.type==="radio"?(e.openBlock(),e.createBlock(e.unref(i.default),{key:1},{default:e.withCtx(()=>[(e.openBlock(),e.createBlock(e.resolveDynamicComponent(y.value),e.mergeProps(s.value,{"onUpdate:modelValue":p,onChange:f,onFocus:_,onBlur:m,class:v.value}),null,16,["class"]))]),_:1})):(e.openBlock(),e.createBlock(e.unref(i.default),{key:2},{default:e.withCtx(()=>[(e.openBlock(),e.createBlock(e.resolveDynamicComponent(y.value),e.mergeProps(s.value,{"onUpdate:modelValue":p,onChange:f,onFocus:_,onBlur:m,class:v.value}),null,16,["class"]))]),_:1})),r.description||c.value?(e.openBlock(),e.createBlock(e.unref(x.default),{key:3},{default:e.withCtx(()=>[r.description?(e.openBlock(),e.createBlock(e.unref(E.default),{key:0},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(r.description),1)]),_:1})):e.createCommentVNode("",!0),c.value?(e.openBlock(),e.createBlock(e.unref(L.default),{key:1},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(c.value),1)]),_:1})):e.createCommentVNode("",!0)]),_:1})):e.createCommentVNode("",!0)]),_:1},8,["class"])]),_:1},8,["class","style"])]),_:1})],2))}});exports.default=A;
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue");require("../shadcn/index.cjs");require("lucide-vue-next");;/* empty css */const v=require("../atoms/JInput.vue.cjs"),F=require("../atoms/JTextarea.vue.cjs"),z=require("../atoms/JCheckbox.vue.cjs"),N=require("../atoms/JCombo.vue.cjs"),T=require("../atoms/JSearchCombo.vue.cjs"),S=require("../atoms/JRadio.vue.cjs"),J=require("../atoms/JSwitch.vue.cjs"),M=require("../atoms/JDatepicker.vue.cjs");require("md-editor-v3");;/* empty css */;/* empty css */const P=require("../../lib/utils.cjs");require("../shadcn/badge-variants.cjs");require("@vueuse/core");require("reka-ui");;/* empty css */require("../shadcn/avatar-variants.cjs");require("dompurify");;/* empty css */require("ag-grid-vue3");require("ag-grid-community");require("ag-grid-enterprise");;/* empty css */;/* empty css */;/* empty css */require("vue-sonner");const i=require("../shadcn/FieldGroup.vue.cjs"),g=require("../shadcn/Field.vue.cjs"),x=require("../shadcn/FieldLabel.vue.cjs"),k=require("../shadcn/FieldContent.vue.cjs"),E=require("../shadcn/FieldDescription.vue.cjs"),L=require("../shadcn/FieldError.vue.cjs"),j={key:0,class:"text-destructive ml-1"},A=e.defineComponent({__name:"JFormField",props:{class:{},label:{},description:{},errorMsg:{},type:{default:"input"},inlineLabel:{},orientation:{default:"horizontal"},labelAlign:{default:"left"},labelWidth:{default:"80px"},id:{},modelValue:{},placeholder:{},disabled:{type:Boolean},readonly:{type:Boolean},required:{type:Boolean},name:{},styleType:{},inputType:{},options:{},multiple:{type:Boolean},radioDirection:{default:"horizontal"}},emits:["update:modelValue","change","focus","blur"],setup(r,{expose:q,emit:C}){const b=["class","label","description","errorMsg","type","inlineLabel","orientation","labelAlign","labelWidth","radioDirection"],t=r,u=C,o=e.ref(""),c=e.computed(()=>t.errorMsg||o.value),s=e.computed(()=>{const l={},a=t;for(const n in a)b.includes(n)||(l[n]=a[n]);if(t.inputType&&t.type==="input"&&(l.type=t.inputType,delete l.inputType,!t.placeholder)){const n={text:"텍스트를 입력하세요",email:"이메일을 입력하세요",password:"비밀번호를 입력하세요",tel:"전화번호를 입력하세요",url:"URL을 입력하세요",number:"숫자를 입력하세요",search:"검색어를 입력하세요",date:"날짜를 선택하세요",time:"시간을 선택하세요","datetime-local":"날짜와 시간을 선택하세요",month:"월을 선택하세요",week:"주를 선택하세요"};l.placeholder=n[t.inputType]||"입력하세요"}return t.radioDirection&&t.type==="radio"&&(l.styletype=t.radioDirection,delete l.radioDirection),l}),d=l=>{if(!t.required)return;o.value="";const a=l!==void 0?l:t.modelValue;t.type==="checkbox"||t.type==="switch"?a!=="Y"&&(o.value="필수 항목입니다."):(a==null||a==="")&&(o.value="필수 입력 항목입니다.")},B=e.computed(()=>!(t.modelValue!==null&&t.modelValue!==void 0&&t.modelValue!=="")),p=l=>{u("update:modelValue",l),d(l)},f=l=>{u("change",l),d(l)},_=l=>{u("focus",l)},m=l=>{B.value&&d(),u("blur",l)},V=e.computed(()=>{const l={left:"justify-start",middle:"justify-center",right:"justify-end"},a={left:"text-left",middle:"text-center",right:"text-right"};return t.orientation==="horizontal"?l[t.labelAlign]:a[t.labelAlign]}),w=e.computed(()=>{switch(t.styleType){case"md":return"form-density-md";case"lg":return"form-density-lg";default:return"form-density-sm"}}),h=e.computed(()=>{const l="h-[var(--ctl-h)] leading-[var(--ctl-h)]";return t.type==="datepicker"?`${l} max-w-xs`:t.type==="radio"&&t.radioDirection==="vertical"?"":l}),D={input:v.default,textarea:F.default,checkbox:z.default,switch:J.default,combo:N.default,radio:S.default,searchCombo:T.default,datepicker:M.default},y=e.computed(()=>D[t.type]||v.default);return q({clearError:()=>{o.value=""}}),(l,a)=>(e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(e.unref(P.cn)("space-y-2 flex-1 min-w-0",w.value,t.class))},[e.createVNode(e.unref(i.default),null,{default:e.withCtx(()=>[e.createVNode(e.unref(g.default),{class:e.normalizeClass([r.orientation==="horizontal"?"grid grid-cols-[var(--label-w,8rem)_1fr] items-start space-y-0 gap-2":"space-y-1 gap-1"]),style:e.normalizeStyle(r.orientation==="horizontal"&&r.labelWidth?`--label-w:${r.labelWidth};`:"")},{default:e.withCtx(()=>[e.createVNode(e.unref(x.default),{for:r.id,class:e.normalizeClass(["text-xs",r.orientation==="horizontal"?"flex items-center h-[var(--ctl-h)] w-full":"flex items-center",V.value])},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(r.label)+" ",1),r.required?(e.openBlock(),e.createElementBlock("span",j,"*")):e.createCommentVNode("",!0)]),_:1},8,["for","class"]),e.createVNode(e.unref(k.default),{class:e.normalizeClass([r.orientation==="horizontal"?"min-h-[var(--ctl-h)] flex flex-col justify-start gap-0.5 mt-0":"space-y-2 gap-0"])},{default:e.withCtx(()=>[r.type==="checkbox"||r.type==="switch"?(e.openBlock(),e.createBlock(e.unref(i.default),{key:0,"data-slot":"checkbox-group"},{default:e.withCtx(()=>[e.createVNode(e.unref(g.default),{orientation:"horizontal",class:"flex gap-2 space-y-0 h-[var(--ctl-h)] leading-[var(--ctl-h)] items-center"},{default:e.withCtx(()=>[(e.openBlock(),e.createBlock(e.resolveDynamicComponent(y.value),e.mergeProps(s.value,{"onUpdate:modelValue":p,onChange:f,onFocus:_,onBlur:m}),null,16)),r.inlineLabel?(e.openBlock(),e.createBlock(e.unref(x.default),{key:0,for:r.id,class:"text-xs font-normal m-0 h-[var(--ctl-h)] leading-[var(--ctl-h)]"},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(r.inlineLabel),1)]),_:1},8,["for"])):e.createCommentVNode("",!0)]),_:1})]),_:1})):r.type==="radio"?(e.openBlock(),e.createBlock(e.unref(i.default),{key:1},{default:e.withCtx(()=>[(e.openBlock(),e.createBlock(e.resolveDynamicComponent(y.value),e.mergeProps(s.value,{"onUpdate:modelValue":p,onChange:f,onFocus:_,onBlur:m,class:h.value}),null,16,["class"]))]),_:1})):(e.openBlock(),e.createBlock(e.unref(i.default),{key:2},{default:e.withCtx(()=>[(e.openBlock(),e.createBlock(e.resolveDynamicComponent(y.value),e.mergeProps(s.value,{"onUpdate:modelValue":p,onChange:f,onFocus:_,onBlur:m,class:h.value}),null,16,["class"]))]),_:1})),r.description||c.value?(e.openBlock(),e.createBlock(e.unref(k.default),{key:3},{default:e.withCtx(()=>[r.description?(e.openBlock(),e.createBlock(e.unref(E.default),{key:0},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(r.description),1)]),_:1})):e.createCommentVNode("",!0),c.value?(e.openBlock(),e.createBlock(e.unref(L.default),{key:1},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(c.value),1)]),_:1})):e.createCommentVNode("",!0)]),_:1})):e.createCommentVNode("",!0)]),_:1},8,["class"])]),_:1},8,["class","style"])]),_:1})],2))}});exports.default=A;
2
2
  //# sourceMappingURL=JFormField.vue2.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"JFormField.vue2.cjs","sources":["../../../../src/components/molecules/JFormField.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport { Field, FieldContent, FieldLabel, FieldDescription, FieldError, FieldGroup } from '@/components/shadcn'\nimport { JInput, JTextarea, JCheckbox, JSwitch, JCombo, JRadio, JSearchCombo, JDatepicker } from '@/components/atoms'\nimport { cn } from '@/lib/utils'\n\n// 컴포넌트 타입 정의\ntype ComponentType = 'input' | 'textarea' | 'checkbox' | 'switch' | 'combo' | 'radio' | 'searchCombo' | 'datepicker'\n\n// FormField 자체의 props (레이아웃 관련)\nconst FORM_FIELD_PROPS = [\n 'class',\n 'label',\n 'description',\n 'errorMsg',\n 'type',\n 'inlineLabel',\n 'orientation',\n 'labelAlign',\n 'labelWidth',\n 'radioDirection',\n] as const\n\nconst props = withDefaults(\n defineProps<{\n // ============ FormField 자체 props (레이아웃만) ============\n /** 추가 클래스 (외부 커스터마이징용) */\n class?: string\n /** 필드 레이블 */\n label?: string\n /** 필드 설명 (레이블 아래 표시) */\n description?: string\n /** 에러 메시지 */\n errorMsg?: string\n /** 컴포넌트 타입 (렌더링할 컴포넌트 지정) */\n type?: ComponentType\n /** 체크박스/스위치 타입일 때만 사용하는 옆 라벨 */\n inlineLabel?: string\n /** 레이블과 컨트롤의 배치 방향 */\n orientation?: 'vertical' | 'horizontal' | 'responsive'\n /** 레이블 텍스트 정렬 */\n labelAlign?: 'left' | 'middle' | 'right'\n /** 레이블 영역 너비 (horizontal orientation일 때만 적용) */\n labelWidth?: string\n\n // ============ 내부 컴포넌트로 전달할 공통 props ============\n /** Input 요소의 id (label for와 연결) */\n id?: string\n /** v-model로 양방향 데이터 바인딩 */\n modelValue?: any\n /** 입력 전 표시되는 안내문 (Input/Textarea/Select/Combobox) */\n placeholder?: string\n /** 비활성화 상태 (전체) */\n disabled?: boolean\n /** 읽기 전용 상태 (Input/Textarea) */\n readonly?: boolean\n /** 필수 입력/선택 여부 (전체) */\n required?: boolean\n /** form 데이터 전송 시 키 이름 (전체) */\n name?: string\n /** 스타일 테마 지정 (J-prefixed 컴포넌트의 styleType) */\n styleType?: string\n\n // ============ Input 전용 props ============\n /** Input 타입 (text, email, password 등) */\n inputType?: string\n\n\n // ============ Select/Combobox/Radio 전용 props ============\n /** 선택 가능한 항목 배열 */\n options?: Array<{ label: string; value: string | number; disabled?: boolean }>\n \n // ============ Select/Combobox 전용 props ============\n /** 다중 선택 허용 여부 */\n multiple?: boolean\n\n\n // ============ Radio 전용 props ============\n /** Radio 옵션 나열 방향 */\n radioDirection?: 'horizontal' | 'vertical'\n }>(),\n {\n type: 'input',\n orientation: 'vertical',\n labelAlign: 'left',\n labelWidth: undefined,\n radioDirection: 'horizontal',\n }\n)\n\n// 이벤트 정의\nconst emit = defineEmits<{\n 'update:modelValue': [value: any]\n 'change': [value: any]\n 'focus': [event: FocusEvent]\n 'blur': [event: FocusEvent]\n}>()\n\n// 내부 에러 상태 (props.error가 없을 때만 사용)\nconst internalError = ref<string>('')\n\n// 최종 에러 메시지 (외부 errorMsg가 우선)\nconst finalError = computed(() => props.errorMsg || internalError.value)\n\n// FormField 자체 props와 내부 컴포넌트 props 분리\nconst controlProps = computed(() => {\n const result: Record<string, any> = {}\n const propsObj = props as Record<string, any>\n \n for (const key in propsObj) {\n // FormField 자체 props는 제외\n if (!FORM_FIELD_PROPS.includes(key as any)) {\n result[key] = propsObj[key]\n }\n }\n \n // inputType을 type으로 변환 (JInput 컴포넌트에서 사용)\n if (props.inputType && props.type === 'input') {\n result.type = props.inputType\n delete result.inputType // inputType은 제거\n \n // placeholder가 없으면 inputType에 따라 기본값 설정\n if (!props.placeholder) {\n const defaultPlaceholders: Record<string, string> = {\n 'text': '텍스트를 입력하세요',\n 'email': '이메일을 입력하세요',\n 'password': '비밀번호를 입력하세요',\n 'tel': '전화번호를 입력하세요',\n 'url': 'URL을 입력하세요',\n 'number': '숫자를 입력하세요',\n 'search': '검색어를 입력하세요',\n 'date': '날짜를 선택하세요',\n 'time': '시간을 선택하세요',\n 'datetime-local': '날짜와 시간을 선택하세요',\n 'month': '월을 선택하세요',\n 'week': '주를 선택하세요',\n }\n \n result.placeholder = defaultPlaceholders[props.inputType] || '입력하세요'\n }\n }\n \n // radioDirection을 styletype으로 변환 (JRadio 컴포넌트에서 사용)\n if (props.radioDirection && props.type === 'radio') {\n result.styletype = props.radioDirection\n delete result.radioDirection // radioDirection은 제거\n }\n \n return result\n})\n\n // Built-in validation\n const validateField = (currentValue?: any) => {\n if (!props.required) return\n\n internalError.value = ''\n\n // 현재 값 또는 props.modelValue 사용\n const value = currentValue !== undefined ? currentValue : props.modelValue\n\n // Required 체크\n if (props.type === 'checkbox' || props.type === 'switch') {\n if (value !== 'Y') {\n internalError.value = '필수 항목입니다.'\n }\n } else {\n if (value === null || value === undefined || value === '') {\n internalError.value = '필수 입력 항목입니다.'\n }\n }\n }\n\n // 초기 로드 시 validation 실행 (blur 이벤트에서만)\n const shouldValidateOnMount = computed(() => {\n // Storybook에서 초기값이 있는 경우 validation 스킵\n if (props.modelValue !== null && props.modelValue !== undefined && props.modelValue !== '') {\n return false\n }\n return true\n })\n\n// 이벤트 핸들러\nconst handleUpdateModelValue = (value: any) => {\n emit('update:modelValue', value)\n \n // 값이 변경되면 즉시 validation 실행 (현재 값 전달)\n validateField(value)\n}\n\nconst handleChange = (value: any) => {\n emit('change', value)\n \n // change 이벤트에서도 validation 실행 (현재 값 전달)\n validateField(value)\n}\n\nconst handleFocus = (event: FocusEvent) => {\n emit('focus', event)\n}\n\n const handleBlur = (event: FocusEvent) => {\n // blur 시에만 validation 실행 (초기 로드 시에는 실행하지 않음)\n if (shouldValidateOnMount.value) {\n validateField()\n }\n emit('blur', event)\n }\n\n// 레이블 정렬 클래스 (레이블 영역 내에서 레이블의 위치 제어)\nconst labelAlignClass = computed(() => {\n // horizontal일 때는 레이블 영역 내에서 레이블의 위치를 제어\n const mapHorizontal = { \n left: 'justify-start', // 레이블 영역의 왼쪽에 레이블 위치\n middle: 'justify-center', // 레이블 영역의 가운데에 레이블 위치\n right: 'justify-end' // 레이블 영역의 오른쪽에 레이블 위치 (input과 가까이)\n }\n // vertical일 때는 텍스트 정렬만 제어\n const mapVertical = { left: 'text-left', middle: 'text-center', right: 'text-right' }\n return props.orientation === 'horizontal' ? mapHorizontal[props.labelAlign] : mapVertical[props.labelAlign]\n})\n\n/** 행높이 토큰 클래스 매핑 */\nconst densityClass = computed(() => {\n switch (props.styleType) {\n case 'md': return 'form-density-md'\n case 'lg': return 'form-density-lg'\n default: return 'form-density-sm' // 기본값 변경: md → sm (컴팩트 디자인)\n }\n})\n\n/** 컨트롤 클래스 (datepicker는 최대 너비 제한, radio vertical은 높이 제한 없음) */\nconst controlClass = computed(() => {\n const baseClass = 'h-[var(--ctl-h)] leading-[var(--ctl-h)]'\n \n if (props.type === 'datepicker') {\n return `${baseClass} max-w-xs`\n }\n \n // Radio vertical일 때는 높이 제한 없음\n if (props.type === 'radio' && props.radioDirection === 'vertical') {\n return ''\n }\n \n return baseClass\n})\n\n// 컴포넌트 매핑\nconst componentMap: Record<ComponentType, any> = {\n input: JInput,\n textarea: JTextarea,\n checkbox: JCheckbox,\n switch: JSwitch,\n combo: JCombo,\n radio: JRadio,\n searchCombo: JSearchCombo,\n datepicker: JDatepicker,\n}\n\n// type에 따라 렌더링할 컴포넌트 결정\nconst resolvedComponent = computed(() => {\n return componentMap[props.type!] || JInput\n})\n\n// 외부에서 수동으로 에러 클리어 가능하도록 expose\ndefineExpose({\n clearError: () => { internalError.value = '' }\n})\n</script>\n\n<template>\n <div :class=\"cn('space-y-2 flex-1 min-w-0', densityClass, props.class)\">\n <FieldGroup >\n <Field :class=\"[\n orientation === 'horizontal'\n ? 'grid grid-cols-[var(--label-w,8rem)_1fr] items-start space-y-0 gap-2'\n : 'space-y-1 gap-1'\n ]\"\n :style=\"orientation === 'horizontal' && labelWidth ? `--label-w:${labelWidth};` : ''\"\n >\n <!-- 메인 라벨 (필수표기 포함) -->\n <FieldLabel \n :for=\"id\" \n :class=\"[\n 'text-xs', // 컴팩트 디자인을 위해 폰트 크기 축소\n orientation === 'horizontal'\n ? 'flex items-center h-[var(--ctl-h)] w-full'\n : 'flex items-center',\n labelAlignClass\n ]\"\n >\n {{ label }}\n <span v-if=\"required\" class=\"text-destructive ml-1\">*</span>\n </FieldLabel>\n\n <FieldContent \n :class=\"[\n orientation === 'horizontal'\n ? 'min-h-[var(--ctl-h)] flex flex-col justify-start gap-0.5 mt-0'\n : 'space-y-2 gap-0'\n ]\"\n >\n <!-- 체크박스/스위치: 항상 가로 정렬로 컨트롤 + 인라인 라벨 묶기 -->\n <FieldGroup v-if=\"type === 'checkbox' || type === 'switch'\" data-slot=\"checkbox-group\">\n <!-- 부모 orientation과 무관하게 수평 정렬 고정 -->\n <Field orientation=\"horizontal\" class=\"flex gap-2 space-y-0 h-[var(--ctl-h)] leading-[var(--ctl-h)] items-center\">\n <component\n :is=\"resolvedComponent\"\n v-bind=\"controlProps\"\n @update:modelValue=\"handleUpdateModelValue\"\n @change=\"handleChange\"\n @focus=\"handleFocus\"\n @blur=\"handleBlur\"\n />\n <FieldLabel\n v-if=\"inlineLabel\"\n :for=\"id\"\n class=\"text-xs font-normal m-0 h-[var(--ctl-h)] leading-[var(--ctl-h)]\"\n >\n {{ inlineLabel }}\n </FieldLabel>\n </Field>\n </FieldGroup>\n\n <!-- Radio 버튼: radioDirection에 따라 분기 처리 -->\n <FieldGroup v-else-if=\"type === 'radio'\">\n <component\n :is=\"resolvedComponent\"\n v-bind=\"controlProps\"\n @update:modelValue=\"handleUpdateModelValue\"\n @change=\"handleChange\"\n @focus=\"handleFocus\"\n @blur=\"handleBlur\"\n :class=\"controlClass\"\n />\n </FieldGroup>\n\n <!-- 그 외 컨트롤: orientation 규칙 그대로 따름 -->\n <FieldGroup v-else>\n <component\n :is=\"resolvedComponent\"\n v-bind=\"controlProps\"\n @update:modelValue=\"handleUpdateModelValue\"\n @change=\"handleChange\"\n @focus=\"handleFocus\"\n @blur=\"handleBlur\"\n :class=\"controlClass\"\n />\n </FieldGroup>\n \n <!-- 설명/에러: 항상 컨트롤 '바로 아래'에 표시 -->\n <FieldContent v-if=\"description || finalError\">\n <FieldDescription v-if=\"description\">\n {{ description }}\n </FieldDescription>\n <FieldError v-if=\"finalError\">\n {{ finalError }}\n </FieldError>\n </FieldContent>\n </FieldContent>\n </Field>\n </FieldGroup>\n </div>\n</template>\n\n\n\n<style scoped>\n/* 높이 토큰(밀도) — CSS 변수는 scoped에서도 자식 요소에 상속됨 */\n.form-density-sm { --ctl-h: 2rem; /* 32px */ }\n.form-density-md { --ctl-h: 2.25rem; /* 36px (shadcn 기본에 근접) */ }\n.form-density-lg { --ctl-h: 2.5rem; /* 40px */ }\n</style>"],"names":["FORM_FIELD_PROPS","props","__props","emit","__emit","internalError","ref","finalError","computed","controlProps","result","propsObj","key","defaultPlaceholders","validateField","currentValue","value","shouldValidateOnMount","handleUpdateModelValue","handleChange","handleFocus","event","handleBlur","labelAlignClass","mapHorizontal","mapVertical","densityClass","controlClass","baseClass","componentMap","JInput","JTextarea","JCheckbox","JSwitch","JCombo","JRadio","JSearchCombo","JDatepicker","resolvedComponent","__expose","_createElementBlock","_unref","cn","_createVNode","FieldGroup","Field","_normalizeClass","_normalizeStyle","FieldLabel","_createTextVNode","_toDisplayString","_hoisted_1","FieldContent","_createBlock","_openBlock","_resolveDynamicComponent","_mergeProps","FieldDescription","FieldError"],"mappings":"i/DAUA,MAAMA,EAAmB,CACvB,QACA,QACA,cACA,WACA,OACA,cACA,cACA,aACA,aACA,gBAAA,EAGIC,EAAQC,EAoERC,EAAOC,EAQPC,EAAgBC,EAAAA,IAAY,EAAE,EAG9BC,EAAaC,EAAAA,SAAS,IAAMP,EAAM,UAAYI,EAAc,KAAK,EAGjEI,EAAeD,EAAAA,SAAS,IAAM,CAClC,MAAME,EAA8B,CAAA,EAC9BC,EAAWV,EAEjB,UAAWW,KAAOD,EAEXX,EAAiB,SAASY,CAAU,IACvCF,EAAOE,CAAG,EAAID,EAASC,CAAG,GAK9B,GAAIX,EAAM,WAAaA,EAAM,OAAS,UACpCS,EAAO,KAAOT,EAAM,UACpB,OAAOS,EAAO,UAGV,CAACT,EAAM,aAAa,CACtB,MAAMY,EAA8C,CAClD,KAAQ,aACR,MAAS,aACT,SAAY,cACZ,IAAO,cACP,IAAO,aACP,OAAU,YACV,OAAU,aACV,KAAQ,YACR,KAAQ,YACR,iBAAkB,gBAClB,MAAS,WACT,KAAQ,UAAA,EAGVH,EAAO,YAAcG,EAAoBZ,EAAM,SAAS,GAAK,OAC/D,CAIF,OAAIA,EAAM,gBAAkBA,EAAM,OAAS,UACzCS,EAAO,UAAYT,EAAM,eACzB,OAAOS,EAAO,gBAGTA,CACT,CAAC,EAGOI,EAAiBC,GAAuB,CAC5C,GAAI,CAACd,EAAM,SAAU,OAErBI,EAAc,MAAQ,GAGtB,MAAMW,EAAQD,IAAiB,OAAYA,EAAed,EAAM,WAG5DA,EAAM,OAAS,YAAcA,EAAM,OAAS,SAC1Ce,IAAU,MACZX,EAAc,MAAQ,cAGpBW,GAAU,MAA+BA,IAAU,MACrDX,EAAc,MAAQ,eAG5B,EAGMY,EAAwBT,EAAAA,SAAS,IAEjC,EAAAP,EAAM,aAAe,MAAQA,EAAM,aAAe,QAAaA,EAAM,aAAe,GAIzF,EAGGiB,EAA0BF,GAAe,CAC7Cb,EAAK,oBAAqBa,CAAK,EAG/BF,EAAcE,CAAK,CACrB,EAEMG,EAAgBH,GAAe,CACnCb,EAAK,SAAUa,CAAK,EAGpBF,EAAcE,CAAK,CACrB,EAEMI,EAAeC,GAAsB,CACzClB,EAAK,QAASkB,CAAK,CACrB,EAEQC,EAAcD,GAAsB,CAEpCJ,EAAsB,OACxBH,EAAA,EAEFX,EAAK,OAAQkB,CAAK,CACpB,EAGIE,EAAkBf,EAAAA,SAAS,IAAM,CAErC,MAAMgB,EAAgB,CACpB,KAAM,gBACN,OAAQ,iBACR,MAAO,aAAA,EAGHC,EAAc,CAAE,KAAM,YAAa,OAAQ,cAAe,MAAO,YAAA,EACvE,OAAOxB,EAAM,cAAgB,aAAeuB,EAAcvB,EAAM,UAAU,EAAIwB,EAAYxB,EAAM,UAAU,CAC5G,CAAC,EAGKyB,EAAelB,EAAAA,SAAS,IAAM,CAClC,OAAQP,EAAM,UAAA,CACZ,IAAK,KAAM,MAAO,kBAClB,IAAK,KAAM,MAAO,kBAClB,QAAW,MAAO,iBAAA,CAEtB,CAAC,EAGK0B,EAAenB,EAAAA,SAAS,IAAM,CAClC,MAAMoB,EAAY,0CAElB,OAAI3B,EAAM,OAAS,aACV,GAAG2B,CAAS,YAIjB3B,EAAM,OAAS,SAAWA,EAAM,iBAAmB,WAC9C,GAGF2B,CACT,CAAC,EAGKC,EAA2C,CAC/C,MAAOC,EAAAA,QACP,SAAUC,EAAAA,QACV,SAAUC,EAAAA,QACV,OAAQC,EAAAA,QACR,MAAOC,EAAAA,QACP,MAAOC,EAAAA,QACP,YAAaC,EAAAA,QACb,WAAYC,EAAAA,OAAA,EAIRC,EAAoB9B,EAAAA,SAAS,IAC1BqB,EAAa5B,EAAM,IAAK,GAAK6B,EAAAA,OACrC,EAGD,OAAAS,EAAa,CACX,WAAY,IAAM,CAAElC,EAAc,MAAQ,EAAG,CAAA,CAC9C,wBAICmC,EAAAA,mBA2FM,MAAA,CA3FA,uBAAOC,EAAAA,MAAAC,IAAA,EAAE,2BAA6BhB,QAAczB,EAAM,KAAK,CAAA,CAAA,GACnE0C,EAAAA,YAyFaF,EAAAA,MAAAG,SAAA,EAAA,KAAA,mBAxFX,IAuFQ,CAvFRD,cAuFQF,EAAAA,MAAAI,EAAAA,OAAA,EAAA,CAvFA,MAAKC,EAAAA,eAAA,CAAc5C,EAAA,cAAW,wGAKnC,MAAK6C,EAAAA,eAAE7C,EAAA,cAAW,cAAqBA,EAAA,wBAA0BA,EAAA,UAAU,IAAA,EAAA,CAAA,qBAG5E,IAYa,CAZbyC,cAYaF,EAAAA,MAAAO,EAAAA,OAAA,EAAA,CAXV,IAAK9C,EAAA,GACL,MAAK4C,EAAAA,eAAA,WAAgE5C,EAAA,cAAW,6EAA+HqB,EAAA,KAAA,uBAQhN,IAAW,CAAR0B,EAAAA,gBAAAC,EAAAA,gBAAAhD,EAAA,KAAK,EAAG,IACX,CAAA,EAAYA,EAAA,wBAAZsC,qBAA4D,OAA5DW,EAAoD,GAAC,yDAGvDR,cAgEeF,EAAAA,MAAAW,EAAAA,OAAA,EAAA,CA/DZ,MAAKN,EAAAA,eAAA,CAAgB5C,EAAA,cAAW,qHAOjC,IAmBa,CAnBKA,EAAA,mBAAuBA,EAAA,OAAI,wBAA7CmD,EAAAA,YAmBaZ,EAAAA,MAAAG,EAAAA,OAAA,EAAA,OAnB+C,YAAU,gBAAA,qBAEpE,IAgBQ,CAhBRD,cAgBQF,EAAAA,MAAAI,EAAAA,OAAA,EAAA,CAhBD,YAAY,aAAa,MAAM,2EAAA,qBACpC,IAOE,EAPFS,YAAA,EAAAD,EAAAA,YAOEE,0BANKjB,EAAA,KAAiB,EADxBkB,EAAAA,WAEU/C,EAKR,MALoB,CACnB,sBAAmBS,EACnB,SAAQC,EACR,QAAOC,EACP,OAAME,CAAA,aAGDpB,EAAA,2BADRmD,EAAAA,YAMaZ,EAAAA,MAAAO,EAAAA,OAAA,EAAA,OAJV,IAAK9C,EAAA,GACN,MAAM,iEAAA,qBAEN,IAAiB,qCAAdA,EAAA,WAAW,EAAA,CAAA,CAAA,iEAMGA,EAAA,OAAI,uBAA3BmD,cAUaZ,EAAAA,MAAAG,EAAAA,OAAA,EAAA,CAAA,IAAA,GAAA,mBATX,IAQE,EARFU,YAAA,EAAAD,EAAAA,YAQEE,0BAPKjB,EAAA,KAAiB,EADxBkB,EAAAA,WAEU/C,EAMR,MANoB,CACnB,sBAAmBS,EACnB,SAAQC,EACR,QAAOC,EACP,OAAME,EACN,MAAOK,EAAA,KAAA,+CAKZ0B,cAUaZ,EAAAA,MAAAG,EAAAA,OAAA,EAAA,CAAA,IAAA,GAAA,mBATX,IAQE,EARFU,YAAA,EAAAD,EAAAA,YAQEE,0BAPKjB,EAAA,KAAiB,EADxBkB,EAAAA,WAEU/C,EAMR,MANoB,CACnB,sBAAmBS,EACnB,SAAQC,EACR,QAAOC,EACP,OAAME,EACN,MAAOK,EAAA,KAAA,gCAKQzB,EAAA,aAAeK,EAAA,qBAAnC8C,cAOeZ,EAAAA,MAAAW,EAAAA,OAAA,EAAA,CAAA,IAAA,GAAA,mBANb,IAEmB,CAFKlD,EAAA,2BAAxBmD,EAAAA,YAEmBZ,QAAAgB,EAAAA,OAAA,EAAA,CAAA,IAAA,GAAA,mBADjB,IAAiB,qCAAdvD,EAAA,WAAW,EAAA,CAAA,CAAA,sCAEEK,EAAA,qBAAlB8C,EAAAA,YAEaZ,QAAAiB,EAAAA,OAAA,EAAA,CAAA,IAAA,GAAA,mBADX,IAAgB,qCAAbnD,EAAA,KAAU,EAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"JFormField.vue2.cjs","sources":["../../../../src/components/molecules/JFormField.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport { Field, FieldContent, FieldLabel, FieldDescription, FieldError, FieldGroup } from '@/components/shadcn'\nimport { JInput, JTextarea, JCheckbox, JSwitch, JCombo, JRadio, JSearchCombo, JDatepicker } from '@/components/atoms'\nimport { cn } from '@/lib/utils'\n\n// 컴포넌트 타입 정의\ntype ComponentType = 'input' | 'textarea' | 'checkbox' | 'switch' | 'combo' | 'radio' | 'searchCombo' | 'datepicker'\n\n// FormField 자체의 props (레이아웃 관련)\nconst FORM_FIELD_PROPS = [\n 'class',\n 'label',\n 'description',\n 'errorMsg',\n 'type',\n 'inlineLabel',\n 'orientation',\n 'labelAlign',\n 'labelWidth',\n 'radioDirection',\n] as const\n\nconst props = withDefaults(\n defineProps<{\n // ============ FormField 자체 props (레이아웃만) ============\n /** 추가 클래스 (외부 커스터마이징용) */\n class?: string\n /** 필드 레이블 */\n label?: string\n /** 필드 설명 (레이블 아래 표시) */\n description?: string\n /** 에러 메시지 */\n errorMsg?: string\n /** 컴포넌트 타입 (렌더링할 컴포넌트 지정) */\n type?: ComponentType\n /** 체크박스/스위치 타입일 때만 사용하는 옆 라벨 */\n inlineLabel?: string\n /** 레이블과 컨트롤의 배치 방향 */\n orientation?: 'vertical' | 'horizontal' | 'responsive'\n /** 레이블 텍스트 정렬 */\n labelAlign?: 'left' | 'middle' | 'right'\n /** 레이블 영역 너비 (horizontal orientation일 때만 적용) */\n labelWidth?: string\n\n // ============ 내부 컴포넌트로 전달할 공통 props ============\n /** Input 요소의 id (label for와 연결) */\n id?: string\n /** v-model로 양방향 데이터 바인딩 */\n modelValue?: any\n /** 입력 전 표시되는 안내문 (Input/Textarea/Select/Combobox) */\n placeholder?: string\n /** 비활성화 상태 (전체) */\n disabled?: boolean\n /** 읽기 전용 상태 (Input/Textarea) */\n readonly?: boolean\n /** 필수 입력/선택 여부 (전체) */\n required?: boolean\n /** form 데이터 전송 시 키 이름 (전체) */\n name?: string\n /** 스타일 테마 지정 (J-prefixed 컴포넌트의 styleType) */\n styleType?: string\n\n // ============ Input 전용 props ============\n /** Input 타입 (text, email, password 등) */\n inputType?: string\n\n\n // ============ Select/Combobox/Radio 전용 props ============\n /** 선택 가능한 항목 배열 */\n options?: Array<{ label: string; value: string | number; disabled?: boolean }>\n \n // ============ Select/Combobox 전용 props ============\n /** 다중 선택 허용 여부 */\n multiple?: boolean\n\n\n // ============ Radio 전용 props ============\n /** Radio 옵션 나열 방향 */\n radioDirection?: 'horizontal' | 'vertical'\n }>(),\n {\n type: 'input',\n orientation: 'horizontal',\n labelAlign: 'left',\n labelWidth: '80px',\n radioDirection: 'horizontal',\n }\n)\n\n// 이벤트 정의\nconst emit = defineEmits<{\n 'update:modelValue': [value: any]\n 'change': [value: any]\n 'focus': [event: FocusEvent]\n 'blur': [event: FocusEvent]\n}>()\n\n// 내부 에러 상태 (props.error가 없을 때만 사용)\nconst internalError = ref<string>('')\n\n// 최종 에러 메시지 (외부 errorMsg가 우선)\nconst finalError = computed(() => props.errorMsg || internalError.value)\n\n// FormField 자체 props와 내부 컴포넌트 props 분리\nconst controlProps = computed(() => {\n const result: Record<string, any> = {}\n const propsObj = props as Record<string, any>\n \n for (const key in propsObj) {\n // FormField 자체 props는 제외\n if (!FORM_FIELD_PROPS.includes(key as any)) {\n result[key] = propsObj[key]\n }\n }\n \n // inputType을 type으로 변환 (JInput 컴포넌트에서 사용)\n if (props.inputType && props.type === 'input') {\n result.type = props.inputType\n delete result.inputType // inputType은 제거\n \n // placeholder가 없으면 inputType에 따라 기본값 설정\n if (!props.placeholder) {\n const defaultPlaceholders: Record<string, string> = {\n 'text': '텍스트를 입력하세요',\n 'email': '이메일을 입력하세요',\n 'password': '비밀번호를 입력하세요',\n 'tel': '전화번호를 입력하세요',\n 'url': 'URL을 입력하세요',\n 'number': '숫자를 입력하세요',\n 'search': '검색어를 입력하세요',\n 'date': '날짜를 선택하세요',\n 'time': '시간을 선택하세요',\n 'datetime-local': '날짜와 시간을 선택하세요',\n 'month': '월을 선택하세요',\n 'week': '주를 선택하세요',\n }\n \n result.placeholder = defaultPlaceholders[props.inputType] || '입력하세요'\n }\n }\n \n // radioDirection을 styletype으로 변환 (JRadio 컴포넌트에서 사용)\n if (props.radioDirection && props.type === 'radio') {\n result.styletype = props.radioDirection\n delete result.radioDirection // radioDirection은 제거\n }\n \n return result\n})\n\n // Built-in validation\n const validateField = (currentValue?: any) => {\n if (!props.required) return\n\n internalError.value = ''\n\n // 현재 값 또는 props.modelValue 사용\n const value = currentValue !== undefined ? currentValue : props.modelValue\n\n // Required 체크\n if (props.type === 'checkbox' || props.type === 'switch') {\n if (value !== 'Y') {\n internalError.value = '필수 항목입니다.'\n }\n } else {\n if (value === null || value === undefined || value === '') {\n internalError.value = '필수 입력 항목입니다.'\n }\n }\n }\n\n // 초기 로드 시 validation 실행 (blur 이벤트에서만)\n const shouldValidateOnMount = computed(() => {\n // Storybook에서 초기값이 있는 경우 validation 스킵\n if (props.modelValue !== null && props.modelValue !== undefined && props.modelValue !== '') {\n return false\n }\n return true\n })\n\n// 이벤트 핸들러\nconst handleUpdateModelValue = (value: any) => {\n emit('update:modelValue', value)\n \n // 값이 변경되면 즉시 validation 실행 (현재 값 전달)\n validateField(value)\n}\n\nconst handleChange = (value: any) => {\n emit('change', value)\n \n // change 이벤트에서도 validation 실행 (현재 값 전달)\n validateField(value)\n}\n\nconst handleFocus = (event: FocusEvent) => {\n emit('focus', event)\n}\n\n const handleBlur = (event: FocusEvent) => {\n // blur 시에만 validation 실행 (초기 로드 시에는 실행하지 않음)\n if (shouldValidateOnMount.value) {\n validateField()\n }\n emit('blur', event)\n }\n\n// 레이블 정렬 클래스 (레이블 영역 내에서 레이블의 위치 제어)\nconst labelAlignClass = computed(() => {\n // horizontal일 때는 레이블 영역 내에서 레이블의 위치를 제어\n const mapHorizontal = { \n left: 'justify-start', // 레이블 영역의 왼쪽에 레이블 위치\n middle: 'justify-center', // 레이블 영역의 가운데에 레이블 위치\n right: 'justify-end' // 레이블 영역의 오른쪽에 레이블 위치 (input과 가까이)\n }\n // vertical일 때는 텍스트 정렬만 제어\n const mapVertical = { left: 'text-left', middle: 'text-center', right: 'text-right' }\n return props.orientation === 'horizontal' ? mapHorizontal[props.labelAlign] : mapVertical[props.labelAlign]\n})\n\n/** 행높이 토큰 클래스 매핑 */\nconst densityClass = computed(() => {\n switch (props.styleType) {\n case 'md': return 'form-density-md'\n case 'lg': return 'form-density-lg'\n default: return 'form-density-sm' // 기본값 변경: md → sm (컴팩트 디자인)\n }\n})\n\n/** 컨트롤 클래스 (datepicker는 최대 너비 제한, radio vertical은 높이 제한 없음) */\nconst controlClass = computed(() => {\n const baseClass = 'h-[var(--ctl-h)] leading-[var(--ctl-h)]'\n \n if (props.type === 'datepicker') {\n return `${baseClass} max-w-xs`\n }\n \n // Radio vertical일 때는 높이 제한 없음\n if (props.type === 'radio' && props.radioDirection === 'vertical') {\n return ''\n }\n \n return baseClass\n})\n\n// 컴포넌트 매핑\nconst componentMap: Record<ComponentType, any> = {\n input: JInput,\n textarea: JTextarea,\n checkbox: JCheckbox,\n switch: JSwitch,\n combo: JCombo,\n radio: JRadio,\n searchCombo: JSearchCombo,\n datepicker: JDatepicker,\n}\n\n// type에 따라 렌더링할 컴포넌트 결정\nconst resolvedComponent = computed(() => {\n return componentMap[props.type!] || JInput\n})\n\n// 외부에서 수동으로 에러 클리어 가능하도록 expose\ndefineExpose({\n clearError: () => { internalError.value = '' }\n})\n</script>\n\n<template>\n <div :class=\"cn('space-y-2 flex-1 min-w-0', densityClass, props.class)\">\n <FieldGroup >\n <Field :class=\"[\n orientation === 'horizontal'\n ? 'grid grid-cols-[var(--label-w,8rem)_1fr] items-start space-y-0 gap-2'\n : 'space-y-1 gap-1'\n ]\"\n :style=\"orientation === 'horizontal' && labelWidth ? `--label-w:${labelWidth};` : ''\"\n >\n <!-- 메인 라벨 (필수표기 포함) -->\n <FieldLabel \n :for=\"id\" \n :class=\"[\n 'text-xs', // 컴팩트 디자인을 위해 폰트 크기 축소\n orientation === 'horizontal'\n ? 'flex items-center h-[var(--ctl-h)] w-full'\n : 'flex items-center',\n labelAlignClass\n ]\"\n >\n {{ label }}\n <span v-if=\"required\" class=\"text-destructive ml-1\">*</span>\n </FieldLabel>\n\n <FieldContent \n :class=\"[\n orientation === 'horizontal'\n ? 'min-h-[var(--ctl-h)] flex flex-col justify-start gap-0.5 mt-0'\n : 'space-y-2 gap-0'\n ]\"\n >\n <!-- 체크박스/스위치: 항상 가로 정렬로 컨트롤 + 인라인 라벨 묶기 -->\n <FieldGroup v-if=\"type === 'checkbox' || type === 'switch'\" data-slot=\"checkbox-group\">\n <!-- 부모 orientation과 무관하게 수평 정렬 고정 -->\n <Field orientation=\"horizontal\" class=\"flex gap-2 space-y-0 h-[var(--ctl-h)] leading-[var(--ctl-h)] items-center\">\n <component\n :is=\"resolvedComponent\"\n v-bind=\"controlProps\"\n @update:modelValue=\"handleUpdateModelValue\"\n @change=\"handleChange\"\n @focus=\"handleFocus\"\n @blur=\"handleBlur\"\n />\n <FieldLabel\n v-if=\"inlineLabel\"\n :for=\"id\"\n class=\"text-xs font-normal m-0 h-[var(--ctl-h)] leading-[var(--ctl-h)]\"\n >\n {{ inlineLabel }}\n </FieldLabel>\n </Field>\n </FieldGroup>\n\n <!-- Radio 버튼: radioDirection에 따라 분기 처리 -->\n <FieldGroup v-else-if=\"type === 'radio'\">\n <component\n :is=\"resolvedComponent\"\n v-bind=\"controlProps\"\n @update:modelValue=\"handleUpdateModelValue\"\n @change=\"handleChange\"\n @focus=\"handleFocus\"\n @blur=\"handleBlur\"\n :class=\"controlClass\"\n />\n </FieldGroup>\n\n <!-- 그 외 컨트롤: orientation 규칙 그대로 따름 -->\n <FieldGroup v-else>\n <component\n :is=\"resolvedComponent\"\n v-bind=\"controlProps\"\n @update:modelValue=\"handleUpdateModelValue\"\n @change=\"handleChange\"\n @focus=\"handleFocus\"\n @blur=\"handleBlur\"\n :class=\"controlClass\"\n />\n </FieldGroup>\n \n <!-- 설명/에러: 항상 컨트롤 '바로 아래'에 표시 -->\n <FieldContent v-if=\"description || finalError\">\n <FieldDescription v-if=\"description\">\n {{ description }}\n </FieldDescription>\n <FieldError v-if=\"finalError\">\n {{ finalError }}\n </FieldError>\n </FieldContent>\n </FieldContent>\n </Field>\n </FieldGroup>\n </div>\n</template>\n\n\n\n<style scoped>\n/* 높이 토큰(밀도) — :root의 --j-ctl-h-* 를 참조, 앱에서 override 가능 */\n.form-density-sm { --ctl-h: var(--j-ctl-h-sm, 1.75rem); }\n.form-density-md { --ctl-h: var(--j-ctl-h-md, 2.25rem); }\n.form-density-lg { --ctl-h: var(--j-ctl-h-lg, 2.5rem); }\n</style>"],"names":["FORM_FIELD_PROPS","props","__props","emit","__emit","internalError","ref","finalError","computed","controlProps","result","propsObj","key","defaultPlaceholders","validateField","currentValue","value","shouldValidateOnMount","handleUpdateModelValue","handleChange","handleFocus","event","handleBlur","labelAlignClass","mapHorizontal","mapVertical","densityClass","controlClass","baseClass","componentMap","JInput","JTextarea","JCheckbox","JSwitch","JCombo","JRadio","JSearchCombo","JDatepicker","resolvedComponent","__expose","_createElementBlock","_unref","cn","_createVNode","FieldGroup","Field","_normalizeClass","_normalizeStyle","FieldLabel","_createTextVNode","_toDisplayString","_hoisted_1","FieldContent","_createBlock","_openBlock","_resolveDynamicComponent","_mergeProps","FieldDescription","FieldError"],"mappings":"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"}