@j-solution/components 1.6.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -6
- package/assets/jwms-portal-frontend-BtHTA-UF.css +1 -0
- package/assets/styles/global-utilities.css +34 -0
- package/assets/styles/j-components.css +1 -1
- package/assets/styles/themes.css +128 -21
- package/components/atoms/JAvatar.vue.cjs +1 -1
- package/components/atoms/JAvatar.vue.cjs.map +1 -1
- package/components/atoms/JAvatar.vue.js +10 -7
- package/components/atoms/JAvatar.vue.js.map +1 -1
- package/components/atoms/JBadge.vue.cjs +1 -1
- package/components/atoms/JBadge.vue.cjs.map +1 -1
- package/components/atoms/JBadge.vue.js +7 -6
- package/components/atoms/JBadge.vue.js.map +1 -1
- package/components/atoms/JButton.vue.cjs +6 -1
- package/components/atoms/JButton.vue.cjs.map +1 -1
- package/components/atoms/JButton.vue.js +10 -85
- package/components/atoms/JButton.vue.js.map +1 -1
- package/components/atoms/JButton.vue2.cjs +1 -1
- package/components/atoms/JButton.vue2.cjs.map +1 -1
- package/components/atoms/JButton.vue2.js +85 -2
- package/components/atoms/JButton.vue2.js.map +1 -1
- package/components/atoms/JDatepicker.vue.cjs +1 -1
- package/components/atoms/JDatepicker.vue.cjs.map +1 -1
- package/components/atoms/JDatepicker.vue.js +10 -10
- package/components/atoms/JDatepicker.vue.js.map +1 -1
- package/components/atoms/JEditor.vue.cjs +1 -1
- package/components/atoms/JEditor.vue.js +1 -1
- package/components/atoms/JEditor.vue2.cjs +1 -1
- package/components/atoms/JEditor.vue2.cjs.map +1 -1
- package/components/atoms/JEditor.vue2.js +31 -17
- package/components/atoms/JEditor.vue2.js.map +1 -1
- package/components/atoms/JGrid.vue.cjs +1 -1
- package/components/atoms/JGrid.vue.js +2 -2
- package/components/atoms/JGrid.vue2.cjs +1 -1
- package/components/atoms/JGrid.vue2.cjs.map +1 -1
- package/components/atoms/JGrid.vue2.js +59 -43
- package/components/atoms/JGrid.vue2.js.map +1 -1
- package/components/atoms/JIcon.vue.cjs +1 -1
- package/components/atoms/JIcon.vue.cjs.map +1 -1
- package/components/atoms/JIcon.vue.js +14 -13
- package/components/atoms/JIcon.vue.js.map +1 -1
- package/components/atoms/JKbd.vue.cjs +1 -1
- package/components/atoms/JKbd.vue.cjs.map +1 -1
- package/components/atoms/JKbd.vue.js +13 -10
- package/components/atoms/JKbd.vue.js.map +1 -1
- package/components/atoms/JLabel.vue.cjs +1 -1
- package/components/atoms/JLabel.vue.cjs.map +1 -1
- package/components/atoms/JLabel.vue.js +26 -22
- package/components/atoms/JLabel.vue.js.map +1 -1
- package/components/atoms/JLink.vue.cjs +1 -1
- package/components/atoms/JLink.vue.cjs.map +1 -1
- package/components/atoms/JLink.vue.js +5 -5
- package/components/atoms/JLink.vue.js.map +1 -1
- package/components/atoms/JPreview.vue.cjs +1 -1
- package/components/atoms/JPreview.vue.js +2 -2
- package/components/atoms/JPreview.vue2.cjs +1 -1
- package/components/atoms/JPreview.vue2.cjs.map +1 -1
- package/components/atoms/JPreview.vue2.js +33 -20
- package/components/atoms/JPreview.vue2.js.map +1 -1
- package/components/atoms/JProgress.vue.cjs +1 -1
- package/components/atoms/JProgress.vue.cjs.map +1 -1
- package/components/atoms/JProgress.vue.js +15 -9
- package/components/atoms/JProgress.vue.js.map +1 -1
- package/components/atoms/JRadio.vue.cjs +1 -1
- package/components/atoms/JRadio.vue.cjs.map +1 -1
- package/components/atoms/JRadio.vue.js +1 -1
- package/components/atoms/JRadio.vue.js.map +1 -1
- package/components/atoms/JSearchCombo.vue.cjs +1 -1
- package/components/atoms/JSearchCombo.vue.cjs.map +1 -1
- package/components/atoms/JSearchCombo.vue.js +38 -37
- package/components/atoms/JSearchCombo.vue.js.map +1 -1
- package/components/atoms/JSectionTitle.vue.cjs +7 -0
- package/components/atoms/JSectionTitle.vue.cjs.map +1 -0
- package/components/atoms/JSectionTitle.vue.js +13 -0
- package/components/atoms/JSectionTitle.vue.js.map +1 -0
- package/components/atoms/JSectionTitle.vue2.cjs +2 -0
- package/components/atoms/JSectionTitle.vue2.cjs.map +1 -0
- package/components/atoms/JSectionTitle.vue2.js +67 -0
- package/components/atoms/JSectionTitle.vue2.js.map +1 -0
- package/components/atoms/JSpinner.vue.cjs +1 -1
- package/components/atoms/JSpinner.vue.cjs.map +1 -1
- package/components/atoms/JSpinner.vue.js +8 -7
- package/components/atoms/JSpinner.vue.js.map +1 -1
- package/components/atoms/JSplitter.vue.cjs +6 -1
- package/components/atoms/JSplitter.vue.cjs.map +1 -1
- package/components/atoms/JSplitter.vue.js +10 -54
- package/components/atoms/JSplitter.vue.js.map +1 -1
- package/components/atoms/JSplitter.vue2.cjs +1 -1
- package/components/atoms/JSplitter.vue2.cjs.map +1 -1
- package/components/atoms/JSplitter.vue2.js +59 -2
- package/components/atoms/JSplitter.vue2.js.map +1 -1
- package/components/atoms/JTooltip.vue.cjs +1 -1
- package/components/atoms/JTooltip.vue.cjs.map +1 -1
- package/components/atoms/JTooltip.vue.js +18 -15
- package/components/atoms/JTooltip.vue.js.map +1 -1
- package/components/examples/ExampleCrudPage.vue.cjs +1 -1
- package/components/examples/ExampleCrudPage.vue.cjs.map +1 -1
- package/components/examples/ExampleCrudPage.vue.js +265 -191
- package/components/examples/ExampleCrudPage.vue.js.map +1 -1
- package/components/examples/ExampleTabMappingPage.vue.cjs +1 -1
- package/components/examples/ExampleTabMappingPage.vue.cjs.map +1 -1
- package/components/examples/ExampleTabMappingPage.vue.js +349 -333
- package/components/examples/ExampleTabMappingPage.vue.js.map +1 -1
- package/components/molecules/JAlert.vue.cjs +1 -1
- package/components/molecules/JAlert.vue.cjs.map +1 -1
- package/components/molecules/JAlert.vue.js +18 -16
- package/components/molecules/JAlert.vue.js.map +1 -1
- package/components/molecules/JBreadcrumb.vue.cjs +1 -1
- package/components/molecules/JBreadcrumb.vue.cjs.map +1 -1
- package/components/molecules/JBreadcrumb.vue.js +3 -3
- package/components/molecules/JBreadcrumb.vue.js.map +1 -1
- package/components/molecules/JCard.vue.cjs +1 -1
- package/components/molecules/JCard.vue.cjs.map +1 -1
- package/components/molecules/JCard.vue.js +55 -39
- package/components/molecules/JCard.vue.js.map +1 -1
- package/components/molecules/JEmptyState.vue.cjs +7 -0
- package/components/molecules/JEmptyState.vue.cjs.map +1 -0
- package/components/molecules/JEmptyState.vue.js +13 -0
- package/components/molecules/JEmptyState.vue.js.map +1 -0
- package/components/molecules/JEmptyState.vue2.cjs +2 -0
- package/components/molecules/JEmptyState.vue2.cjs.map +1 -0
- package/components/molecules/JEmptyState.vue2.js +127 -0
- package/components/molecules/JEmptyState.vue2.js.map +1 -0
- package/components/molecules/JFormField.vue.cjs +6 -1
- package/components/molecules/JFormField.vue.cjs.map +1 -1
- package/components/molecules/JFormField.vue.js +10 -262
- package/components/molecules/JFormField.vue.js.map +1 -1
- package/components/molecules/JFormField.vue2.cjs +2 -0
- package/components/molecules/JFormField.vue2.cjs.map +1 -0
- package/components/molecules/JFormField.vue2.js +271 -0
- package/components/molecules/JFormField.vue2.js.map +1 -0
- package/components/molecules/JTabs.vue.cjs +1 -1
- package/components/molecules/JTabs.vue.js +1 -1
- package/components/molecules/JTabs.vue2.cjs +1 -1
- package/components/molecules/JTabs.vue2.cjs.map +1 -1
- package/components/molecules/JTabs.vue2.js +50 -56
- package/components/molecules/JTabs.vue2.js.map +1 -1
- package/components/molecules/JTitlebar.vue.cjs +1 -1
- package/components/molecules/JTitlebar.vue.cjs.map +1 -1
- package/components/molecules/JTitlebar.vue.js +49 -47
- package/components/molecules/JTitlebar.vue.js.map +1 -1
- package/components/organisms/JDynamicForm.vue2.cjs +1 -1
- package/components/organisms/JDynamicForm.vue2.cjs.map +1 -1
- package/components/organisms/JDynamicForm.vue2.js +35 -32
- package/components/organisms/JDynamicForm.vue2.js.map +1 -1
- package/components/organisms/JDynamicTabs.vue.cjs +1 -1
- package/components/organisms/JDynamicTabs.vue.cjs.map +1 -1
- package/components/organisms/JDynamicTabs.vue.js +47 -52
- package/components/organisms/JDynamicTabs.vue.js.map +1 -1
- package/components/organisms/JFilterBar.vue.cjs +6 -1
- package/components/organisms/JFilterBar.vue.cjs.map +1 -1
- package/components/organisms/JFilterBar.vue.js +10 -137
- package/components/organisms/JFilterBar.vue.js.map +1 -1
- package/components/organisms/JFilterBar.vue2.cjs +1 -1
- package/components/organisms/JFilterBar.vue2.cjs.map +1 -1
- package/components/organisms/JFilterBar.vue2.js +141 -2
- package/components/organisms/JFilterBar.vue2.js.map +1 -1
- package/components/organisms/JFormModal.vue.cjs +1 -1
- package/components/organisms/JFormModal.vue.cjs.map +1 -1
- package/components/organisms/JFormModal.vue.js +54 -49
- package/components/organisms/JFormModal.vue.js.map +1 -1
- package/components/organisms/JHeader.vue.cjs +1 -1
- package/components/organisms/JHeader.vue.cjs.map +1 -1
- package/components/organisms/JHeader.vue.js +211 -208
- package/components/organisms/JHeader.vue.js.map +1 -1
- package/components/organisms/JModal.vue.cjs +1 -1
- package/components/organisms/JModal.vue.cjs.map +1 -1
- package/components/organisms/JModal.vue.js +31 -26
- package/components/organisms/JModal.vue.js.map +1 -1
- package/components/organisms/JPageContainer.vue.cjs +1 -1
- package/components/organisms/JPageContainer.vue.cjs.map +1 -1
- package/components/organisms/JPageContainer.vue.js +22 -22
- package/components/organisms/JPageContainer.vue.js.map +1 -1
- package/components/organisms/JSearchPanel.vue2.cjs +1 -1
- package/components/organisms/JSearchPanel.vue2.cjs.map +1 -1
- package/components/organisms/JSearchPanel.vue2.js +34 -32
- package/components/organisms/JSearchPanel.vue2.js.map +1 -1
- package/components/organisms/JShuttle.vue.cjs +7 -0
- package/components/organisms/JShuttle.vue.cjs.map +1 -0
- package/components/organisms/JShuttle.vue.js +13 -0
- package/components/organisms/JShuttle.vue.js.map +1 -0
- package/components/organisms/JShuttle.vue2.cjs +2 -0
- package/components/organisms/JShuttle.vue2.cjs.map +1 -0
- package/components/organisms/JShuttle.vue2.js +216 -0
- package/components/organisms/JShuttle.vue2.js.map +1 -0
- package/components/organisms/JSidebarAdvanced.vue.cjs +1 -1
- package/components/organisms/JSidebarAdvanced.vue.js +7 -7
- package/components/organisms/JSidebarAdvanced.vue2.cjs +1 -1
- package/components/organisms/JSidebarAdvanced.vue2.cjs.map +1 -1
- package/components/organisms/JSidebarAdvanced.vue2.js +40 -40
- package/components/organisms/JSidebarAdvanced.vue2.js.map +1 -1
- package/components/organisms/JSidebarSimple/JDynamicMenuItem.vue.cjs +1 -1
- package/components/organisms/JSidebarSimple/JDynamicMenuItem.vue.cjs.map +1 -1
- package/components/organisms/JSidebarSimple/JDynamicMenuItem.vue.js +83 -63
- package/components/organisms/JSidebarSimple/JDynamicMenuItem.vue.js.map +1 -1
- package/components/organisms/JSidebarSimple.vue.cjs +1 -1
- package/components/organisms/JSidebarSimple.vue.js +2 -2
- package/components/organisms/JSidebarSimple.vue2.cjs +1 -1
- package/components/organisms/JSidebarSimple.vue2.cjs.map +1 -1
- package/components/organisms/JSidebarSimple.vue2.js +2 -2
- package/components/organisms/JSidebarSimple.vue2.js.map +1 -1
- package/components/shadcn/AccordionTrigger.vue.cjs +1 -1
- package/components/shadcn/AccordionTrigger.vue.cjs.map +1 -1
- package/components/shadcn/AccordionTrigger.vue.js +3 -3
- package/components/shadcn/AccordionTrigger.vue.js.map +1 -1
- package/components/shadcn/Card.vue.cjs +1 -1
- package/components/shadcn/Card.vue.cjs.map +1 -1
- package/components/shadcn/Card.vue.js +1 -1
- package/components/shadcn/Card.vue.js.map +1 -1
- package/components/shadcn/CardContent.vue.cjs +1 -1
- package/components/shadcn/CardContent.vue.cjs.map +1 -1
- package/components/shadcn/CardContent.vue.js +4 -4
- package/components/shadcn/CardContent.vue.js.map +1 -1
- package/components/shadcn/CardDescription.vue.cjs +1 -1
- package/components/shadcn/CardDescription.vue.cjs.map +1 -1
- package/components/shadcn/CardDescription.vue.js +1 -1
- package/components/shadcn/CardDescription.vue.js.map +1 -1
- package/components/shadcn/CardFooter.vue.cjs +1 -1
- package/components/shadcn/CardFooter.vue.cjs.map +1 -1
- package/components/shadcn/CardFooter.vue.js +7 -7
- package/components/shadcn/CardFooter.vue.js.map +1 -1
- package/components/shadcn/CardHeader.vue.cjs +1 -1
- package/components/shadcn/CardHeader.vue.cjs.map +1 -1
- package/components/shadcn/CardHeader.vue.js +8 -8
- package/components/shadcn/CardHeader.vue.js.map +1 -1
- package/components/shadcn/CardTitle.vue.cjs +1 -1
- package/components/shadcn/CardTitle.vue.cjs.map +1 -1
- package/components/shadcn/CardTitle.vue.js +5 -5
- package/components/shadcn/CardTitle.vue.js.map +1 -1
- package/components/shadcn/Input.vue.cjs +1 -1
- package/components/shadcn/Input.vue.cjs.map +1 -1
- package/components/shadcn/Input.vue.js +3 -3
- package/components/shadcn/Input.vue.js.map +1 -1
- package/components/shadcn/SelectTrigger.vue.cjs +1 -1
- package/components/shadcn/SelectTrigger.vue.cjs.map +1 -1
- package/components/shadcn/SelectTrigger.vue.js +2 -2
- package/components/shadcn/SelectTrigger.vue.js.map +1 -1
- package/components/shadcn/Switch.vue.cjs +1 -1
- package/components/shadcn/Switch.vue.cjs.map +1 -1
- package/components/shadcn/Switch.vue.js +2 -2
- package/components/shadcn/Switch.vue.js.map +1 -1
- package/components/shadcn/TabsContent.vue.cjs +1 -1
- package/components/shadcn/TabsContent.vue.cjs.map +1 -1
- package/components/shadcn/TabsContent.vue.js +1 -1
- package/components/shadcn/TabsContent.vue.js.map +1 -1
- package/components/shadcn/TabsList.vue.cjs +1 -1
- package/components/shadcn/TabsList.vue.cjs.map +1 -1
- package/components/shadcn/TabsList.vue.js +10 -10
- package/components/shadcn/TabsList.vue.js.map +1 -1
- package/components/shadcn/TabsTrigger.vue.cjs +1 -1
- package/components/shadcn/TabsTrigger.vue.cjs.map +1 -1
- package/components/shadcn/TabsTrigger.vue.js +4 -4
- package/components/shadcn/TabsTrigger.vue.js.map +1 -1
- package/components/shadcn/Textarea.vue.cjs +1 -1
- package/components/shadcn/Textarea.vue.cjs.map +1 -1
- package/components/shadcn/Textarea.vue.js +2 -2
- package/components/shadcn/Textarea.vue.js.map +1 -1
- package/components/shadcn/index.cjs +1 -1
- package/components/shadcn/index.cjs.map +1 -1
- package/components/shadcn/index.js +9 -8
- package/components/shadcn/index.js.map +1 -1
- package/components/templates/JLayout.vue.cjs.map +1 -1
- package/components/templates/JLayout.vue.js.map +1 -1
- package/index.cjs +1 -1
- package/index.js +73 -67
- package/package.json +1 -1
- package/types/index.d.ts +1025 -766
- package/assets/jwms-portal-frontend-DntSIcYt.css +0 -1
- package/components/molecules/JFormField.vue3.cjs +0 -2
- package/components/molecules/JFormField.vue3.cjs.map +0 -1
- package/components/molecules/JFormField.vue3.js +0 -6
- package/components/molecules/JFormField.vue3.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JDynamicTabs.vue.cjs","sources":["../../../../src/components/organisms/JDynamicTabs.vue"],"sourcesContent":["<script setup lang=\"ts\">\r\nimport { ref, computed, watch } from 'vue'\r\nimport JTabs from '@/components/molecules/JTabs.vue'\r\nimport type { \r\n JDynamicTabsProps, \r\n JDynamicTabsEmits, \r\n JDynamicTabsMethods,\r\n DynamicTab \r\n} from '@/types/dynamic-tabs.types'\r\n\r\n/**\r\n * JDynamicTabs - 동적 탭 컴포넌트 (organisms)\r\n * Dynamic Tabs Component\r\n * \r\n * @description\r\n * 사이드 메뉴 클릭으로 탭을 동적으로 추가/제거할 수 있는 탭 컴포넌트입니다.\r\n * \r\n * Features:\r\n * - 탭 동적 추가/제거\r\n * - 중복 탭 방지 (이미 있으면 활성화)\r\n * - 닫기 버튼으로 탭 제거\r\n * - 활성 탭 자동 재설정\r\n * - 최대 탭 개수 제한\r\n * \r\n * @example\r\n * const tabsRef = ref()\r\n * \r\n * const handleMenuClick = (menuItem) => {\r\n * tabsRef.value?.addTab({\r\n * id: menuItem.id,\r\n * label: menuItem.label,\r\n * component: menuItem.component,\r\n * closable: true\r\n * })\r\n * }\r\n */\r\n\r\nconst props = withDefaults(defineProps<JDynamicTabsProps>(), {\r\n initialTabs: () => [],\r\n maxTabs: 0, // 0 = unlimited\r\n emptyMessage: '탭을 추가해주세요.',\r\n styletype: 'default',\r\n})\r\n\r\nconst emit = defineEmits<JDynamicTabsEmits>()\r\n\r\n/**\r\n * 탭 목록 (내부 상태)\r\n * Tabs list (internal state)\r\n */\r\nconst tabs = ref<DynamicTab[]>(Array.isArray(props.initialTabs) ? [...props.initialTabs] : [])\r\n\r\n/**\r\n * 현재 활성화된 탭 ID (내부 상태)\r\n * Current active tab ID (internal state)\r\n */\r\nconst activeTabId = ref<string>(\r\n props.defaultActiveId || (Array.isArray(props.initialTabs) && props.initialTabs.length > 0 ? props.initialTabs[0]?.id : '') || ''\r\n)\r\n\r\n/**\r\n * initialTabs가 변경되면 내부 상태 업데이트\r\n * Update internal state when initialTabs changes\r\n */\r\nwatch(() => props.initialTabs, (newTabs) => {\r\n if (!newTabs) {\r\n tabs.value = []\r\n return\r\n }\r\n \r\n if (Array.isArray(newTabs) && newTabs.length > 0) {\r\n tabs.value = [...newTabs]\r\n if (!activeTabId.value && newTabs[0]) {\r\n activeTabId.value = newTabs[0].id\r\n }\r\n } else if (Array.isArray(newTabs)) {\r\n // 빈 배열인 경우\r\n tabs.value = []\r\n } else {\r\n // 배열이 아닌 경우\r\n tabs.value = []\r\n }\r\n}, { immediate: true })\r\n\r\n/**\r\n * 탭 추가\r\n * Add tab (if exists, activate it; otherwise, add to array and activate)\r\n * \r\n * @param tab - 추가할 탭 정보\r\n */\r\nconst addTab = (tab: DynamicTab) => {\r\n // tabs.value가 배열이 아니면 초기화\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 === tab.id)\r\n \r\n if (existingTab) {\r\n // 이미 있으면 해당 탭 활성화\r\n activateTab(tab.id)\r\n return\r\n }\r\n \r\n // 최대 탭 개수 체크\r\n if (props.maxTabs > 0 && tabs.value.length >= props.maxTabs) {\r\n console.warn(`최대 ${props.maxTabs}개의 탭만 열 수 있습니다.`)\r\n return\r\n }\r\n \r\n // 새 탭 추가\r\n tabs.value.push({\r\n ...tab,\r\n closable: tab.closable !== false, // 기본값 true\r\n })\r\n \r\n // 새로 추가된 탭 활성화\r\n activateTab(tab.id)\r\n \r\n // 이벤트 발생\r\n emit('tabAdd', tab)\r\n}\r\n\r\n/**\r\n * 탭 닫기\r\n * Close tab (remove from array and reset active tab)\r\n * \r\n * @param id - 닫을 탭 ID\r\n */\r\nconst closeTab = (id: string) => {\r\n if (!Array.isArray(tabs.value)) {\r\n return\r\n }\r\n \r\n const tabIndex = tabs.value.findIndex(t => t.id === id)\r\n \r\n if (tabIndex === -1) return\r\n \r\n // 탭 제거\r\n tabs.value.splice(tabIndex, 1)\r\n \r\n // 활성 탭 재설정\r\n if (activeTabId.value === id) {\r\n if (tabs.value.length > 0) {\r\n // 이전 탭이 있으면 이전 탭 활성화, 없으면 다음 탭 활성화\r\n const newIndex = Math.min(tabIndex, tabs.value.length - 1)\r\n activeTabId.value = tabs.value[newIndex]?.id || ''\r\n } else {\r\n activeTabId.value = ''\r\n }\r\n }\r\n \r\n // 이벤트 발생\r\n emit('tabClose', id)\r\n}\r\n\r\n/**\r\n * 탭 활성화\r\n * Activate tab\r\n * \r\n * @param id - 활성화할 탭 ID\r\n */\r\nconst activateTab = (id: string) => {\r\n if (!Array.isArray(tabs.value)) {\r\n return\r\n }\r\n const tab = tabs.value.find(t => t.id === id)\r\n if (tab) {\r\n activeTabId.value = id\r\n }\r\n}\r\n\r\n/**\r\n * 특정 탭 찾기\r\n * Find specific tab\r\n * \r\n * @param id - 찾을 탭 ID\r\n */\r\nconst findTab = (id: string): DynamicTab | undefined => {\r\n if (!Array.isArray(tabs.value)) {\r\n return undefined\r\n }\r\n return tabs.value.find(t => t.id === id)\r\n}\r\n\r\n/**\r\n * 모든 탭 닫기 (closable이 true인 탭만)\r\n * Close all tabs (only closable tabs)\r\n */\r\nconst closeAllTabs = () => {\r\n if (!Array.isArray(tabs.value)) {\r\n return\r\n }\r\n const closableTabs = tabs.value.filter(t => t.closable)\r\n closableTabs.forEach(tab => closeTab(tab.id))\r\n}\r\n\r\n/**\r\n * 탭 변경 핸들러\r\n * Tab change handler\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 * 탭 닫기 핸들러\r\n * Tab close handler\r\n */\r\nconst handleTabClose = (id: string) => {\r\n closeTab(id)\r\n}\r\n\r\n/**\r\n * 외부에서 호출 가능한 메서드 노출\r\n * Expose methods for external use\r\n */\r\ndefineExpose<JDynamicTabsMethods>({\r\n addTab,\r\n closeTab,\r\n activateTab,\r\n findTab,\r\n closeAllTabs,\r\n})\r\n\r\n/**\r\n * 안전한 tabs 배열\r\n * Safe tabs array\r\n */\r\nconst safeTabs = computed(() => {\r\n return Array.isArray(tabs.value) ? tabs.value : []\r\n})\r\n\r\n/**\r\n * 탭이 있는지 여부\r\n * Whether there are tabs\r\n */\r\nconst hasTabs = computed(() => safeTabs.value.length > 0)\r\n\r\n/**\r\n * 루트 클래스\r\n * Root classes\r\n */\r\nconst rootClasses = computed(() => {\r\n const classes = ['w-full', 'h-full']\r\n \r\n if (props.className) {\r\n classes.push(props.className)\r\n }\r\n \r\n return classes.join(' ')\r\n})\r\n\r\n/**\r\n * 콘텐츠 클래스\r\n * Content classes\r\n */\r\nconst contentClasses = computed(() => {\r\n const classes = []\r\n \r\n if (props.contentClassName) {\r\n classes.push(props.contentClassName)\r\n }\r\n \r\n return classes.join(' ')\r\n})\r\n</script>\r\n\r\n<template>\r\n <div :class=\"rootClasses\">\r\n <!-- 탭이 있을 경우 / When there are tabs -->\r\n <JTabs\r\n v-if=\"hasTabs\"\r\n :tabs=\"safeTabs\"\r\n :active-tab-id=\"activeTabId\"\r\n :styletype=\"props.styletype\"\r\n :class=\"contentClasses\"\r\n @tab-change=\"handleTabChange\"\r\n @tab-close=\"handleTabClose\"\r\n >\r\n <!-- 각 탭 콘텐츠 슬롯 전달 / Forward slots for each tab content -->\r\n <template\r\n v-for=\"tab in safeTabs\"\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>\r\n </JTabs>\r\n \r\n <!-- 탭이 없을 경우 / When there are no tabs -->\r\n <div\r\n v-else\r\n class=\"flex items-center justify-center h-full w-full\"\r\n >\r\n <div class=\"text-center\">\r\n <p class=\"text-muted-foreground text-lg\">{{ emptyMessage }}</p>\r\n <p class=\"text-muted-foreground/60 text-sm mt-2\">\r\n 메뉴를 클릭하여 탭을 추가하세요.\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n"],"names":["props","__props","emit","__emit","tabs","ref","activeTabId","watch","newTabs","addTab","tab","t","activateTab","closeTab","id","tabIndex","newIndex","findTab","closeAllTabs","handleTabChange","handleTabClose","__expose","safeTabs","computed","hasTabs","rootClasses","classes","contentClasses","_createElementBlock","_createBlock","JTabs","_renderList","_withCtx","slotProps","_renderSlot","_ctx","_openBlock","_hoisted_1","_createElementVNode","_hoisted_2","_hoisted_3","_toDisplayString","_cache"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"JDynamicTabs.vue.cjs","sources":["../../../../src/components/organisms/JDynamicTabs.vue"],"sourcesContent":["<script setup lang=\"ts\">\r\nimport { ref, computed, watch } from 'vue'\r\nimport JTabs from '@/components/molecules/JTabs.vue'\r\nimport { cn } from '@/lib/utils'\r\nimport type {\r\n JDynamicTabsProps, \r\n JDynamicTabsEmits, \r\n JDynamicTabsMethods,\r\n DynamicTab \r\n} from '@/types/dynamic-tabs.types'\r\n\r\n/**\r\n * JDynamicTabs - 동적 탭 컴포넌트 (organisms)\r\n * Dynamic Tabs Component\r\n * \r\n * @description\r\n * 사이드 메뉴 클릭으로 탭을 동적으로 추가/제거할 수 있는 탭 컴포넌트입니다.\r\n * \r\n * Features:\r\n * - 탭 동적 추가/제거\r\n * - 중복 탭 방지 (이미 있으면 활성화)\r\n * - 닫기 버튼으로 탭 제거\r\n * - 활성 탭 자동 재설정\r\n * - 최대 탭 개수 제한\r\n * \r\n * @example\r\n * const tabsRef = ref()\r\n * \r\n * const handleMenuClick = (menuItem) => {\r\n * tabsRef.value?.addTab({\r\n * id: menuItem.id,\r\n * label: menuItem.label,\r\n * component: menuItem.component,\r\n * closable: true\r\n * })\r\n * }\r\n */\r\n\r\nconst props = withDefaults(defineProps<JDynamicTabsProps>(), {\r\n initialTabs: () => [],\r\n maxTabs: 0, // 0 = unlimited\r\n emptyMessage: '탭을 추가해주세요.',\r\n styletype: 'default',\r\n})\r\n\r\nconst emit = defineEmits<JDynamicTabsEmits>()\r\n\r\n/**\r\n * 탭 목록 (내부 상태)\r\n * Tabs list (internal state)\r\n */\r\nconst tabs = ref<DynamicTab[]>(Array.isArray(props.initialTabs) ? [...props.initialTabs] : [])\r\n\r\n/**\r\n * 현재 활성화된 탭 ID (내부 상태)\r\n * Current active tab ID (internal state)\r\n */\r\nconst activeTabId = ref<string>(\r\n props.defaultActiveId || (Array.isArray(props.initialTabs) && props.initialTabs.length > 0 ? props.initialTabs[0]?.id : '') || ''\r\n)\r\n\r\n/**\r\n * initialTabs가 변경되면 내부 상태 업데이트\r\n * Update internal state when initialTabs changes\r\n */\r\nwatch(() => props.initialTabs, (newTabs) => {\r\n if (!newTabs) {\r\n tabs.value = []\r\n return\r\n }\r\n \r\n if (Array.isArray(newTabs) && newTabs.length > 0) {\r\n tabs.value = [...newTabs]\r\n if (!activeTabId.value && newTabs[0]) {\r\n activeTabId.value = newTabs[0].id\r\n }\r\n } else if (Array.isArray(newTabs)) {\r\n // 빈 배열인 경우\r\n tabs.value = []\r\n } else {\r\n // 배열이 아닌 경우\r\n tabs.value = []\r\n }\r\n}, { immediate: true })\r\n\r\n/**\r\n * 탭 추가\r\n * Add tab (if exists, activate it; otherwise, add to array and activate)\r\n * \r\n * @param tab - 추가할 탭 정보\r\n */\r\nconst addTab = (tab: DynamicTab) => {\r\n // tabs.value가 배열이 아니면 초기화\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 === tab.id)\r\n \r\n if (existingTab) {\r\n // 이미 있으면 해당 탭 활성화\r\n activateTab(tab.id)\r\n return\r\n }\r\n \r\n // 최대 탭 개수 체크\r\n if (props.maxTabs > 0 && tabs.value.length >= props.maxTabs) {\r\n console.warn(`최대 ${props.maxTabs}개의 탭만 열 수 있습니다.`)\r\n return\r\n }\r\n \r\n // 새 탭 추가\r\n tabs.value.push({\r\n ...tab,\r\n closable: tab.closable !== false, // 기본값 true\r\n })\r\n \r\n // 새로 추가된 탭 활성화\r\n activateTab(tab.id)\r\n \r\n // 이벤트 발생\r\n emit('tabAdd', tab)\r\n}\r\n\r\n/**\r\n * 탭 닫기\r\n * Close tab (remove from array and reset active tab)\r\n * \r\n * @param id - 닫을 탭 ID\r\n */\r\nconst closeTab = (id: string) => {\r\n if (!Array.isArray(tabs.value)) {\r\n return\r\n }\r\n \r\n const tabIndex = tabs.value.findIndex(t => t.id === id)\r\n \r\n if (tabIndex === -1) return\r\n \r\n // 탭 제거\r\n tabs.value.splice(tabIndex, 1)\r\n \r\n // 활성 탭 재설정\r\n if (activeTabId.value === id) {\r\n if (tabs.value.length > 0) {\r\n // 이전 탭이 있으면 이전 탭 활성화, 없으면 다음 탭 활성화\r\n const newIndex = Math.min(tabIndex, tabs.value.length - 1)\r\n activeTabId.value = tabs.value[newIndex]?.id || ''\r\n } else {\r\n activeTabId.value = ''\r\n }\r\n }\r\n \r\n // 이벤트 발생\r\n emit('tabClose', id)\r\n}\r\n\r\n/**\r\n * 탭 활성화\r\n * Activate tab\r\n * \r\n * @param id - 활성화할 탭 ID\r\n */\r\nconst activateTab = (id: string) => {\r\n if (!Array.isArray(tabs.value)) {\r\n return\r\n }\r\n const tab = tabs.value.find(t => t.id === id)\r\n if (tab) {\r\n activeTabId.value = id\r\n }\r\n}\r\n\r\n/**\r\n * 특정 탭 찾기\r\n * Find specific tab\r\n * \r\n * @param id - 찾을 탭 ID\r\n */\r\nconst findTab = (id: string): DynamicTab | undefined => {\r\n if (!Array.isArray(tabs.value)) {\r\n return undefined\r\n }\r\n return tabs.value.find(t => t.id === id)\r\n}\r\n\r\n/**\r\n * 모든 탭 닫기 (closable이 true인 탭만)\r\n * Close all tabs (only closable tabs)\r\n */\r\nconst closeAllTabs = () => {\r\n if (!Array.isArray(tabs.value)) {\r\n return\r\n }\r\n const closableTabs = tabs.value.filter(t => t.closable)\r\n closableTabs.forEach(tab => closeTab(tab.id))\r\n}\r\n\r\n/**\r\n * 탭 변경 핸들러\r\n * Tab change handler\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 * 탭 닫기 핸들러\r\n * Tab close handler\r\n */\r\nconst handleTabClose = (id: string) => {\r\n closeTab(id)\r\n}\r\n\r\n/**\r\n * 외부에서 호출 가능한 메서드 노출\r\n * Expose methods for external use\r\n */\r\ndefineExpose<JDynamicTabsMethods>({\r\n addTab,\r\n closeTab,\r\n activateTab,\r\n findTab,\r\n closeAllTabs,\r\n})\r\n\r\n/**\r\n * 안전한 tabs 배열\r\n * Safe tabs array\r\n */\r\nconst safeTabs = computed(() => {\r\n return Array.isArray(tabs.value) ? tabs.value : []\r\n})\r\n\r\n/**\r\n * 탭이 있는지 여부\r\n * Whether there are tabs\r\n */\r\nconst hasTabs = computed(() => safeTabs.value.length > 0)\r\n\r\n/**\r\n * 루트 클래스\r\n * Root classes\r\n */\r\nconst rootClasses = computed(() => {\r\n return cn('w-full h-full', props.class)\r\n})\r\n\r\n/**\r\n * 콘텐츠 클래스\r\n * Content classes\r\n */\r\nconst contentClasses = computed(() => {\r\n return props.contentClass || ''\r\n})\r\n</script>\r\n\r\n<template>\r\n <div :class=\"rootClasses\">\r\n <!-- 탭이 있을 경우 / When there are tabs -->\r\n <JTabs\r\n v-if=\"hasTabs\"\r\n :tabs=\"safeTabs\"\r\n :active-tab-id=\"activeTabId\"\r\n :styletype=\"props.styletype\"\r\n :class=\"contentClasses\"\r\n @tab-change=\"handleTabChange\"\r\n @tab-close=\"handleTabClose\"\r\n >\r\n <!-- 각 탭 콘텐츠 슬롯 전달 / Forward slots for each tab content -->\r\n <template\r\n v-for=\"tab in safeTabs\"\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>\r\n </JTabs>\r\n \r\n <!-- 탭이 없을 경우 / When there are no tabs -->\r\n <div\r\n v-else\r\n class=\"flex items-center justify-center h-full w-full\"\r\n >\r\n <div class=\"text-center\">\r\n <p class=\"text-muted-foreground text-lg\">{{ emptyMessage }}</p>\r\n <p class=\"text-muted-foreground/60 text-sm mt-2\">\r\n 메뉴를 클릭하여 탭을 추가하세요.\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n"],"names":["props","__props","emit","__emit","tabs","ref","activeTabId","watch","newTabs","addTab","tab","t","activateTab","closeTab","id","tabIndex","newIndex","findTab","closeAllTabs","handleTabChange","handleTabClose","__expose","safeTabs","computed","hasTabs","rootClasses","cn","contentClasses","_createElementBlock","_createBlock","JTabs","_renderList","_withCtx","slotProps","_renderSlot","_ctx","_openBlock","_hoisted_1","_createElementVNode","_hoisted_2","_hoisted_3","_toDisplayString","_cache"],"mappings":"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"}
|
|
@@ -1,116 +1,111 @@
|
|
|
1
|
-
import { defineComponent as
|
|
2
|
-
import
|
|
3
|
-
|
|
1
|
+
import { defineComponent as B, ref as b, watch as E, computed as i, createElementBlock as y, openBlock as d, normalizeClass as m, createBlock as M, createSlots as S, renderList as z, withCtx as D, renderSlot as J, normalizeProps as P, guardReactiveProps as j, createElementVNode as v, toDisplayString as L } from "vue";
|
|
2
|
+
import N from "../molecules/JTabs.vue.js";
|
|
3
|
+
import { cn as R } from "../../lib/utils.js";
|
|
4
|
+
const V = {
|
|
4
5
|
key: 1,
|
|
5
6
|
class: "flex items-center justify-center h-full w-full"
|
|
6
|
-
},
|
|
7
|
+
}, q = { class: "text-center" }, w = { class: "text-muted-foreground text-lg" }, K = /* @__PURE__ */ B({
|
|
7
8
|
__name: "JDynamicTabs",
|
|
8
9
|
props: {
|
|
9
10
|
initialTabs: { default: () => [] },
|
|
10
11
|
defaultActiveId: {},
|
|
11
12
|
maxTabs: { default: 0 },
|
|
12
13
|
emptyMessage: { default: "탭을 추가해주세요." },
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
class: {},
|
|
15
|
+
contentClass: {},
|
|
15
16
|
styletype: { default: "default" }
|
|
16
17
|
},
|
|
17
18
|
emits: ["tabChange", "tabClose", "tabAdd"],
|
|
18
|
-
setup(
|
|
19
|
-
const
|
|
20
|
-
|
|
19
|
+
setup(f, { expose: A, emit: p }) {
|
|
20
|
+
const s = f, n = p, a = b(Array.isArray(s.initialTabs) ? [...s.initialTabs] : []), r = b(
|
|
21
|
+
s.defaultActiveId || (Array.isArray(s.initialTabs) && s.initialTabs.length > 0 ? s.initialTabs[0]?.id : "") || ""
|
|
21
22
|
);
|
|
22
|
-
|
|
23
|
+
E(() => s.initialTabs, (e) => {
|
|
23
24
|
if (!e) {
|
|
24
25
|
a.value = [];
|
|
25
26
|
return;
|
|
26
27
|
}
|
|
27
|
-
Array.isArray(e) && e.length > 0 ? (a.value = [...e], !
|
|
28
|
+
Array.isArray(e) && e.length > 0 ? (a.value = [...e], !r.value && e[0] && (r.value = e[0].id)) : Array.isArray(e) ? a.value = [] : a.value = [];
|
|
28
29
|
}, { immediate: !0 });
|
|
29
|
-
const
|
|
30
|
+
const T = (e) => {
|
|
30
31
|
if (Array.isArray(a.value) || (a.value = []), a.value.find((l) => l.id === e.id)) {
|
|
31
32
|
u(e.id);
|
|
32
33
|
return;
|
|
33
34
|
}
|
|
34
|
-
if (
|
|
35
|
-
console.warn(`최대 ${
|
|
35
|
+
if (s.maxTabs > 0 && a.value.length >= s.maxTabs) {
|
|
36
|
+
console.warn(`최대 ${s.maxTabs}개의 탭만 열 수 있습니다.`);
|
|
36
37
|
return;
|
|
37
38
|
}
|
|
38
39
|
a.value.push({
|
|
39
40
|
...e,
|
|
40
41
|
closable: e.closable !== !1
|
|
41
42
|
// 기본값 true
|
|
42
|
-
}), u(e.id),
|
|
43
|
+
}), u(e.id), n("tabAdd", e);
|
|
43
44
|
}, o = (e) => {
|
|
44
45
|
if (!Array.isArray(a.value))
|
|
45
46
|
return;
|
|
46
|
-
const
|
|
47
|
-
if (
|
|
48
|
-
if (a.value.splice(
|
|
47
|
+
const t = a.value.findIndex((l) => l.id === e);
|
|
48
|
+
if (t !== -1) {
|
|
49
|
+
if (a.value.splice(t, 1), r.value === e)
|
|
49
50
|
if (a.value.length > 0) {
|
|
50
|
-
const l = Math.min(
|
|
51
|
-
|
|
51
|
+
const l = Math.min(t, a.value.length - 1);
|
|
52
|
+
r.value = a.value[l]?.id || "";
|
|
52
53
|
} else
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
r.value = "";
|
|
55
|
+
n("tabClose", e);
|
|
55
56
|
}
|
|
56
57
|
}, u = (e) => {
|
|
57
58
|
if (!Array.isArray(a.value))
|
|
58
59
|
return;
|
|
59
|
-
a.value.find((l) => l.id === e) && (
|
|
60
|
-
},
|
|
60
|
+
a.value.find((l) => l.id === e) && (r.value = e);
|
|
61
|
+
}, h = (e) => {
|
|
61
62
|
if (Array.isArray(a.value))
|
|
62
|
-
return a.value.find((
|
|
63
|
+
return a.value.find((t) => t.id === e);
|
|
63
64
|
}, g = () => {
|
|
64
65
|
if (!Array.isArray(a.value))
|
|
65
66
|
return;
|
|
66
|
-
a.value.filter((
|
|
67
|
+
a.value.filter((t) => t.closable).forEach((t) => o(t.id));
|
|
67
68
|
}, x = (e) => {
|
|
68
|
-
|
|
69
|
+
r.value = e, n("tabChange", e);
|
|
69
70
|
}, C = (e) => {
|
|
70
71
|
o(e);
|
|
71
72
|
};
|
|
72
|
-
|
|
73
|
-
addTab:
|
|
73
|
+
A({
|
|
74
|
+
addTab: T,
|
|
74
75
|
closeTab: o,
|
|
75
76
|
activateTab: u,
|
|
76
|
-
findTab:
|
|
77
|
+
findTab: h,
|
|
77
78
|
closeAllTabs: g
|
|
78
79
|
});
|
|
79
|
-
const c =
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}), I = n(() => {
|
|
83
|
-
const e = [];
|
|
84
|
-
return t.contentClassName && e.push(t.contentClassName), e.join(" ");
|
|
85
|
-
});
|
|
86
|
-
return (e, s) => (d(), b("div", {
|
|
87
|
-
class: y(N.value)
|
|
80
|
+
const c = i(() => Array.isArray(a.value) ? a.value : []), _ = i(() => c.value.length > 0), I = i(() => R("w-full h-full", s.class)), k = i(() => s.contentClass || "");
|
|
81
|
+
return (e, t) => (d(), y("div", {
|
|
82
|
+
class: m(I.value)
|
|
88
83
|
}, [
|
|
89
|
-
_.value ? (d(),
|
|
84
|
+
_.value ? (d(), M(N, {
|
|
90
85
|
key: 0,
|
|
91
86
|
tabs: c.value,
|
|
92
|
-
"active-tab-id":
|
|
93
|
-
styletype:
|
|
94
|
-
class:
|
|
87
|
+
"active-tab-id": r.value,
|
|
88
|
+
styletype: s.styletype,
|
|
89
|
+
class: m(k.value),
|
|
95
90
|
onTabChange: x,
|
|
96
91
|
onTabClose: C
|
|
97
|
-
},
|
|
98
|
-
|
|
92
|
+
}, S({ _: 2 }, [
|
|
93
|
+
z(c.value, (l) => ({
|
|
99
94
|
name: `content-${l.id}`,
|
|
100
|
-
fn:
|
|
101
|
-
|
|
95
|
+
fn: D(($) => [
|
|
96
|
+
J(e.$slots, `content-${l.id}`, P(j($)))
|
|
102
97
|
])
|
|
103
98
|
}))
|
|
104
|
-
]), 1032, ["tabs", "active-tab-id", "styletype", "class"])) : (d(),
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
99
|
+
]), 1032, ["tabs", "active-tab-id", "styletype", "class"])) : (d(), y("div", V, [
|
|
100
|
+
v("div", q, [
|
|
101
|
+
v("p", w, L(f.emptyMessage), 1),
|
|
102
|
+
t[0] || (t[0] = v("p", { class: "text-muted-foreground/60 text-sm mt-2" }, " 메뉴를 클릭하여 탭을 추가하세요. ", -1))
|
|
108
103
|
])
|
|
109
104
|
]))
|
|
110
105
|
], 2));
|
|
111
106
|
}
|
|
112
107
|
});
|
|
113
108
|
export {
|
|
114
|
-
|
|
109
|
+
K as default
|
|
115
110
|
};
|
|
116
111
|
//# sourceMappingURL=JDynamicTabs.vue.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JDynamicTabs.vue.js","sources":["../../../../src/components/organisms/JDynamicTabs.vue"],"sourcesContent":["<script setup lang=\"ts\">\r\nimport { ref, computed, watch } from 'vue'\r\nimport JTabs from '@/components/molecules/JTabs.vue'\r\nimport type { \r\n JDynamicTabsProps, \r\n JDynamicTabsEmits, \r\n JDynamicTabsMethods,\r\n DynamicTab \r\n} from '@/types/dynamic-tabs.types'\r\n\r\n/**\r\n * JDynamicTabs - 동적 탭 컴포넌트 (organisms)\r\n * Dynamic Tabs Component\r\n * \r\n * @description\r\n * 사이드 메뉴 클릭으로 탭을 동적으로 추가/제거할 수 있는 탭 컴포넌트입니다.\r\n * \r\n * Features:\r\n * - 탭 동적 추가/제거\r\n * - 중복 탭 방지 (이미 있으면 활성화)\r\n * - 닫기 버튼으로 탭 제거\r\n * - 활성 탭 자동 재설정\r\n * - 최대 탭 개수 제한\r\n * \r\n * @example\r\n * const tabsRef = ref()\r\n * \r\n * const handleMenuClick = (menuItem) => {\r\n * tabsRef.value?.addTab({\r\n * id: menuItem.id,\r\n * label: menuItem.label,\r\n * component: menuItem.component,\r\n * closable: true\r\n * })\r\n * }\r\n */\r\n\r\nconst props = withDefaults(defineProps<JDynamicTabsProps>(), {\r\n initialTabs: () => [],\r\n maxTabs: 0, // 0 = unlimited\r\n emptyMessage: '탭을 추가해주세요.',\r\n styletype: 'default',\r\n})\r\n\r\nconst emit = defineEmits<JDynamicTabsEmits>()\r\n\r\n/**\r\n * 탭 목록 (내부 상태)\r\n * Tabs list (internal state)\r\n */\r\nconst tabs = ref<DynamicTab[]>(Array.isArray(props.initialTabs) ? [...props.initialTabs] : [])\r\n\r\n/**\r\n * 현재 활성화된 탭 ID (내부 상태)\r\n * Current active tab ID (internal state)\r\n */\r\nconst activeTabId = ref<string>(\r\n props.defaultActiveId || (Array.isArray(props.initialTabs) && props.initialTabs.length > 0 ? props.initialTabs[0]?.id : '') || ''\r\n)\r\n\r\n/**\r\n * initialTabs가 변경되면 내부 상태 업데이트\r\n * Update internal state when initialTabs changes\r\n */\r\nwatch(() => props.initialTabs, (newTabs) => {\r\n if (!newTabs) {\r\n tabs.value = []\r\n return\r\n }\r\n \r\n if (Array.isArray(newTabs) && newTabs.length > 0) {\r\n tabs.value = [...newTabs]\r\n if (!activeTabId.value && newTabs[0]) {\r\n activeTabId.value = newTabs[0].id\r\n }\r\n } else if (Array.isArray(newTabs)) {\r\n // 빈 배열인 경우\r\n tabs.value = []\r\n } else {\r\n // 배열이 아닌 경우\r\n tabs.value = []\r\n }\r\n}, { immediate: true })\r\n\r\n/**\r\n * 탭 추가\r\n * Add tab (if exists, activate it; otherwise, add to array and activate)\r\n * \r\n * @param tab - 추가할 탭 정보\r\n */\r\nconst addTab = (tab: DynamicTab) => {\r\n // tabs.value가 배열이 아니면 초기화\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 === tab.id)\r\n \r\n if (existingTab) {\r\n // 이미 있으면 해당 탭 활성화\r\n activateTab(tab.id)\r\n return\r\n }\r\n \r\n // 최대 탭 개수 체크\r\n if (props.maxTabs > 0 && tabs.value.length >= props.maxTabs) {\r\n console.warn(`최대 ${props.maxTabs}개의 탭만 열 수 있습니다.`)\r\n return\r\n }\r\n \r\n // 새 탭 추가\r\n tabs.value.push({\r\n ...tab,\r\n closable: tab.closable !== false, // 기본값 true\r\n })\r\n \r\n // 새로 추가된 탭 활성화\r\n activateTab(tab.id)\r\n \r\n // 이벤트 발생\r\n emit('tabAdd', tab)\r\n}\r\n\r\n/**\r\n * 탭 닫기\r\n * Close tab (remove from array and reset active tab)\r\n * \r\n * @param id - 닫을 탭 ID\r\n */\r\nconst closeTab = (id: string) => {\r\n if (!Array.isArray(tabs.value)) {\r\n return\r\n }\r\n \r\n const tabIndex = tabs.value.findIndex(t => t.id === id)\r\n \r\n if (tabIndex === -1) return\r\n \r\n // 탭 제거\r\n tabs.value.splice(tabIndex, 1)\r\n \r\n // 활성 탭 재설정\r\n if (activeTabId.value === id) {\r\n if (tabs.value.length > 0) {\r\n // 이전 탭이 있으면 이전 탭 활성화, 없으면 다음 탭 활성화\r\n const newIndex = Math.min(tabIndex, tabs.value.length - 1)\r\n activeTabId.value = tabs.value[newIndex]?.id || ''\r\n } else {\r\n activeTabId.value = ''\r\n }\r\n }\r\n \r\n // 이벤트 발생\r\n emit('tabClose', id)\r\n}\r\n\r\n/**\r\n * 탭 활성화\r\n * Activate tab\r\n * \r\n * @param id - 활성화할 탭 ID\r\n */\r\nconst activateTab = (id: string) => {\r\n if (!Array.isArray(tabs.value)) {\r\n return\r\n }\r\n const tab = tabs.value.find(t => t.id === id)\r\n if (tab) {\r\n activeTabId.value = id\r\n }\r\n}\r\n\r\n/**\r\n * 특정 탭 찾기\r\n * Find specific tab\r\n * \r\n * @param id - 찾을 탭 ID\r\n */\r\nconst findTab = (id: string): DynamicTab | undefined => {\r\n if (!Array.isArray(tabs.value)) {\r\n return undefined\r\n }\r\n return tabs.value.find(t => t.id === id)\r\n}\r\n\r\n/**\r\n * 모든 탭 닫기 (closable이 true인 탭만)\r\n * Close all tabs (only closable tabs)\r\n */\r\nconst closeAllTabs = () => {\r\n if (!Array.isArray(tabs.value)) {\r\n return\r\n }\r\n const closableTabs = tabs.value.filter(t => t.closable)\r\n closableTabs.forEach(tab => closeTab(tab.id))\r\n}\r\n\r\n/**\r\n * 탭 변경 핸들러\r\n * Tab change handler\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 * 탭 닫기 핸들러\r\n * Tab close handler\r\n */\r\nconst handleTabClose = (id: string) => {\r\n closeTab(id)\r\n}\r\n\r\n/**\r\n * 외부에서 호출 가능한 메서드 노출\r\n * Expose methods for external use\r\n */\r\ndefineExpose<JDynamicTabsMethods>({\r\n addTab,\r\n closeTab,\r\n activateTab,\r\n findTab,\r\n closeAllTabs,\r\n})\r\n\r\n/**\r\n * 안전한 tabs 배열\r\n * Safe tabs array\r\n */\r\nconst safeTabs = computed(() => {\r\n return Array.isArray(tabs.value) ? tabs.value : []\r\n})\r\n\r\n/**\r\n * 탭이 있는지 여부\r\n * Whether there are tabs\r\n */\r\nconst hasTabs = computed(() => safeTabs.value.length > 0)\r\n\r\n/**\r\n * 루트 클래스\r\n * Root classes\r\n */\r\nconst rootClasses = computed(() => {\r\n const classes = ['w-full', 'h-full']\r\n \r\n if (props.className) {\r\n classes.push(props.className)\r\n }\r\n \r\n return classes.join(' ')\r\n})\r\n\r\n/**\r\n * 콘텐츠 클래스\r\n * Content classes\r\n */\r\nconst contentClasses = computed(() => {\r\n const classes = []\r\n \r\n if (props.contentClassName) {\r\n classes.push(props.contentClassName)\r\n }\r\n \r\n return classes.join(' ')\r\n})\r\n</script>\r\n\r\n<template>\r\n <div :class=\"rootClasses\">\r\n <!-- 탭이 있을 경우 / When there are tabs -->\r\n <JTabs\r\n v-if=\"hasTabs\"\r\n :tabs=\"safeTabs\"\r\n :active-tab-id=\"activeTabId\"\r\n :styletype=\"props.styletype\"\r\n :class=\"contentClasses\"\r\n @tab-change=\"handleTabChange\"\r\n @tab-close=\"handleTabClose\"\r\n >\r\n <!-- 각 탭 콘텐츠 슬롯 전달 / Forward slots for each tab content -->\r\n <template\r\n v-for=\"tab in safeTabs\"\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>\r\n </JTabs>\r\n \r\n <!-- 탭이 없을 경우 / When there are no tabs -->\r\n <div\r\n v-else\r\n class=\"flex items-center justify-center h-full w-full\"\r\n >\r\n <div class=\"text-center\">\r\n <p class=\"text-muted-foreground text-lg\">{{ emptyMessage }}</p>\r\n <p class=\"text-muted-foreground/60 text-sm mt-2\">\r\n 메뉴를 클릭하여 탭을 추가하세요.\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n"],"names":["props","__props","emit","__emit","tabs","ref","activeTabId","watch","newTabs","addTab","tab","t","activateTab","closeTab","id","tabIndex","newIndex","findTab","closeAllTabs","handleTabChange","handleTabClose","__expose","safeTabs","computed","hasTabs","rootClasses","classes","contentClasses","_createElementBlock","_createBlock","JTabs","_renderList","_withCtx","slotProps","_renderSlot","_ctx","_openBlock","_hoisted_1","_createElementVNode","_hoisted_2","_hoisted_3","_toDisplayString","_cache"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"JDynamicTabs.vue.js","sources":["../../../../src/components/organisms/JDynamicTabs.vue"],"sourcesContent":["<script setup lang=\"ts\">\r\nimport { ref, computed, watch } from 'vue'\r\nimport JTabs from '@/components/molecules/JTabs.vue'\r\nimport { cn } from '@/lib/utils'\r\nimport type {\r\n JDynamicTabsProps, \r\n JDynamicTabsEmits, \r\n JDynamicTabsMethods,\r\n DynamicTab \r\n} from '@/types/dynamic-tabs.types'\r\n\r\n/**\r\n * JDynamicTabs - 동적 탭 컴포넌트 (organisms)\r\n * Dynamic Tabs Component\r\n * \r\n * @description\r\n * 사이드 메뉴 클릭으로 탭을 동적으로 추가/제거할 수 있는 탭 컴포넌트입니다.\r\n * \r\n * Features:\r\n * - 탭 동적 추가/제거\r\n * - 중복 탭 방지 (이미 있으면 활성화)\r\n * - 닫기 버튼으로 탭 제거\r\n * - 활성 탭 자동 재설정\r\n * - 최대 탭 개수 제한\r\n * \r\n * @example\r\n * const tabsRef = ref()\r\n * \r\n * const handleMenuClick = (menuItem) => {\r\n * tabsRef.value?.addTab({\r\n * id: menuItem.id,\r\n * label: menuItem.label,\r\n * component: menuItem.component,\r\n * closable: true\r\n * })\r\n * }\r\n */\r\n\r\nconst props = withDefaults(defineProps<JDynamicTabsProps>(), {\r\n initialTabs: () => [],\r\n maxTabs: 0, // 0 = unlimited\r\n emptyMessage: '탭을 추가해주세요.',\r\n styletype: 'default',\r\n})\r\n\r\nconst emit = defineEmits<JDynamicTabsEmits>()\r\n\r\n/**\r\n * 탭 목록 (내부 상태)\r\n * Tabs list (internal state)\r\n */\r\nconst tabs = ref<DynamicTab[]>(Array.isArray(props.initialTabs) ? [...props.initialTabs] : [])\r\n\r\n/**\r\n * 현재 활성화된 탭 ID (내부 상태)\r\n * Current active tab ID (internal state)\r\n */\r\nconst activeTabId = ref<string>(\r\n props.defaultActiveId || (Array.isArray(props.initialTabs) && props.initialTabs.length > 0 ? props.initialTabs[0]?.id : '') || ''\r\n)\r\n\r\n/**\r\n * initialTabs가 변경되면 내부 상태 업데이트\r\n * Update internal state when initialTabs changes\r\n */\r\nwatch(() => props.initialTabs, (newTabs) => {\r\n if (!newTabs) {\r\n tabs.value = []\r\n return\r\n }\r\n \r\n if (Array.isArray(newTabs) && newTabs.length > 0) {\r\n tabs.value = [...newTabs]\r\n if (!activeTabId.value && newTabs[0]) {\r\n activeTabId.value = newTabs[0].id\r\n }\r\n } else if (Array.isArray(newTabs)) {\r\n // 빈 배열인 경우\r\n tabs.value = []\r\n } else {\r\n // 배열이 아닌 경우\r\n tabs.value = []\r\n }\r\n}, { immediate: true })\r\n\r\n/**\r\n * 탭 추가\r\n * Add tab (if exists, activate it; otherwise, add to array and activate)\r\n * \r\n * @param tab - 추가할 탭 정보\r\n */\r\nconst addTab = (tab: DynamicTab) => {\r\n // tabs.value가 배열이 아니면 초기화\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 === tab.id)\r\n \r\n if (existingTab) {\r\n // 이미 있으면 해당 탭 활성화\r\n activateTab(tab.id)\r\n return\r\n }\r\n \r\n // 최대 탭 개수 체크\r\n if (props.maxTabs > 0 && tabs.value.length >= props.maxTabs) {\r\n console.warn(`최대 ${props.maxTabs}개의 탭만 열 수 있습니다.`)\r\n return\r\n }\r\n \r\n // 새 탭 추가\r\n tabs.value.push({\r\n ...tab,\r\n closable: tab.closable !== false, // 기본값 true\r\n })\r\n \r\n // 새로 추가된 탭 활성화\r\n activateTab(tab.id)\r\n \r\n // 이벤트 발생\r\n emit('tabAdd', tab)\r\n}\r\n\r\n/**\r\n * 탭 닫기\r\n * Close tab (remove from array and reset active tab)\r\n * \r\n * @param id - 닫을 탭 ID\r\n */\r\nconst closeTab = (id: string) => {\r\n if (!Array.isArray(tabs.value)) {\r\n return\r\n }\r\n \r\n const tabIndex = tabs.value.findIndex(t => t.id === id)\r\n \r\n if (tabIndex === -1) return\r\n \r\n // 탭 제거\r\n tabs.value.splice(tabIndex, 1)\r\n \r\n // 활성 탭 재설정\r\n if (activeTabId.value === id) {\r\n if (tabs.value.length > 0) {\r\n // 이전 탭이 있으면 이전 탭 활성화, 없으면 다음 탭 활성화\r\n const newIndex = Math.min(tabIndex, tabs.value.length - 1)\r\n activeTabId.value = tabs.value[newIndex]?.id || ''\r\n } else {\r\n activeTabId.value = ''\r\n }\r\n }\r\n \r\n // 이벤트 발생\r\n emit('tabClose', id)\r\n}\r\n\r\n/**\r\n * 탭 활성화\r\n * Activate tab\r\n * \r\n * @param id - 활성화할 탭 ID\r\n */\r\nconst activateTab = (id: string) => {\r\n if (!Array.isArray(tabs.value)) {\r\n return\r\n }\r\n const tab = tabs.value.find(t => t.id === id)\r\n if (tab) {\r\n activeTabId.value = id\r\n }\r\n}\r\n\r\n/**\r\n * 특정 탭 찾기\r\n * Find specific tab\r\n * \r\n * @param id - 찾을 탭 ID\r\n */\r\nconst findTab = (id: string): DynamicTab | undefined => {\r\n if (!Array.isArray(tabs.value)) {\r\n return undefined\r\n }\r\n return tabs.value.find(t => t.id === id)\r\n}\r\n\r\n/**\r\n * 모든 탭 닫기 (closable이 true인 탭만)\r\n * Close all tabs (only closable tabs)\r\n */\r\nconst closeAllTabs = () => {\r\n if (!Array.isArray(tabs.value)) {\r\n return\r\n }\r\n const closableTabs = tabs.value.filter(t => t.closable)\r\n closableTabs.forEach(tab => closeTab(tab.id))\r\n}\r\n\r\n/**\r\n * 탭 변경 핸들러\r\n * Tab change handler\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 * 탭 닫기 핸들러\r\n * Tab close handler\r\n */\r\nconst handleTabClose = (id: string) => {\r\n closeTab(id)\r\n}\r\n\r\n/**\r\n * 외부에서 호출 가능한 메서드 노출\r\n * Expose methods for external use\r\n */\r\ndefineExpose<JDynamicTabsMethods>({\r\n addTab,\r\n closeTab,\r\n activateTab,\r\n findTab,\r\n closeAllTabs,\r\n})\r\n\r\n/**\r\n * 안전한 tabs 배열\r\n * Safe tabs array\r\n */\r\nconst safeTabs = computed(() => {\r\n return Array.isArray(tabs.value) ? tabs.value : []\r\n})\r\n\r\n/**\r\n * 탭이 있는지 여부\r\n * Whether there are tabs\r\n */\r\nconst hasTabs = computed(() => safeTabs.value.length > 0)\r\n\r\n/**\r\n * 루트 클래스\r\n * Root classes\r\n */\r\nconst rootClasses = computed(() => {\r\n return cn('w-full h-full', props.class)\r\n})\r\n\r\n/**\r\n * 콘텐츠 클래스\r\n * Content classes\r\n */\r\nconst contentClasses = computed(() => {\r\n return props.contentClass || ''\r\n})\r\n</script>\r\n\r\n<template>\r\n <div :class=\"rootClasses\">\r\n <!-- 탭이 있을 경우 / When there are tabs -->\r\n <JTabs\r\n v-if=\"hasTabs\"\r\n :tabs=\"safeTabs\"\r\n :active-tab-id=\"activeTabId\"\r\n :styletype=\"props.styletype\"\r\n :class=\"contentClasses\"\r\n @tab-change=\"handleTabChange\"\r\n @tab-close=\"handleTabClose\"\r\n >\r\n <!-- 각 탭 콘텐츠 슬롯 전달 / Forward slots for each tab content -->\r\n <template\r\n v-for=\"tab in safeTabs\"\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>\r\n </JTabs>\r\n \r\n <!-- 탭이 없을 경우 / When there are no tabs -->\r\n <div\r\n v-else\r\n class=\"flex items-center justify-center h-full w-full\"\r\n >\r\n <div class=\"text-center\">\r\n <p class=\"text-muted-foreground text-lg\">{{ emptyMessage }}</p>\r\n <p class=\"text-muted-foreground/60 text-sm mt-2\">\r\n 메뉴를 클릭하여 탭을 추가하세요.\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n"],"names":["props","__props","emit","__emit","tabs","ref","activeTabId","watch","newTabs","addTab","tab","t","activateTab","closeTab","id","tabIndex","newIndex","findTab","closeAllTabs","handleTabChange","handleTabClose","__expose","safeTabs","computed","hasTabs","rootClasses","cn","contentClasses","_createElementBlock","_createBlock","JTabs","_renderList","_withCtx","slotProps","_renderSlot","_ctx","_openBlock","_hoisted_1","_createElementVNode","_hoisted_2","_hoisted_3","_toDisplayString","_cache"],"mappings":"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"}
|
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("./JFilterBar.vue2.cjs");;/* empty css */const t = (t_comp, t_opts) => {
|
|
2
|
+
const t_merged = t_comp.__vccOpts || t_comp;
|
|
3
|
+
for (const [t_key, t_val] of t_opts)
|
|
4
|
+
t_merged[t_key] = t_val;
|
|
5
|
+
return t_merged;
|
|
6
|
+
};,r=t(e.default,[["__scopeId","data-v-88a88c37"]]);exports.default=r;
|
|
2
7
|
//# sourceMappingURL=JFilterBar.vue.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JFilterBar.vue.cjs","sources":[
|
|
1
|
+
{"version":3,"file":"JFilterBar.vue.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
@@ -1,140 +1,13 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
__name: "JFilterBar",
|
|
11
|
-
props: {
|
|
12
|
-
title: {},
|
|
13
|
-
collapsed: { type: Boolean, default: !1 },
|
|
14
|
-
collapsible: { type: Boolean, default: !0 },
|
|
15
|
-
filterValues: { default: () => ({}) },
|
|
16
|
-
filterDisplay: { default: () => ({}) },
|
|
17
|
-
showResetButton: { type: Boolean, default: !1 },
|
|
18
|
-
showSearchButton: { type: Boolean, default: !1 },
|
|
19
|
-
resetButtonText: { default: "초기화" },
|
|
20
|
-
searchButtonText: { default: "조회" }
|
|
21
|
-
},
|
|
22
|
-
emits: ["update:collapsed", "update:filterValues", "search", "reset"],
|
|
23
|
-
setup(l, { emit: w }) {
|
|
24
|
-
const r = l, a = w, m = g(() => r.collapsible ? !r.collapsed : !0);
|
|
25
|
-
function B(e) {
|
|
26
|
-
return !!(e == null || typeof e == "string" && e.trim() === "" || Array.isArray(e) && e.length === 0);
|
|
27
|
-
}
|
|
28
|
-
const y = g(() => {
|
|
29
|
-
const e = [];
|
|
30
|
-
for (const [t, s] of Object.entries(r.filterDisplay)) {
|
|
31
|
-
const c = r.filterValues[t];
|
|
32
|
-
if (B(c)) continue;
|
|
33
|
-
const h = s.displayValue ? s.displayValue(c) : String(c);
|
|
34
|
-
h.trim() !== "" && e.push({
|
|
35
|
-
key: t,
|
|
36
|
-
label: s.label,
|
|
37
|
-
value: h
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
return e;
|
|
41
|
-
});
|
|
42
|
-
function _() {
|
|
43
|
-
a("update:collapsed", !r.collapsed);
|
|
44
|
-
}
|
|
45
|
-
function C() {
|
|
46
|
-
const e = {};
|
|
47
|
-
for (const t of Object.keys(r.filterValues)) {
|
|
48
|
-
const s = r.filterValues[t];
|
|
49
|
-
typeof s == "string" ? e[t] = "" : Array.isArray(s) ? e[t] = [] : e[t] = null;
|
|
50
|
-
}
|
|
51
|
-
a("update:filterValues", e), a("reset");
|
|
52
|
-
}
|
|
53
|
-
function S() {
|
|
54
|
-
a("search");
|
|
55
|
-
}
|
|
56
|
-
function A(e) {
|
|
57
|
-
const t = { ...r.filterValues }, s = t[e];
|
|
58
|
-
typeof s == "string" ? t[e] = "" : Array.isArray(s) ? t[e] = [] : t[e] = null, a("update:filterValues", t);
|
|
59
|
-
}
|
|
60
|
-
return (e, t) => (o(), u("div", L, [
|
|
61
|
-
n("div", M, [
|
|
62
|
-
n("div", X, [
|
|
63
|
-
l.collapsible ? (o(), u("button", {
|
|
64
|
-
key: 0,
|
|
65
|
-
type: "button",
|
|
66
|
-
class: "flex items-center justify-center h-7 w-7 rounded hover:bg-accent hover:text-accent-foreground transition-colors",
|
|
67
|
-
onClick: _
|
|
68
|
-
}, [
|
|
69
|
-
x(v(N), {
|
|
70
|
-
class: T([
|
|
71
|
-
"h-4 w-4 transition-transform",
|
|
72
|
-
m.value ? "rotate-0" : "-rotate-90"
|
|
73
|
-
])
|
|
74
|
-
}, null, 8, ["class"])
|
|
75
|
-
])) : i("", !0),
|
|
76
|
-
l.title ? (o(), f(J, {
|
|
77
|
-
key: 1,
|
|
78
|
-
text: l.title,
|
|
79
|
-
class: "text-sm font-semibold text-foreground"
|
|
80
|
-
}, null, 8, ["text"])) : i("", !0),
|
|
81
|
-
y.value.length > 0 ? (o(), u("div", q, [
|
|
82
|
-
(o(!0), u(j, null, z(y.value, (s) => (o(), f(O, {
|
|
83
|
-
key: s.key,
|
|
84
|
-
variant: "secondary",
|
|
85
|
-
size: "sm",
|
|
86
|
-
class: "flex items-center gap-1 cursor-default"
|
|
87
|
-
}, {
|
|
88
|
-
default: p(() => [
|
|
89
|
-
n("span", G, d(s.label) + ":", 1),
|
|
90
|
-
n("span", null, d(s.value), 1),
|
|
91
|
-
n("button", {
|
|
92
|
-
type: "button",
|
|
93
|
-
class: "ml-0.5 rounded-full hover:bg-gray-300 p-0.5 transition-colors",
|
|
94
|
-
onClick: E((c) => A(s.key), ["stop"])
|
|
95
|
-
}, [
|
|
96
|
-
x(v(R), { class: "h-3 w-3" })
|
|
97
|
-
], 8, H)
|
|
98
|
-
]),
|
|
99
|
-
_: 2
|
|
100
|
-
}, 1024))), 128))
|
|
101
|
-
])) : i("", !0)
|
|
102
|
-
]),
|
|
103
|
-
n("div", I, [
|
|
104
|
-
b(e.$slots, "actions"),
|
|
105
|
-
l.showResetButton ? (o(), f(k, {
|
|
106
|
-
key: 0,
|
|
107
|
-
variant: "secondary",
|
|
108
|
-
size: "sm",
|
|
109
|
-
onClick: C
|
|
110
|
-
}, {
|
|
111
|
-
default: p(() => [
|
|
112
|
-
V(d(l.resetButtonText), 1)
|
|
113
|
-
]),
|
|
114
|
-
_: 1
|
|
115
|
-
})) : i("", !0),
|
|
116
|
-
l.showSearchButton ? (o(), f(k, {
|
|
117
|
-
key: 1,
|
|
118
|
-
styletype: "primary",
|
|
119
|
-
size: "sm",
|
|
120
|
-
onClick: S
|
|
121
|
-
}, {
|
|
122
|
-
default: p(() => [
|
|
123
|
-
V(d(l.searchButtonText), 1)
|
|
124
|
-
]),
|
|
125
|
-
_: 1
|
|
126
|
-
})) : i("", !0)
|
|
127
|
-
])
|
|
128
|
-
]),
|
|
129
|
-
D(n("div", K, [
|
|
130
|
-
b(e.$slots, "filters")
|
|
131
|
-
], 512), [
|
|
132
|
-
[F, m.value]
|
|
133
|
-
])
|
|
134
|
-
]));
|
|
135
|
-
}
|
|
136
|
-
});
|
|
1
|
+
import o from "./JFilterBar.vue2.js";
|
|
2
|
+
/* empty css */
|
|
3
|
+
const r = (r_comp, r_opts) => {
|
|
4
|
+
const r_merged = r_comp.__vccOpts || r_comp;
|
|
5
|
+
for (const [r_key, r_val] of r_opts)
|
|
6
|
+
r_merged[r_key] = r_val;
|
|
7
|
+
return r_merged;
|
|
8
|
+
};
|
|
9
|
+
const p = /* @__PURE__ */ r(o, [["__scopeId", "data-v-88a88c37"]]);
|
|
137
10
|
export {
|
|
138
|
-
|
|
11
|
+
p as default
|
|
139
12
|
};
|
|
140
13
|
//# sourceMappingURL=JFilterBar.vue.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JFilterBar.vue.js","sources":[
|
|
1
|
+
{"version":3,"file":"JFilterBar.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue"),d=require("lucide-vue-next"),B=require("../atoms/JBadge.vue.cjs"),f=require("../atoms/JButton.vue.cjs"),k=require("../atoms/JLabel.vue.cjs"),v=require("../../lib/utils.cjs"),x={class:"flex items-center justify-between px-3 py-1.5"},b={class:"flex items-center gap-2"},_={key:2,class:"flex items-center gap-1 flex-wrap"},w={class:"text-muted-foreground"},C=["onClick"],N={class:"flex items-center gap-2"},E={class:"px-3 pb-3"},S=e.defineComponent({__name:"JFilterBar",props:{class:{},title:{},collapsed:{type:Boolean,default:!0},collapsible:{type:Boolean,default:!0},filterValues:{default:()=>({})},filterDisplay:{default:()=>({})},showResetButton:{type:Boolean,default:!1},showSearchButton:{type:Boolean,default:!1},resetButtonText:{default:"초기화"},searchButtonText:{default:"조회"}},emits:["update:collapsed","update:filterValues","search","reset"],setup(s,{emit:p}){const o=s,n=p,c=e.computed(()=>o.collapsible?!o.collapsed:!0);function m(t){return!!(t==null||typeof t=="string"&&t.trim()===""||Array.isArray(t)&&t.length===0)}const i=e.computed(()=>{const t=[];for(const[l,r]of Object.entries(o.filterDisplay)){const a=o.filterValues[l];if(m(a))continue;const u=r.displayValue?r.displayValue(a):String(a);u.trim()!==""&&t.push({key:l,label:r.label,value:u})}return t});function y(){n("update:collapsed",!o.collapsed)}function h(){const t={};for(const l of Object.keys(o.filterValues)){const r=o.filterValues[l];typeof r=="string"?t[l]="":Array.isArray(r)?t[l]=[]:t[l]=null}n("update:filterValues",t),n("reset")}function V(){n("search")}function g(t){const l={...o.filterValues},r=l[t];typeof r=="string"?l[t]="":Array.isArray(r)?l[t]=[]:l[t]=null,n("update:filterValues",l)}return(t,l)=>(e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(e.unref(v.cn)("j-filter-bar w-full rounded-sm border bg-card text-card-foreground",o.class))},[e.createElementVNode("div",x,[e.createElementVNode("div",b,[s.collapsible?(e.openBlock(),e.createElementBlock("button",{key:0,type:"button",class:"flex items-center justify-center h-6 w-6 rounded hover:bg-accent hover:text-accent-foreground transition-colors",onClick:y},[e.createVNode(e.unref(d.ChevronDown),{class:e.normalizeClass(["h-3.5 w-3.5 transition-transform",c.value?"rotate-0":"-rotate-90"])},null,8,["class"])])):e.createCommentVNode("",!0),s.title?(e.openBlock(),e.createBlock(k.default,{key:1,text:s.title,class:"text-sm font-semibold text-foreground"},null,8,["text"])):e.createCommentVNode("",!0),i.value.length>0?(e.openBlock(),e.createElementBlock("div",_,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(i.value,r=>(e.openBlock(),e.createBlock(B.default,{key:r.key,variant:"secondary",size:"sm",class:"flex items-center gap-1 cursor-default"},{default:e.withCtx(()=>[e.createElementVNode("span",w,e.toDisplayString(r.label)+":",1),e.createElementVNode("span",null,e.toDisplayString(r.value),1),e.createElementVNode("button",{type:"button",class:"ml-0.5 rounded-full hover:bg-gray-300 p-0.5 transition-colors",onClick:e.withModifiers(a=>g(r.key),["stop"])},[e.createVNode(e.unref(d.X),{class:"h-3 w-3"})],8,C)]),_:2},1024))),128))])):e.createCommentVNode("",!0)]),e.createElementVNode("div",N,[e.renderSlot(t.$slots,"actions",{},void 0,!0),s.showResetButton?(e.openBlock(),e.createBlock(f.default,{key:0,variant:"secondary",size:"sm",onClick:h},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(s.resetButtonText),1)]),_:1})):e.createCommentVNode("",!0),s.showSearchButton?(e.openBlock(),e.createBlock(f.default,{key:1,styletype:"primary",size:"sm",onClick:V},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(s.searchButtonText),1)]),_:1})):e.createCommentVNode("",!0)])]),e.withDirectives(e.createElementVNode("div",E,[e.renderSlot(t.$slots,"filters",{},void 0,!0)],512),[[e.vShow,c.value]])],2))}});exports.default=S;
|
|
2
2
|
//# sourceMappingURL=JFilterBar.vue2.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JFilterBar.vue2.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"JFilterBar.vue2.cjs","sources":["../../../../src/components/organisms/JFilterBar.vue"],"sourcesContent":["<template>\r\n <div :class=\"cn('j-filter-bar w-full rounded-sm border bg-card text-card-foreground', props.class)\">\r\n <!-- Row 1: toolbar -->\r\n <div class=\"flex items-center justify-between px-3 py-1.5\">\r\n <div class=\"flex items-center gap-2\">\r\n <button\r\n v-if=\"collapsible\"\r\n type=\"button\"\r\n class=\"flex items-center justify-center h-6 w-6 rounded hover:bg-accent hover:text-accent-foreground transition-colors\"\r\n @click=\"toggleCollapsed\"\r\n >\r\n <ChevronDown\r\n :class=\"[\r\n 'h-3.5 w-3.5 transition-transform',\r\n isExpanded ? 'rotate-0' : '-rotate-90',\r\n ]\"\r\n />\r\n </button>\r\n <!-- 타이틀 -->\r\n <JLabel\r\n v-if=\"title\"\r\n :text=\"title\"\r\n class=\"text-sm font-semibold text-foreground\"\r\n />\r\n <!-- 선택된 필터 뱃지 표시 -->\r\n <div v-if=\"activeFilters.length > 0\" class=\"flex items-center gap-1 flex-wrap\">\r\n <JBadge\r\n v-for=\"filter in activeFilters\"\r\n :key=\"filter.key\"\r\n variant=\"secondary\"\r\n size=\"sm\"\r\n class=\"flex items-center gap-1 cursor-default\"\r\n >\r\n <span class=\"text-muted-foreground\">{{ filter.label }}:</span>\r\n <span>{{ filter.value }}</span>\r\n <button\r\n type=\"button\"\r\n class=\"ml-0.5 rounded-full hover:bg-gray-300 p-0.5 transition-colors\"\r\n @click.stop=\"removeFilter(filter.key)\"\r\n >\r\n <X class=\"h-3 w-3\" />\r\n </button>\r\n </JBadge>\r\n </div>\r\n </div>\r\n <div class=\"flex items-center gap-2\">\r\n <slot name=\"actions\" />\r\n <JButton\r\n v-if=\"showResetButton\"\r\n variant=\"secondary\"\r\n size=\"sm\"\r\n @click=\"handleReset\"\r\n >\r\n {{ resetButtonText }}\r\n </JButton>\r\n <JButton\r\n v-if=\"showSearchButton\"\r\n styletype=\"primary\"\r\n size=\"sm\"\r\n @click=\"handleSearch\"\r\n >\r\n {{ searchButtonText }}\r\n </JButton>\r\n </div>\r\n </div>\r\n\r\n <!-- Row 2: filters -->\r\n <div v-show=\"isExpanded\" class=\"px-3 pb-3\">\r\n <slot name=\"filters\" />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed } from 'vue'\r\nimport { ChevronDown, X } from 'lucide-vue-next'\r\nimport JBadge from '@/components/atoms/JBadge.vue'\r\nimport JButton from '@/components/atoms/JButton.vue'\r\nimport JLabel from '@/components/atoms/JLabel.vue'\r\nimport { cn } from '@/lib/utils'\r\n\r\n/** 활성 필터 아이템 타입 */\r\nexport interface ActiveFilterItem {\r\n /** 필터 식별 키 */\r\n key: string\r\n /** 표시할 라벨 (필터명) */\r\n label: string\r\n /** 표시할 값 */\r\n value: string\r\n}\r\n\r\n/** 필터 설정 타입 */\r\nexport interface FilterDisplayItem {\r\n /** 표시할 라벨 */\r\n label: string\r\n /** 값을 표시용 문자열로 변환 (예: combo value -> label) */\r\n displayValue?: (value: unknown) => string\r\n}\r\n\r\nexport interface JFilterBarProps {\r\n /** 추가 클래스 (외부 커스터마이징용) */\r\n class?: string\r\n /** 필터바 타이틀 */\r\n title?: string\r\n /** 필터 접힘 상태 (v-model 지원) */\r\n collapsed?: boolean\r\n /** 접기/펼치기 가능 여부. false면 토글 버튼 숨김 & 필터 항상 표시 */\r\n collapsible?: boolean\r\n /** 필터 값 객체 (v-model:filterValues 지원) */\r\n filterValues?: Record<string, unknown>\r\n /** 필터 표시 설정 (label, displayValue 등) */\r\n filterDisplay?: Record<string, FilterDisplayItem>\r\n /** 초기화 버튼 표시 여부 */\r\n showResetButton?: boolean\r\n /** 조회 버튼 표시 여부 */\r\n showSearchButton?: boolean\r\n /** 초기화 버튼 텍스트 */\r\n resetButtonText?: string\r\n /** 조회 버튼 텍스트 */\r\n searchButtonText?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<JFilterBarProps>(), {\r\n collapsed: true,\r\n collapsible: true,\r\n filterValues: () => ({}),\r\n filterDisplay: () => ({}),\r\n showResetButton: false,\r\n showSearchButton: false,\r\n resetButtonText: '초기화',\r\n searchButtonText: '조회',\r\n})\r\n\r\nconst emit = defineEmits<{\r\n 'update:collapsed': [value: boolean]\r\n 'update:filterValues': [value: Record<string, unknown>]\r\n /** 조회 버튼 클릭 */\r\n search: []\r\n /** 초기화 버튼 클릭 */\r\n reset: []\r\n}>()\r\n\r\nconst isExpanded = computed(() => {\r\n if (!props.collapsible) return true\r\n return !props.collapsed\r\n})\r\n\r\n/** 값이 비어있는지 확인 */\r\nfunction isEmpty(value: unknown): boolean {\r\n if (value === null || value === undefined) return true\r\n if (typeof value === 'string' && value.trim() === '') return true\r\n if (Array.isArray(value) && value.length === 0) return true\r\n return false\r\n}\r\n\r\n/** filterValues + filterDisplay 기반으로 활성 필터 목록 자동 생성 */\r\nconst activeFilters = computed<ActiveFilterItem[]>(() => {\r\n const filters: ActiveFilterItem[] = []\r\n\r\n for (const [key, config] of Object.entries(props.filterDisplay)) {\r\n const value = props.filterValues[key]\r\n if (isEmpty(value)) continue\r\n\r\n const displayValue = config.displayValue ? config.displayValue(value) : String(value)\r\n if (displayValue.trim() === '') continue\r\n\r\n filters.push({\r\n key,\r\n label: config.label,\r\n value: displayValue,\r\n })\r\n }\r\n\r\n return filters\r\n})\r\n\r\nfunction toggleCollapsed() {\r\n emit('update:collapsed', !props.collapsed)\r\n}\r\n\r\nfunction handleReset() {\r\n // filterValues의 모든 값을 초기화\r\n const resetValues: Record<string, unknown> = {}\r\n for (const key of Object.keys(props.filterValues)) {\r\n const currentValue = props.filterValues[key]\r\n if (typeof currentValue === 'string') {\r\n resetValues[key] = ''\r\n } else if (Array.isArray(currentValue)) {\r\n resetValues[key] = []\r\n } else {\r\n resetValues[key] = null\r\n }\r\n }\r\n emit('update:filterValues', resetValues)\r\n emit('reset')\r\n}\r\n\r\nfunction handleSearch() {\r\n emit('search')\r\n}\r\n\r\nfunction removeFilter(key: string) {\r\n // filterValues 업데이트 (해당 키 값을 초기화)\r\n const newValues = { ...props.filterValues }\r\n const currentValue = newValues[key]\r\n\r\n // 타입에 따라 적절한 초기값으로 설정\r\n if (typeof currentValue === 'string') {\r\n newValues[key] = ''\r\n } else if (Array.isArray(currentValue)) {\r\n newValues[key] = []\r\n } else {\r\n newValues[key] = null\r\n }\r\n\r\n emit('update:filterValues', newValues)\r\n}\r\n</script>\r\n\r\n<style scoped>\r\n/* ========================================\r\n 패턴 3: Tabs 아래 배치 시 연결 스타일\r\n ======================================== */\r\n\r\n:deep([data-state=\"active\"]) > .j-filter-bar {\r\n border-top: none;\r\n border-top-left-radius: 0;\r\n border-top-right-radius: 0;\r\n}\r\n\r\n:deep([role=\"tabpanel\"]) .j-filter-bar {\r\n border-top: none;\r\n}\r\n</style>\r\n"],"names":["props","__props","emit","__emit","isExpanded","computed","isEmpty","value","activeFilters","filters","key","config","displayValue","toggleCollapsed","handleReset","resetValues","currentValue","handleSearch","removeFilter","newValues","_createElementBlock","_normalizeClass","_unref","cn","_createElementVNode","_hoisted_1","_hoisted_2","_createVNode","ChevronDown","_createBlock","JLabel","_openBlock","_hoisted_3","_Fragment","_renderList","filter","JBadge","_hoisted_4","_toDisplayString","_withModifiers","$event","X","_hoisted_6","_renderSlot","_ctx","JButton","_withDirectives","_hoisted_7"],"mappings":"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"}
|