{"version":3,"file":"JLayoutAdvanced.vue.cjs","sources":["../../../../src/components/templates/JLayoutAdvanced.vue"],"sourcesContent":["<script setup lang=\"ts\">\r\nimport { ref, computed, nextTick, type Component } from 'vue'\r\nimport JLayout from './JLayout.vue'\r\nimport JHeader from '@/components/organisms/JHeader.vue'\r\nimport JSidebarAdvanced from '@/components/organisms/JSidebarAdvanced.vue'\r\nimport JDynamicTabs from '@/components/organisms/JDynamicTabs.vue'\r\nimport type { SidebarMenuItem, MenuClickEvent, MenuPermission } from '@/types/sidebar-menu.types'\r\nimport type { DynamicTab } from '@/types/dynamic-tabs.types'\r\n\r\n// Router는 optional (Storybook 환경에서는 없을 수 있음)\r\n// 라이브러리 컴포넌트이므로 Router 의존성을 피하기 위해 optional import\r\n// vue-router는 이미 설치되어 있으므로 일반 import 사용\r\n// 런타임에 Router 인스턴스가 있는지만 체크\r\nimport { RouterView, useRouter } from 'vue-router'\r\n\r\n/**\r\n * JLayoutAdvanced - Advanced Sidebar + JDynamicTabs 조합 템플릿 (templates)\r\n * Advanced Layout Template\r\n * \r\n * @description\r\n * JLayout을 사용하는 Preset 템플릿입니다.\r\n * JHeader, JSidebarAdvanced, JDynamicTabs, JPageContainer를 조합한 기본 레이아웃 예시를 제공합니다.\r\n * \r\n * 레이아웃 구조:\r\n * - header: 상단 헤더 영역 (슬롯)\r\n * - sidebar: 사이드바 영역 (슬롯)\r\n * - content: 메인 콘텐츠 영역 (슬롯)\r\n * \r\n * ⚠️ 중요: menuItems의 path를 활용하여 RouterView로 자동 탭 생성 및 경로 이동이 가능합니다.\r\n * \r\n * Router가 있는 경우: menuItems의 path를 기반으로 자동 탭 생성되며, \r\n * content-tab-{id} 슬롯으로 RouterView를 제공하면 됩니다.\r\n * \r\n * Router가 없는 경우: content-tab-{id} 슬롯으로 직접 컴포넌트를 제공해야 합니다.\r\n */\r\n\r\nconst props = withDefaults(defineProps<{\r\n  /** 레이아웃 스타일 타입 */\r\n  styletype?: 'default' | 'minimal'\r\n  /** 콘텐츠 영역 스크롤 가능 여부 */\r\n  contentScroll?: boolean\r\n  /** 추가 CSS 클래스 */\r\n  class?: string\r\n  /** 메뉴 아이템 목록 (자동 탭 생성을 위해 필요) */\r\n  menuItems?: SidebarMenuItem[]\r\n  /** 즐겨찾기 메뉴 키 목록 */\r\n  favorites?: (number | string)[]\r\n  /** 권한 목록 */\r\n  permissions?: MenuPermission[]\r\n  /** 탭 콘텐츠 렌더 방식 ('router-view': 기존 RouterView fallback, 'slot': 외부 slot + resolveComponent 전용) */\r\n  contentMode?: 'router-view' | 'slot'\r\n  /** 메뉴 클릭 시 router.push로 URL 동기화 여부 */\r\n  syncUrl?: boolean\r\n  /** 경로 → 컴포넌트 resolver. Promise 허용. undefined 반환 시 JTabs fallback으로 이어짐 */\r\n  resolveComponent?: (path: string) => Component | Promise<Component> | undefined\r\n}>(), {\r\n  styletype: 'minimal',\r\n  contentScroll: true,\r\n  menuItems: () => [],\r\n  favorites: () => [],\r\n  permissions: () => [],\r\n  contentMode: 'router-view',\r\n  syncUrl: true,\r\n})\r\n\r\nconst emit = defineEmits<{\r\n  /** 메뉴 클릭 이벤트 (자동 처리되지 않는 경우) */\r\n  menuClick: [event: MenuClickEvent]\r\n  /** 탭 추가 이벤트 */\r\n  tabAdd: [tab: DynamicTab]\r\n  /** 탭 변경 이벤트 */\r\n  tabChange: [tabId: string]\r\n  /** 탭 닫기 이벤트 */\r\n  tabClose: [tabId: string]\r\n  /** 즐겨찾기 변경 이벤트 */\r\n  favoriteChange: [menuKey: number | string | undefined, isFavorite: boolean]\r\n}>()\r\n\r\n/**\r\n * 사이드바 열림 상태\r\n */\r\nconst isSidebarOpen = ref(true)\r\n\r\n/**\r\n * 사이드바 토글 핸들러\r\n */\r\nconst handleSidebarToggle = () => {\r\n  isSidebarOpen.value = !isSidebarOpen.value\r\n}\r\n\r\n/**\r\n * JDynamicTabs 컴포넌트 참조\r\n */\r\nconst tabsRef = ref<InstanceType<typeof JDynamicTabs> | null>(null)\r\n\r\n/**\r\n * 탭 목록 (내부 상태)\r\n */\r\nconst tabs = ref<DynamicTab[]>([])\r\n\r\n/**\r\n * 현재 활성 탭 ID\r\n */\r\nconst activeTabId = ref<string | undefined>(undefined)\r\n\r\n/**\r\n * path를 기반으로 탭 ID 생성\r\n * 예: /product/info -> tab-product-info\r\n */\r\nconst generateTabIdFromPath = (path: string): string => {\r\n  if (!path) return ''\r\n  // 앞뒤 슬래시 제거 및 특수문자 변환\r\n  const cleanPath = path.replace(/^\\/+|\\/+$/g, '').replace(/\\//g, '-')\r\n  return `tab-${cleanPath}`\r\n}\r\n\r\n/**\r\n * Router 인스턴스 (RouterView 사용을 위해, 선택적)\r\n * Router가 없을 수 있으므로 optional하게 처리\r\n */\r\nlet router: ReturnType<typeof useRouter> | null = null\r\ntry {\r\n  router = useRouter()\r\n} catch {\r\n  // Router가 설정되지 않은 경우 (스토리북 환경 등)\r\n  router = null\r\n}\r\n\r\n/**\r\n * 메뉴 클릭 핸들러 - path 기반 탭 자동 생성\r\n */\r\nconst handleMenuClick = async (event: MenuClickEvent) => {\r\n  const menuItem = event.menuItem\r\n\r\n  // Link 타입 메뉴만 탭으로 생성\r\n  if (!menuItem || menuItem.menuType !== 'L') {\r\n    // 자동 처리하지 않는 경우 이벤트 emit\r\n    emit('menuClick', event)\r\n    return\r\n  }\r\n\r\n  // path가 없으면 자동 처리 불가\r\n  if (!menuItem.path) {\r\n    emit('menuClick', event)\r\n    return\r\n  }\r\n\r\n  // 탭 ID 생성 (path 기반)\r\n  const tabId = generateTabIdFromPath(menuItem.path)\r\n\r\n  // 컴포넌트가 마운트될 때까지 대기\r\n  await nextTick()\r\n  await nextTick()\r\n\r\n  // 이미 열린 탭이면 활성화하고 종료 (resolveComponent 호출 불필요)\r\n  if (tabsRef.value?.findTab(tabId)) {\r\n    tabsRef.value.activateTab(tabId)\r\n    return\r\n  }\r\n\r\n  // resolveComponent가 제공된 경우 탭 컴포넌트 resolve\r\n  let resolvedComponent: Component | undefined\r\n  if (props.resolveComponent && menuItem.path) {\r\n    resolvedComponent = await props.resolveComponent(menuItem.path)\r\n  }\r\n\r\n  // JDynamicTabs의 addTab 메서드를 직접 호출하여 탭 추가\r\n  if (tabsRef.value && typeof tabsRef.value.addTab === 'function') {\r\n    try {\r\n      tabsRef.value.addTab({\r\n        id: tabId,\r\n        label: menuItem.label || '제목 없음',\r\n        icon: menuItem.icon,\r\n        closable: true,\r\n        component: resolvedComponent,\r\n        meta: {\r\n          path: menuItem.path,\r\n          menuKey: menuItem.menuKey,\r\n        },\r\n      })\r\n      emit('tabAdd', { id: tabId, label: menuItem.label || '제목 없음', icon: menuItem.icon, closable: true })\r\n\r\n      // Router가 있으면 경로로 이동 (Router에 이미 정의된 경로 사용)\r\n      if (router && props.syncUrl) {\r\n        router.push({ path: menuItem.path })\r\n      }\r\n      return\r\n    } catch (error) {\r\n      console.error('탭 추가 실패:', error)\r\n    }\r\n  }\r\n\r\n  // tabsRef가 없거나 addTab 실패 시 tabs.value에 직접 추가\r\n  const newTab: DynamicTab = {\r\n    id: tabId,\r\n    label: menuItem.label || '제목 없음',\r\n    icon: menuItem.icon,\r\n    closable: true,\r\n    ...(resolvedComponent ? { component: resolvedComponent } : {}),\r\n    meta: {\r\n      path: menuItem.path,\r\n      menuKey: menuItem.menuKey,\r\n    },\r\n  }\r\n\r\n  if (!Array.isArray(tabs.value)) {\r\n    tabs.value = []\r\n  }\r\n\r\n  // 이미 있는 탭인지 확인\r\n  const existingTab = tabs.value.find(t => t.id === tabId)\r\n  if (!existingTab) {\r\n    tabs.value = [...tabs.value, newTab]\r\n    activeTabId.value = tabId\r\n    emit('tabAdd', newTab)\r\n  } else {\r\n    activeTabId.value = tabId\r\n  }\r\n  \r\n  // Router가 있으면 경로로 이동\r\n  if (router && props.syncUrl) {\r\n    router.push({ path: menuItem.path })\r\n  }\r\n}\r\n\r\n/**\r\n * 탭 추가 핸들러\r\n */\r\nconst handleTabAdd = (tab: DynamicTab) => {\r\n  // 탭이 추가되면 tabs.value 동기화\r\n  if (!Array.isArray(tabs.value)) {\r\n    tabs.value = []\r\n  }\r\n  const exists = tabs.value.find(t => t.id === tab.id)\r\n  if (!exists) {\r\n    tabs.value.push(tab)\r\n  }\r\n  activeTabId.value = tab.id\r\n  emit('tabAdd', tab)\r\n}\r\n\r\n/**\r\n * 탭 닫기 핸들러\r\n */\r\nconst handleTabClose = (id: string) => {\r\n  if (!tabs.value || !Array.isArray(tabs.value) || tabs.value.length === 0) return\r\n  tabs.value = tabs.value.filter((t: DynamicTab) => t.id !== id)\r\n  if (activeTabId.value === id && Array.isArray(tabs.value) && tabs.value.length > 0 && tabs.value[0]) {\r\n    activeTabId.value = tabs.value[0].id\r\n  } else if (Array.isArray(tabs.value) && tabs.value.length === 0) {\r\n    activeTabId.value = undefined\r\n  }\r\n  emit('tabClose', id)\r\n}\r\n\r\n/**\r\n * 탭 변경 핸들러\r\n */\r\nconst handleTabChange = (id: string) => {\r\n  activeTabId.value = id\r\n  emit('tabChange', id)\r\n}\r\n\r\n/**\r\n * tabs를 computed로 만들어 반응성 확보\r\n */\r\nconst tabsComputed = computed(() => Array.isArray(tabs.value) ? tabs.value : [])\r\n</script>\r\n\r\n<template>\r\n  <JLayout v-bind=\"props\">\r\n    <template #header>\r\n      <slot name=\"header\" :is-sidebar-open=\"isSidebarOpen\" :on-sidebar-toggle=\"handleSidebarToggle\">\r\n        <!-- 기본 헤더 예시 -->\r\n        <JHeader \r\n          logo-text=\"JWMS Portal\" \r\n          :styletype=\"props.styletype\"\r\n          :show-sidebar-toggle=\"true\"\r\n          :is-sidebar-open=\"isSidebarOpen\"\r\n          @sidebar-toggle=\"handleSidebarToggle\"\r\n        />\r\n      </slot>\r\n    </template>\r\n    <template #sidebar>\r\n      <slot name=\"sidebar\" :is-sidebar-open=\"isSidebarOpen\">\r\n        <!-- 기본 사이드바 예시 -->\r\n        <JSidebarAdvanced \r\n          :menu-items=\"props.menuItems\" \r\n          :favorites=\"props.favorites\"\r\n          :permissions=\"props.permissions\"\r\n          :styletype=\"props.styletype\" \r\n          :is-visible=\"isSidebarOpen\"\r\n          @menu-click=\"handleMenuClick\"\r\n          @favorite-change=\"(menuKey, isFavorite) => emit('favoriteChange', menuKey, isFavorite)\"\r\n        />\r\n      </slot>\r\n    </template>\r\n    <template #content>\r\n      <slot name=\"content\" :tabs=\"tabsComputed\" :active-tab-id=\"activeTabId\" :tabs-ref=\"tabsRef\">\r\n        <!-- 기본 콘텐츠 예시 - JDynamicTabs 포함 -->\r\n        <JDynamicTabs\r\n          ref=\"tabsRef\"\r\n          :initial-tabs=\"tabsComputed\"\r\n          :default-active-id=\"activeTabId\"\r\n          :styletype=\"props.styletype || 'minimal'\"\r\n          :keep-alive=\"true\"\r\n          @tab-add=\"handleTabAdd\"\r\n          @tab-close=\"handleTabClose\"\r\n          @tab-change=\"handleTabChange\"\r\n        >\r\n          <!-- 슬롯을 통해 외부에서 탭 컨텐츠 제공 가능 -->\r\n          <!-- RouterView를 사용하여 Router의 routes를 재사용 (Route 통일성 보장) -->\r\n          <template\r\n            v-for=\"tab in tabsComputed\"\r\n            :key=\"tab.id\"\r\n            #[`content-${tab.id}`]=\"slotProps\"\r\n          >\r\n            <slot :name=\"`content-${tab.id}`\" v-bind=\"slotProps\">\r\n              <template v-if=\"props.contentMode === 'router-view'\">\r\n                <RouterView\r\n                  v-if=\"tab.meta?.path && router\"\r\n                  :key=\"tab.id\"\r\n                />\r\n                <div v-else class=\"flex-1 p-6\">\r\n                  <p class=\"text-muted-foreground\">콘텐츠를 배치해주세요. (Path: {{ tab.meta?.path || 'N/A' }})</p>\r\n                  <p v-if=\"!router\" class=\"text-xs text-gray-400 mt-2\">\r\n                    Router가 없어서 슬롯으로 콘텐츠를 제공해야 합니다.\r\n                  </p>\r\n                </div>\r\n              </template>\r\n              <!-- contentMode='slot': slot을 비움 → JTabs가 tab.component(resolveComponent 결과) 렌더 -->\r\n            </slot>\r\n          </template>\r\n        </JDynamicTabs>\r\n      </slot>\r\n    </template>\r\n  </JLayout>\r\n</template>\r\n\r\n"],"names":["props","__props","emit","__emit","isSidebarOpen","ref","handleSidebarToggle","tabsRef","tabs","activeTabId","generateTabIdFromPath","path","router","useRouter","handleMenuClick","event","menuItem","tabId","nextTick","resolvedComponent","error","newTab","t","handleTabAdd","tab","handleTabClose","id","handleTabChange","tabsComputed","computed","_openBlock","_createBlock","JLayout","_renderSlot","_ctx","_createVNode","JHeader","JSidebarAdvanced","_cache","menuKey","isFavorite","JDynamicTabs","_renderList","_withCtx","slotProps","_normalizeProps","_guardReactiveProps","_createElementBlock","_Fragment","_unref","RouterView","_hoisted_1","_createElementVNode","_hoisted_2","_toDisplayString","_hoisted_3"],"mappings":"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"}