{"version":3,"file":"JTree.vue.cjs","sources":["../../../../src/components/organisms/JTree.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, watch } from 'vue'\nimport type { SidebarMenuItem, MenuPermission, MenuClickEvent } from '@/types/sidebar-menu.types'\nimport JDynamicMenuItem from './JSidebarSimple/JDynamicMenuItem.vue'\nimport { cn } from '@/lib/utils'\nimport { filterMenuItems, getExpandedKeysForSearch } from '@/lib/menu-utils'\n\n/**\n * JTree - 트리 뷰 컴포넌트\n * Tree View Component\n *\n * @description\n * 계층 데이터를 조회/탐색하는 읽기 전용 트리 컴포넌트입니다.\n * JDynamicMenuItem을 재귀적으로 렌더링하며, 네비게이션 기능을 비활성화하고\n * nodeClick 이벤트로 선택을 처리합니다.\n *\n * @example\n * ```vue\n * <JTree\n *   :items=\"treeData\"\n *   v-model:expanded-keys=\"expandedKeys\"\n *   v-model:active-key=\"activeKey\"\n *   @node-click=\"handleNodeClick\"\n * />\n * ```\n *\n * @example JSON 트리 데이터 예시\n * ```json\n * [\n *   {\n *     \"label\": \"프로그램 관리\",\n *     \"icon\": \"folder\",\n *     \"menuType\": \"F\",\n *     \"menuKey\": 1,\n *     \"children\": [\n *       {\n *         \"label\": \"시스템 관리\",\n *         \"menuType\": \"L\",\n *         \"menuKey\": 11\n *       },\n *       {\n *         \"label\": \"사용자 관리\",\n *         \"menuType\": \"L\",\n *         \"menuKey\": 12\n *       }\n *     ]\n *   }\n * ]\n * ```\n */\n\ntype StyleType = 'default' | 'minimal'\n\nconst props = withDefaults(\n  defineProps<{\n    /** 트리 노드 데이터 */\n    items: SidebarMenuItem[]\n    /** 펼쳐진 노드 키 목록 (v-model 지원, 배열) */\n    expandedKeys?: (number | string)[]\n    /** 현재 선택(하이라이트)된 노드의 menuKey (v-model 지원) */\n    activeKey?: number | string | null\n    /** 권한 목록 */\n    permissions?: MenuPermission[]\n    /** 최대 깊이 제한 (무한 루프 방지) */\n    maxDepth?: number\n    /** 스타일 타입 */\n    styletype?: StyleType\n    /** 검색어 (노드 필터링) */\n    searchQuery?: string\n    /** 추가 CSS 클래스 */\n    class?: string\n  }>(),\n  {\n    expandedKeys: () => [],\n    activeKey: null,\n    permissions: () => [],\n    maxDepth: 10,\n    styletype: 'minimal',\n    searchQuery: '',\n  },\n)\n\nconst emit = defineEmits<{\n  /** 노드 클릭 이벤트 */\n  nodeClick: [event: MenuClickEvent]\n  /** 확장 상태 변경 이벤트 */\n  expandChange: [menuKey: number | string | undefined, expanded: boolean]\n  /** expandedKeys v-model 업데이트 */\n  'update:expandedKeys': [keys: (number | string)[]]\n  /** activeKey v-model 업데이트 */\n  'update:activeKey': [key: number | string | null]\n}>()\n\n/**\n * 내부 확장 키 관리 (Set으로 변환하여 사용)\n */\nconst internalExpandedKeys = ref<Set<number | string>>(new Set(props.expandedKeys))\n\n/**\n * 검색어로 필터링된 트리 아이템\n */\nconst filteredTreeItems = computed(() => {\n  return filterMenuItems(props.items, props.searchQuery || '')\n})\n\n/**\n * 검색 결과에 따라 부모 노드 자동 확장\n */\nwatch(\n  () => filteredTreeItems.value,\n  (filtered) => {\n    if (!props.searchQuery || props.searchQuery.trim() === '') {\n      return\n    }\n\n    // 검색 결과에서 매칭된 하위 노드가 있는 부모를 찾아 확장\n    const keysToExpand = getExpandedKeysForSearch(filtered)\n    keysToExpand.forEach(key => {\n      internalExpandedKeys.value.add(key)\n    })\n\n    // v-model 업데이트\n    emit('update:expandedKeys', [...internalExpandedKeys.value])\n  },\n  { immediate: false }\n)\n\n/**\n * expandedKeys prop 변경 감지\n */\nwatch(\n  () => props.expandedKeys,\n  (newKeys) => {\n    internalExpandedKeys.value = new Set(newKeys)\n  },\n  { deep: true }\n)\n\n/**\n * 확장 상태 변경 핸들러\n */\nconst handleExpandChange = (menuKey: number | string | undefined, expanded: boolean) => {\n  if (!menuKey) return\n\n  // 새로운 Set 생성 (reactivity 보장)\n  const newKeys = new Set(internalExpandedKeys.value)\n\n  if (expanded) {\n    newKeys.add(menuKey)\n  } else {\n    newKeys.delete(menuKey)\n  }\n\n  internalExpandedKeys.value = newKeys\n\n  // v-model 업데이트 및 이벤트 발생\n  emit('update:expandedKeys', [...newKeys])\n  emit('expandChange', menuKey, expanded)\n}\n\n/**\n * 노드 클릭 핸들러\n */\nconst handleNodeClick = (event: MenuClickEvent) => {\n  // activeKey 업데이트\n  const menuKey = event.menuItem.menuKey\n  if (menuKey !== undefined) {\n    emit('update:activeKey', menuKey)\n  }\n\n  // nodeClick 이벤트 발생\n  emit('nodeClick', event)\n}\n\n/**\n * 스타일 프리셋\n */\nconst STYLE_PRESETS: Record<StyleType, {\n  containerClass: string\n  menuPaddingClass: string\n}> = {\n  default: {\n    containerClass: 'w-full',\n    menuPaddingClass: 'space-y-1',\n  },\n  minimal: {\n    containerClass: 'w-full',\n    menuPaddingClass: 'space-y-0.5',\n  },\n}\n\nconst preset = computed(() => {\n  return STYLE_PRESETS[props.styletype] ?? STYLE_PRESETS.default\n})\n\n/**\n * 루트 클래스\n */\nconst rootClasses = computed(() => {\n  return cn(\n    preset.value.containerClass,\n    props.class\n  )\n})\n</script>\n\n<template>\n  <div :class=\"rootClasses\">\n    <div :class=\"preset.menuPaddingClass\">\n      <JDynamicMenuItem\n        v-for=\"(item, index) in filteredTreeItems\"\n        :key=\"item.menuKey || item.label || index\"\n        :item=\"item\"\n        :level=\"0\"\n        :max-depth=\"maxDepth\"\n        :permissions=\"permissions\"\n        :expanded-keys=\"internalExpandedKeys\"\n        :styletype=\"styletype\"\n        :disable-navigation=\"true\"\n        :active-key=\"activeKey\"\n        @menu-click=\"handleNodeClick\"\n        @expand-change=\"handleExpandChange\"\n      />\n    </div>\n  </div>\n</template>\n"],"names":["props","__props","emit","__emit","internalExpandedKeys","ref","filteredTreeItems","computed","filterMenuItems","watch","filtered","getExpandedKeysForSearch","key","newKeys","handleExpandChange","menuKey","expanded","handleNodeClick","event","STYLE_PRESETS","preset","rootClasses","cn","_createElementBlock","_createElementVNode","_normalizeClass","_openBlock","_Fragment","_renderList","item","index","_createBlock","JDynamicMenuItem"],"mappings":"8jBAqDA,MAAMA,EAAQC,EA6BRC,EAAOC,EAcPC,EAAuBC,EAAAA,IAA0B,IAAI,IAAIL,EAAM,YAAY,CAAC,EAK5EM,EAAoBC,EAAAA,SAAS,IAC1BC,EAAAA,gBAAgBR,EAAM,MAAOA,EAAM,aAAe,EAAE,CAC5D,EAKDS,EAAAA,MACE,IAAMH,EAAkB,MACvBI,GAAa,CACZ,GAAI,CAACV,EAAM,aAAeA,EAAM,YAAY,KAAA,IAAW,GACrD,OAImBW,EAAAA,yBAAyBD,CAAQ,EACzC,QAAQE,GAAO,CAC1BR,EAAqB,MAAM,IAAIQ,CAAG,CACpC,CAAC,EAGDV,EAAK,sBAAuB,CAAC,GAAGE,EAAqB,KAAK,CAAC,CAC7D,EACA,CAAE,UAAW,EAAA,CAAM,EAMrBK,EAAAA,MACE,IAAMT,EAAM,aACXa,GAAY,CACXT,EAAqB,MAAQ,IAAI,IAAIS,CAAO,CAC9C,EACA,CAAE,KAAM,EAAA,CAAK,EAMf,MAAMC,EAAqB,CAACC,EAAsCC,IAAsB,CACtF,GAAI,CAACD,EAAS,OAGd,MAAMF,EAAU,IAAI,IAAIT,EAAqB,KAAK,EAE9CY,EACFH,EAAQ,IAAIE,CAAO,EAEnBF,EAAQ,OAAOE,CAAO,EAGxBX,EAAqB,MAAQS,EAG7BX,EAAK,sBAAuB,CAAC,GAAGW,CAAO,CAAC,EACxCX,EAAK,eAAgBa,EAASC,CAAQ,CACxC,EAKMC,EAAmBC,GAA0B,CAEjD,MAAMH,EAAUG,EAAM,SAAS,QAC3BH,IAAY,QACdb,EAAK,mBAAoBa,CAAO,EAIlCb,EAAK,YAAagB,CAAK,CACzB,EAKMC,EAGD,CACH,QAAS,CACP,eAAgB,SAChB,iBAAkB,WAAA,EAEpB,QAAS,CACP,eAAgB,SAChB,iBAAkB,aAAA,CACpB,EAGIC,EAASb,EAAAA,SAAS,IACfY,EAAcnB,EAAM,SAAS,GAAKmB,EAAc,OACxD,EAKKE,EAAcd,EAAAA,SAAS,IACpBe,EAAAA,GACLF,EAAO,MAAM,eACbpB,EAAM,KAAA,CAET,8BAICuB,EAAAA,mBAiBM,MAAA,CAjBA,uBAAOF,EAAA,KAAW,CAAA,GACtBG,EAAAA,mBAeM,MAAA,CAfA,MAAKC,EAAAA,eAAEL,EAAA,MAAO,gBAAgB,CAAA,IAClCM,EAAAA,UAAA,EAAA,EAAAH,EAAAA,mBAaEI,WAAA,KAAAC,EAAAA,WAZwBtB,EAAA,MAAiB,CAAjCuB,EAAMC,mBADhBC,EAAAA,YAaEC,UAAA,CAXC,IAAKH,EAAK,SAAWA,EAAK,OAASC,EACnC,KAAAD,EACA,MAAO,EACP,YAAW5B,EAAA,SACX,YAAaA,EAAA,YACb,gBAAeG,EAAA,MACf,UAAWH,EAAA,UACX,qBAAoB,GACpB,aAAYA,EAAA,UACZ,YAAYgB,EACZ,eAAeH,CAAA"}