@j-solution/components 1.7.0 → 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 -8
- 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 +21 -21
- 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/JGrid.vue.cjs +1 -1
- package/components/atoms/JGrid.vue.js +1 -1
- package/components/atoms/JGrid.vue2.cjs +1 -1
- package/components/atoms/JGrid.vue2.cjs.map +1 -1
- package/components/atoms/JGrid.vue2.js +39 -35
- package/components/atoms/JGrid.vue2.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/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/JSplitter.vue.cjs +6 -1
- package/components/atoms/JSplitter.vue.cjs.map +1 -1
- package/components/atoms/JSplitter.vue.js +10 -59
- 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/examples/ExampleCrudPage.vue.cjs +1 -1
- package/components/examples/ExampleCrudPage.vue.cjs.map +1 -1
- package/components/examples/ExampleCrudPage.vue.js +228 -208
- 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 +341 -368
- 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/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 -264
- 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 +44 -50
- 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 +23 -20
- 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 +191 -190
- 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 +47 -45
- 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/JSidebarSimple/JDynamicMenuItem.vue.cjs +1 -1
- package/components/organisms/JSidebarSimple/JDynamicMenuItem.vue.cjs.map +1 -1
- package/components/organisms/JSidebarSimple/JDynamicMenuItem.vue.js +52 -52
- package/components/organisms/JSidebarSimple/JDynamicMenuItem.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/CardHeader.vue.cjs +1 -1
- package/components/shadcn/CardHeader.vue.cjs.map +1 -1
- package/components/shadcn/CardHeader.vue.js +5 -5
- package/components/shadcn/CardHeader.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 +1 -1
- package/components/shadcn/SelectTrigger.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/Textarea.vue.cjs +1 -1
- package/components/shadcn/Textarea.vue.cjs.map +1 -1
- package/components/shadcn/Textarea.vue.js +1 -1
- 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 +4 -4
- 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 +920 -777
- package/assets/jwms-portal-frontend-CwxPfHfa.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.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: !0 },
|
|
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-6 w-6 rounded hover:bg-accent hover:text-accent-foreground transition-colors",
|
|
67
|
-
onClick: _
|
|
68
|
-
}, [
|
|
69
|
-
x(v(N), {
|
|
70
|
-
class: T([
|
|
71
|
-
"h-3.5 w-3.5 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":"o+BA0HA,MAAMA,EAAQC,EAWRC,EAAOC,EASPC,EAAaC,EAAAA,SAAS,IACrBL,EAAM,YACJ,CAACA,EAAM,UADiB,EAEhC,EAGD,SAASM,EAAQC,EAAyB,CAGxC,MAFI,GAAAA,GAAU,MACV,OAAOA,GAAU,UAAYA,EAAM,KAAA,IAAW,IAC9C,MAAM,QAAQA,CAAK,GAAKA,EAAM,SAAW,EAE/C,CAGA,MAAMC,EAAgBH,EAAAA,SAA6B,IAAM,CACvD,MAAMI,EAA8B,CAAA,EAEpC,SAAW,CAACC,EAAKC,CAAM,IAAK,OAAO,QAAQX,EAAM,aAAa,EAAG,CAC/D,MAAMO,EAAQP,EAAM,aAAaU,CAAG,EACpC,GAAIJ,EAAQC,CAAK,EAAG,SAEpB,MAAMK,EAAeD,EAAO,aAAeA,EAAO,aAAaJ,CAAK,EAAI,OAAOA,CAAK,EAChFK,EAAa,KAAA,IAAW,IAE5BH,EAAQ,KAAK,CACX,IAAAC,EACA,MAAOC,EAAO,MACd,MAAOC,CAAA,CACR,CACH,CAEA,OAAOH,CACT,CAAC,EAED,SAASI,GAAkB,CACzBX,EAAK,mBAAoB,CAACF,EAAM,SAAS,CAC3C,CAEA,SAASc,GAAc,CAErB,MAAMC,EAAuC,CAAA,EAC7C,UAAWL,KAAO,OAAO,KAAKV,EAAM,YAAY,EAAG,CACjD,MAAMgB,EAAehB,EAAM,aAAaU,CAAG,EACvC,OAAOM,GAAiB,SAC1BD,EAAYL,CAAG,EAAI,GACV,MAAM,QAAQM,CAAY,EACnCD,EAAYL,CAAG,EAAI,CAAA,EAEnBK,EAAYL,CAAG,EAAI,IAEvB,CACAR,EAAK,sBAAuBa,CAAW,EACvCb,EAAK,OAAO,CACd,CAEA,SAASe,GAAe,CACtBf,EAAK,QAAQ,CACf,CAEA,SAASgB,EAAaR,EAAa,CAEjC,MAAMS,EAAY,CAAE,GAAGnB,EAAM,YAAA,EACvBgB,EAAeG,EAAUT,CAAG,EAG9B,OAAOM,GAAiB,SAC1BG,EAAUT,CAAG,EAAI,GACR,MAAM,QAAQM,CAAY,EACnCG,EAAUT,CAAG,EAAI,CAAA,EAEjBS,EAAUT,CAAG,EAAI,KAGnBR,EAAK,sBAAuBiB,CAAS,CACvC,6BAvNEC,EAAAA,mBAqEM,MAAA,CArEA,MAAKC,EAAAA,eAAEC,QAAAC,EAAAA,EAAA,EAAE,qEAAuEvB,EAAM,KAAK,CAAA,CAAA,GAE/FwB,EAAAA,mBA6DM,MA7DNC,EA6DM,CA5DJD,EAAAA,mBAwCM,MAxCNE,EAwCM,CAtCIzB,EAAA,2BADRmB,EAAAA,mBAYS,SAAA,OAVP,KAAK,SACL,MAAM,kHACL,QAAOP,CAAA,GAERc,cAKEL,EAAAA,MAAAM,EAAAA,WAAA,EAAA,CAJC,MAAKP,EAAAA,eAAA,oCAAsEjB,EAAA,MAAU,WAAA,YAAA,qDAQlFH,EAAA,qBADR4B,EAAAA,YAIEC,EAAAA,QAAA,OAFC,KAAM7B,EAAA,MACP,MAAM,uCAAA,gDAGGO,EAAA,MAAc,OAAM,GAA/BuB,EAAAA,YAAAX,EAAAA,mBAkBM,MAlBNY,EAkBM,kBAjBJZ,EAAAA,mBAgBSa,EAAAA,SAAA,KAAAC,EAAAA,WAfU1B,EAAA,MAAV2B,kBADTN,EAAAA,YAgBSO,UAAA,CAdN,IAAKD,EAAO,IACb,QAAQ,YACR,KAAK,KACL,MAAM,wCAAA,qBAEN,IAA8D,CAA9DX,qBAA8D,OAA9Da,EAA8DC,EAAAA,gBAAvBH,EAAO,KAAK,EAAG,IAAC,CAAA,EACvDX,EAAAA,mBAA+B,OAAA,KAAAc,EAAAA,gBAAtBH,EAAO,KAAK,EAAA,CAAA,EACrBX,EAAAA,mBAMS,SAAA,CALP,KAAK,SACL,MAAM,gEACL,QAAKe,EAAAA,cAAAC,GAAOtB,EAAaiB,EAAO,GAAG,EAAA,CAAA,MAAA,CAAA,CAAA,GAEpCR,EAAAA,YAAqBL,EAAAA,MAAAmB,EAAAA,CAAA,EAAA,CAAlB,MAAM,UAAS,CAAA,6DAK1BjB,EAAAA,mBAkBM,MAlBNkB,EAkBM,CAjBJC,EAAAA,WAAuBC,EAAA,OAAA,UAAA,CAAA,EAAA,OAAA,EAAA,EAEf3C,EAAA,+BADR4B,EAAAA,YAOUgB,EAAAA,QAAA,OALR,QAAQ,YACR,KAAK,KACJ,QAAO/B,CAAA,qBAER,IAAqB,qCAAlBb,EAAA,eAAe,EAAA,CAAA,CAAA,sCAGZA,EAAA,gCADR4B,EAAAA,YAOUgB,EAAAA,QAAA,OALR,UAAU,UACV,KAAK,KACJ,QAAO5B,CAAA,qBAER,IAAsB,qCAAnBhB,EAAA,gBAAgB,EAAA,CAAA,CAAA,0CAMzB6C,iBAAAtB,EAAAA,mBAEM,MAFNuB,EAEM,CADJJ,EAAAA,WAAuBC,EAAA,OAAA,UAAA,CAAA,EAAA,OAAA,EAAA,CAAA,iBADZxC,EAAA,KAAU,CAAA"}
|
|
@@ -1,5 +1,144 @@
|
|
|
1
|
-
import f from "
|
|
1
|
+
import { defineComponent as D, computed as x, createElementBlock as u, openBlock as o, normalizeClass as v, unref as p, createElementVNode as n, withDirectives as T, createCommentVNode as i, createBlock as f, createVNode as b, Fragment as $, renderList as z, withCtx as m, toDisplayString as d, withModifiers as E, renderSlot as V, createTextVNode as B, vShow as F } from "vue";
|
|
2
|
+
import { ChevronDown as N, X as R } from "lucide-vue-next";
|
|
3
|
+
import J from "../atoms/JBadge.vue.js";
|
|
4
|
+
import k from "../atoms/JButton.vue.js";
|
|
5
|
+
import O from "../atoms/JLabel.vue.js";
|
|
6
|
+
import { cn as L } from "../../lib/utils.js";
|
|
7
|
+
const M = { class: "flex items-center justify-between px-3 py-1.5" }, X = { class: "flex items-center gap-2" }, q = {
|
|
8
|
+
key: 2,
|
|
9
|
+
class: "flex items-center gap-1 flex-wrap"
|
|
10
|
+
}, G = { class: "text-muted-foreground" }, H = ["onClick"], I = { class: "flex items-center gap-2" }, K = { class: "px-3 pb-3" }, ee = /* @__PURE__ */ D({
|
|
11
|
+
__name: "JFilterBar",
|
|
12
|
+
props: {
|
|
13
|
+
class: {},
|
|
14
|
+
title: {},
|
|
15
|
+
collapsed: { type: Boolean, default: !0 },
|
|
16
|
+
collapsible: { type: Boolean, default: !0 },
|
|
17
|
+
filterValues: { default: () => ({}) },
|
|
18
|
+
filterDisplay: { default: () => ({}) },
|
|
19
|
+
showResetButton: { type: Boolean, default: !1 },
|
|
20
|
+
showSearchButton: { type: Boolean, default: !1 },
|
|
21
|
+
resetButtonText: { default: "초기화" },
|
|
22
|
+
searchButtonText: { default: "조회" }
|
|
23
|
+
},
|
|
24
|
+
emits: ["update:collapsed", "update:filterValues", "search", "reset"],
|
|
25
|
+
setup(r, { emit: w }) {
|
|
26
|
+
const l = r, a = w, y = x(() => l.collapsible ? !l.collapsed : !0);
|
|
27
|
+
function C(e) {
|
|
28
|
+
return !!(e == null || typeof e == "string" && e.trim() === "" || Array.isArray(e) && e.length === 0);
|
|
29
|
+
}
|
|
30
|
+
const h = x(() => {
|
|
31
|
+
const e = [];
|
|
32
|
+
for (const [t, s] of Object.entries(l.filterDisplay)) {
|
|
33
|
+
const c = l.filterValues[t];
|
|
34
|
+
if (C(c)) continue;
|
|
35
|
+
const g = s.displayValue ? s.displayValue(c) : String(c);
|
|
36
|
+
g.trim() !== "" && e.push({
|
|
37
|
+
key: t,
|
|
38
|
+
label: s.label,
|
|
39
|
+
value: g
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return e;
|
|
43
|
+
});
|
|
44
|
+
function _() {
|
|
45
|
+
a("update:collapsed", !l.collapsed);
|
|
46
|
+
}
|
|
47
|
+
function S() {
|
|
48
|
+
const e = {};
|
|
49
|
+
for (const t of Object.keys(l.filterValues)) {
|
|
50
|
+
const s = l.filterValues[t];
|
|
51
|
+
typeof s == "string" ? e[t] = "" : Array.isArray(s) ? e[t] = [] : e[t] = null;
|
|
52
|
+
}
|
|
53
|
+
a("update:filterValues", e), a("reset");
|
|
54
|
+
}
|
|
55
|
+
function A() {
|
|
56
|
+
a("search");
|
|
57
|
+
}
|
|
58
|
+
function j(e) {
|
|
59
|
+
const t = { ...l.filterValues }, s = t[e];
|
|
60
|
+
typeof s == "string" ? t[e] = "" : Array.isArray(s) ? t[e] = [] : t[e] = null, a("update:filterValues", t);
|
|
61
|
+
}
|
|
62
|
+
return (e, t) => (o(), u("div", {
|
|
63
|
+
class: v(p(L)("j-filter-bar w-full rounded-sm border bg-card text-card-foreground", l.class))
|
|
64
|
+
}, [
|
|
65
|
+
n("div", M, [
|
|
66
|
+
n("div", X, [
|
|
67
|
+
r.collapsible ? (o(), u("button", {
|
|
68
|
+
key: 0,
|
|
69
|
+
type: "button",
|
|
70
|
+
class: "flex items-center justify-center h-6 w-6 rounded hover:bg-accent hover:text-accent-foreground transition-colors",
|
|
71
|
+
onClick: _
|
|
72
|
+
}, [
|
|
73
|
+
b(p(N), {
|
|
74
|
+
class: v([
|
|
75
|
+
"h-3.5 w-3.5 transition-transform",
|
|
76
|
+
y.value ? "rotate-0" : "-rotate-90"
|
|
77
|
+
])
|
|
78
|
+
}, null, 8, ["class"])
|
|
79
|
+
])) : i("", !0),
|
|
80
|
+
r.title ? (o(), f(O, {
|
|
81
|
+
key: 1,
|
|
82
|
+
text: r.title,
|
|
83
|
+
class: "text-sm font-semibold text-foreground"
|
|
84
|
+
}, null, 8, ["text"])) : i("", !0),
|
|
85
|
+
h.value.length > 0 ? (o(), u("div", q, [
|
|
86
|
+
(o(!0), u($, null, z(h.value, (s) => (o(), f(J, {
|
|
87
|
+
key: s.key,
|
|
88
|
+
variant: "secondary",
|
|
89
|
+
size: "sm",
|
|
90
|
+
class: "flex items-center gap-1 cursor-default"
|
|
91
|
+
}, {
|
|
92
|
+
default: m(() => [
|
|
93
|
+
n("span", G, d(s.label) + ":", 1),
|
|
94
|
+
n("span", null, d(s.value), 1),
|
|
95
|
+
n("button", {
|
|
96
|
+
type: "button",
|
|
97
|
+
class: "ml-0.5 rounded-full hover:bg-gray-300 p-0.5 transition-colors",
|
|
98
|
+
onClick: E((c) => j(s.key), ["stop"])
|
|
99
|
+
}, [
|
|
100
|
+
b(p(R), { class: "h-3 w-3" })
|
|
101
|
+
], 8, H)
|
|
102
|
+
]),
|
|
103
|
+
_: 2
|
|
104
|
+
}, 1024))), 128))
|
|
105
|
+
])) : i("", !0)
|
|
106
|
+
]),
|
|
107
|
+
n("div", I, [
|
|
108
|
+
V(e.$slots, "actions", {}, void 0, !0),
|
|
109
|
+
r.showResetButton ? (o(), f(k, {
|
|
110
|
+
key: 0,
|
|
111
|
+
variant: "secondary",
|
|
112
|
+
size: "sm",
|
|
113
|
+
onClick: S
|
|
114
|
+
}, {
|
|
115
|
+
default: m(() => [
|
|
116
|
+
B(d(r.resetButtonText), 1)
|
|
117
|
+
]),
|
|
118
|
+
_: 1
|
|
119
|
+
})) : i("", !0),
|
|
120
|
+
r.showSearchButton ? (o(), f(k, {
|
|
121
|
+
key: 1,
|
|
122
|
+
styletype: "primary",
|
|
123
|
+
size: "sm",
|
|
124
|
+
onClick: A
|
|
125
|
+
}, {
|
|
126
|
+
default: m(() => [
|
|
127
|
+
B(d(r.searchButtonText), 1)
|
|
128
|
+
]),
|
|
129
|
+
_: 1
|
|
130
|
+
})) : i("", !0)
|
|
131
|
+
])
|
|
132
|
+
]),
|
|
133
|
+
T(n("div", K, [
|
|
134
|
+
V(e.$slots, "filters", {}, void 0, !0)
|
|
135
|
+
], 512), [
|
|
136
|
+
[F, y.value]
|
|
137
|
+
])
|
|
138
|
+
], 2));
|
|
139
|
+
}
|
|
140
|
+
});
|
|
2
141
|
export {
|
|
3
|
-
|
|
142
|
+
ee as default
|
|
4
143
|
};
|
|
5
144
|
//# sourceMappingURL=JFilterBar.vue2.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JFilterBar.vue2.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
|
1
|
+
{"version":3,"file":"JFilterBar.vue2.js","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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"}
|
|
@@ -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");const
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue");require("../shadcn/index.cjs");const o=require("../atoms/JButton.vue.cjs");require("lucide-vue-next");require("clsx");require("tailwind-merge");require("@internationalized/date");require("md-editor-v3");;/* empty css */;/* empty css */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 */;/* empty css */;/* empty css */;/* empty css */require("vue-sonner");;/* empty css */;/* empty css */;/* empty css */;/* empty css */;/* empty css */const g=require("./JDynamicForm.vue.cjs");;/* empty css */;/* empty css */require("vue-router");;/* empty css */;/* empty css */;/* empty css */const h=require("../shadcn/Dialog.vue.cjs"),y=require("../shadcn/DialogContent.vue.cjs"),C=require("../shadcn/DialogHeader.vue.cjs"),V=require("../shadcn/DialogTitle.vue.cjs"),b=require("../shadcn/DialogBody.vue.cjs"),k=require("../shadcn/DialogFooter.vue.cjs"),w={class:"space-y-4"},D={key:0,class:"text-sm text-muted-foreground"},N=e.defineComponent({__name:"JFormModal",props:{open:{type:Boolean,default:!1},title:{},description:{},schema:{},modelValue:{},size:{default:"lg"},buttonType:{default:"OkCancel"},confirmText:{default:"확인"},cancelText:{default:"취소"},confirmVariant:{default:"default"},confirmDisabled:{type:Boolean,default:!1}},emits:["update:open","update:modelValue","confirm","cancel","submit","change","error"],setup(r,{expose:d,emit:s}){const n=r,u=s,a=e.ref(null),l=e.ref(n.modelValue||{}),i=e.ref(!1),m=e.computed(()=>({sm:"!max-w-sm",md:"!max-w-md",lg:"!max-w-2xl",xl:"!max-w-4xl","2xl":"!max-w-6xl",full:"!max-w-[95vw]"})[n.size]);e.watch(()=>n.modelValue,t=>{t!=null?l.value={...t}:l.value={}},{deep:!0});const f=t=>{l.value=t,u("update:modelValue",t)},q=t=>{u("change",t)},v=t=>{u("submit",t),i.value&&(u("confirm",t),i.value=!1)},p=t=>{u("error",t),i.value=!1},x=t=>{u("update:open",t)},c=()=>{i.value=!0,a.value&&a.value.submit()},_=()=>{u("cancel"),a.value&&a.value.reset()};return d({reset:()=>a.value?.reset(),submit:()=>a.value?.submit(),formState:e.computed(()=>a.value?.formState)}),(t,B)=>(e.openBlock(),e.createBlock(e.unref(h.default),{open:r.open,"onUpdate:open":x,class:e.normalizeClass(m.value)},{default:e.withCtx(()=>[e.createVNode(e.unref(y.default),null,{default:e.withCtx(()=>[r.title?(e.openBlock(),e.createBlock(e.unref(C.default),{key:0,class:"bg-muted/50 border-b"},{default:e.withCtx(()=>[e.createVNode(e.unref(V.default),null,{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(r.title),1)]),_:1})]),_:1})):e.createCommentVNode("",!0),e.createVNode(e.unref(b.default),{class:"max-h-[70vh] overflow-y-auto"},{default:e.withCtx(()=>[e.createElementVNode("div",w,[r.description?(e.openBlock(),e.createElementBlock("p",D,e.toDisplayString(r.description),1)):e.createCommentVNode("",!0),e.createVNode(e.unref(g.default),{ref_key:"dynamicFormRef",ref:a,schema:r.schema,"model-value":l.value,"onUpdate:modelValue":f,onSubmit:v,onChange:q,onError:p},null,8,["schema","model-value"]),e.renderSlot(t.$slots,"body")])]),_:3}),e.createVNode(e.unref(k.default),null,{default:e.withCtx(()=>[r.buttonType==="OkCancel"?(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[e.createVNode(e.unref(o.default),{variant:"outline",onClick:_},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(r.cancelText),1)]),_:1}),e.createVNode(e.unref(o.default),{variant:r.confirmVariant,disabled:r.confirmDisabled,onClick:c},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(r.confirmText),1)]),_:1},8,["variant","disabled"])],64)):r.buttonType==="Ok"?(e.openBlock(),e.createBlock(e.unref(o.default),{key:1,variant:r.confirmVariant,disabled:r.confirmDisabled,onClick:c,class:"w-full"},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(r.confirmText),1)]),_:1},8,["variant","disabled"])):e.createCommentVNode("",!0),e.renderSlot(t.$slots,"footer")]),_:3})]),_:3})]),_:3},8,["open","class"]))}});exports.default=N;
|
|
2
2
|
//# sourceMappingURL=JFormModal.vue.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JFormModal.vue.cjs","sources":["../../../../src/components/organisms/JFormModal.vue"],"sourcesContent":["<template>\r\n <Dialog\r\n :open=\"open\"\r\n @update:open=\"onOpenChange\"\r\n :class=\"sizeClass\"\r\n >\r\n <DialogContent>\r\n <!-- Header -->\r\n <DialogHeader v-if=\"title\" class=\"bg-muted/50 border-b\">\r\n <DialogTitle>\r\n {{ title }}\r\n </DialogTitle>\r\n </DialogHeader>\r\n\r\n <!-- Body -->\r\n <DialogBody class=\"max-h-[70vh] overflow-y-auto\">\r\n <div class=\"space-y-4\">\r\n <!-- Description -->\r\n <p v-if=\"description\" class=\"text-sm text-muted-foreground\">\r\n {{ description }}\r\n </p>\r\n\r\n <!-- JDynamicForm -->\r\n <JDynamicForm\r\n ref=\"dynamicFormRef\"\r\n :schema=\"schema\"\r\n :model-value=\"internalFormValue\"\r\n @update:model-value=\"handleFormChange\"\r\n @submit=\"handleFormSubmit\"\r\n @change=\"handleFieldChange\"\r\n @error=\"handleFormError\"\r\n />\r\n\r\n <!-- 커스텀 컨텐츠 슬롯 -->\r\n <slot name=\"body\" />\r\n </div>\r\n </DialogBody>\r\n\r\n <!-- Footer -->\r\n <DialogFooter>\r\n <!-- 확인/취소 버튼 -->\r\n <template v-if=\"buttonType === 'OkCancel'\">\r\n <JButton\r\n variant=\"outline\"\r\n @click=\"handleCancel\"\r\n >\r\n {{ cancelText }}\r\n </JButton>\r\n <JButton\r\n :variant=\"confirmVariant\"\r\n :disabled=\"confirmDisabled\"\r\n @click=\"handleConfirm\"\r\n >\r\n {{ confirmText }}\r\n </JButton>\r\n </template>\r\n \r\n <!-- 확인 버튼만 -->\r\n <template v-else-if=\"buttonType === 'Ok'\">\r\n <JButton\r\n :variant=\"confirmVariant\"\r\n :disabled=\"confirmDisabled\"\r\n @click=\"handleConfirm\"\r\n class=\"w-full\"\r\n >\r\n {{ confirmText }}\r\n </JButton>\r\n </template>\r\n\r\n <!-- 커스텀 푸터 슬롯 -->\r\n <slot name=\"footer\" />\r\n </DialogFooter>\r\n </DialogContent>\r\n </Dialog>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed, ref, watch } from 'vue'\r\nimport {\r\n Dialog,\r\n DialogBody,\r\n DialogContent,\r\n DialogFooter,\r\n DialogHeader,\r\n DialogTitle,\r\n} from '@/components/shadcn'\r\nimport { JButton } from '@/components/atoms'\r\nimport { JDynamicForm } from '@/components/organisms'\r\nimport type { FormSchema } from '@/types/dynamic-form'\r\n\r\nexport interface JFormModalProps {\r\n // 모달 표시 여부\r\n open: boolean\r\n // 헤더\r\n title?: string\r\n description?: string\r\n // 다이나믹 폼 스키마 (필수)\r\n schema: FormSchema\r\n // 폼 값\r\n modelValue?: Record<string, any>\r\n // 모달 사이즈 (유동적)\r\n size?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full'\r\n // 버튼 설정\r\n buttonType?: 'Ok' | 'OkCancel'\r\n confirmText?: string\r\n cancelText?: string\r\n confirmVariant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link'\r\n confirmDisabled?: boolean\r\n}\r\n\r\nconst props = withDefaults(defineProps<JFormModalProps>(), {\r\n open: false,\r\n size: 'lg',\r\n buttonType: 'OkCancel',\r\n confirmText: '확인',\r\n cancelText: '취소',\r\n confirmVariant: 'default',\r\n confirmDisabled: false,\r\n})\r\n\r\nconst emit = defineEmits<{\r\n 'update:open': [value: boolean]\r\n 'update:modelValue': [value: Record<string, any>]\r\n 'confirm': [value: Record<string, any>]\r\n 'cancel': []\r\n 'submit': [value: Record<string, any>]\r\n 'change': [data: { field: string; value: any }]\r\n 'error': [errors: any]\r\n}>()\r\n\r\n// 다이나믹 폼 ref\r\nconst dynamicFormRef = ref<InstanceType<typeof JDynamicForm> | null>(null)\r\n\r\n// 내부 폼 값 관리\r\nconst internalFormValue = ref<Record<string, any>>(props.modelValue || {})\r\n// 확인 버튼 클릭 이후 검증 성공 시점에만 confirm 이벤트를 방출하기 위한 플래그\r\nconst isConfirming = ref<boolean>(false)\r\n\r\n// 사이즈 클래스 계산\r\nconst sizeClass = computed(() => {\r\n // Dialog.vue에 기본 max-w-lg가 하드코딩되어 있어 !max-w-*로 오버라이드\r\n const sizeMap: Record<NonNullable<JFormModalProps['size']>, string> = {\r\n 'sm': '!max-w-sm',\r\n 'md': '!max-w-md',\r\n 'lg': '!max-w-2xl',\r\n 'xl': '!max-w-4xl',\r\n '2xl': '!max-w-6xl',\r\n 'full': '!max-w-[95vw]'\r\n }\r\n return sizeMap[props.size]\r\n})\r\n\r\n// props.modelValue 변경 감지\r\nwatch(() => props.modelValue, (newValue) => {\r\n if (newValue != null) {\r\n internalFormValue.value = { ...newValue }\r\n } else {\r\n internalFormValue.value = {}\r\n }\r\n}, { deep: true })\r\n\r\n// 폼 변경 핸들러\r\nconst handleFormChange = (value: Record<string, any>) => {\r\n internalFormValue.value = value\r\n emit('update:modelValue', value)\r\n}\r\n\r\n// 필드 변경 핸들러\r\nconst handleFieldChange = (data: { field: string; value: any }) => {\r\n emit('change', data)\r\n}\r\n\r\n// 폼 제출 핸들러\r\nconst handleFormSubmit = (value: Record<string, any>) => {\r\n emit('submit', value)\r\n if (isConfirming.value) {\r\n emit('confirm', value)\r\n isConfirming.value = false\r\n }\r\n}\r\n\r\n// 폼 에러 핸들러\r\nconst handleFormError = (errors: any) => {\r\n emit('error', errors)\r\n // 검증 실패 시 confirm 방출을 차단\r\n isConfirming.value = false\r\n}\r\n\r\n// 모달 열기/닫기 핸들러\r\nconst onOpenChange = (value: boolean) => {\r\n emit('update:open', value)\r\n}\r\n\r\n// 확인 버튼 핸들러\r\nconst handleConfirm = () => {\r\n // 폼 제출 트리거 (검증 성공 시점에 handleFormSubmit에서 confirm 방출)\r\n isConfirming.value = true\r\n if (dynamicFormRef.value) {\r\n dynamicFormRef.value.submit()\r\n }\r\n}\r\n\r\n// 취소 버튼 핸들러\r\nconst handleCancel = () => {\r\n emit('cancel')\r\n // 폼 리셋\r\n if (dynamicFormRef.value) {\r\n dynamicFormRef.value.reset()\r\n }\r\n}\r\n\r\n// 외부에서 접근 가능한 메서드\r\ndefineExpose({\r\n reset: () => dynamicFormRef.value?.reset(),\r\n submit: () => dynamicFormRef.value?.submit(),\r\n formState: computed(() => dynamicFormRef.value?.formState),\r\n})\r\n</script>\r\n"],"names":["props","__props","emit","__emit","dynamicFormRef","ref","internalFormValue","isConfirming","sizeClass","computed","watch","newValue","handleFormChange","value","handleFieldChange","data","handleFormSubmit","handleFormError","errors","onOpenChange","handleConfirm","handleCancel","__expose","_createBlock","_unref","Dialog","_createVNode","DialogContent","DialogHeader","DialogTitle","DialogBody","_createElementVNode","_hoisted_1","_createElementBlock","_hoisted_2","_toDisplayString","JDynamicForm","_renderSlot","_ctx","DialogFooter","_Fragment","JButton"],"mappings":"
|
|
1
|
+
{"version":3,"file":"JFormModal.vue.cjs","sources":["../../../../src/components/organisms/JFormModal.vue"],"sourcesContent":["<template>\r\n <Dialog\r\n :open=\"open\"\r\n @update:open=\"onOpenChange\"\r\n :class=\"sizeClass\"\r\n >\r\n <DialogContent>\r\n <!-- Header -->\r\n <DialogHeader v-if=\"title\" class=\"bg-muted/50 border-b\">\r\n <DialogTitle>\r\n {{ title }}\r\n </DialogTitle>\r\n </DialogHeader>\r\n\r\n <!-- Body -->\r\n <DialogBody class=\"max-h-[70vh] overflow-y-auto\">\r\n <div class=\"space-y-4\">\r\n <!-- Description -->\r\n <p v-if=\"description\" class=\"text-sm text-muted-foreground\">\r\n {{ description }}\r\n </p>\r\n\r\n <!-- JDynamicForm -->\r\n <JDynamicForm\r\n ref=\"dynamicFormRef\"\r\n :schema=\"schema\"\r\n :model-value=\"internalFormValue\"\r\n @update:model-value=\"handleFormChange\"\r\n @submit=\"handleFormSubmit\"\r\n @change=\"handleFieldChange\"\r\n @error=\"handleFormError\"\r\n />\r\n\r\n <!-- 커스텀 컨텐츠 슬롯 -->\r\n <slot name=\"body\" />\r\n </div>\r\n </DialogBody>\r\n\r\n <!-- Footer -->\r\n <DialogFooter>\r\n <!-- 확인/취소 버튼 -->\r\n <template v-if=\"buttonType === 'OkCancel'\">\r\n <JButton\r\n variant=\"outline\"\r\n @click=\"handleCancel\"\r\n >\r\n {{ cancelText }}\r\n </JButton>\r\n <JButton\r\n :variant=\"confirmVariant\"\r\n :disabled=\"confirmDisabled\"\r\n @click=\"handleConfirm\"\r\n >\r\n {{ confirmText }}\r\n </JButton>\r\n </template>\r\n \r\n <!-- 확인 버튼만 -->\r\n <template v-else-if=\"buttonType === 'Ok'\">\r\n <JButton\r\n :variant=\"confirmVariant\"\r\n :disabled=\"confirmDisabled\"\r\n @click=\"handleConfirm\"\r\n class=\"w-full\"\r\n >\r\n {{ confirmText }}\r\n </JButton>\r\n </template>\r\n\r\n <!-- 커스텀 푸터 슬롯 -->\r\n <slot name=\"footer\" />\r\n </DialogFooter>\r\n </DialogContent>\r\n </Dialog>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed, ref, watch } from 'vue'\r\nimport {\r\n Dialog,\r\n DialogBody,\r\n DialogContent,\r\n DialogFooter,\r\n DialogHeader,\r\n DialogTitle,\r\n} from '@/components/shadcn'\r\nimport { JButton } from '@/components/atoms'\r\nimport { JDynamicForm } from '@/components/organisms'\r\nimport type { FormSchema } from '@/types/dynamic-form'\r\n\r\nexport interface JFormModalProps {\r\n // 모달 표시 여부\r\n open: boolean\r\n // 헤더\r\n title?: string\r\n description?: string\r\n // 다이나믹 폼 스키마 (필수)\r\n schema: FormSchema\r\n // 폼 값\r\n modelValue?: Record<string, any>\r\n // 모달 사이즈 (유동적)\r\n size?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full'\r\n // 버튼 설정\r\n buttonType?: 'Ok' | 'OkCancel'\r\n confirmText?: string\r\n cancelText?: string\r\n confirmVariant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link'\r\n confirmDisabled?: boolean\r\n}\r\n\r\nconst props = withDefaults(defineProps<JFormModalProps>(), {\r\n open: false,\r\n size: 'lg',\r\n buttonType: 'OkCancel',\r\n confirmText: '확인',\r\n cancelText: '취소',\r\n confirmVariant: 'default',\r\n confirmDisabled: false,\r\n})\r\n\r\nconst emit = defineEmits<{\r\n 'update:open': [value: boolean]\r\n 'update:modelValue': [value: Record<string, any>]\r\n 'confirm': [value: Record<string, any>]\r\n 'cancel': []\r\n 'submit': [value: Record<string, any>]\r\n 'change': [data: { field: string; value: any }]\r\n 'error': [errors: any]\r\n}>()\r\n\r\n// 다이나믹 폼 ref\r\nconst dynamicFormRef = ref<InstanceType<typeof JDynamicForm> | null>(null)\r\n\r\n// 내부 폼 값 관리\r\nconst internalFormValue = ref<Record<string, any>>(props.modelValue || {})\r\n// 확인 버튼 클릭 이후 검증 성공 시점에만 confirm 이벤트를 방출하기 위한 플래그\r\nconst isConfirming = ref<boolean>(false)\r\n\r\n// 사이즈 클래스 계산\r\nconst sizeClass = computed(() => {\r\n // Dialog.vue에 기본 max-w-lg가 하드코딩되어 있어 !max-w-*로 오버라이드\r\n const sizeMap: Record<NonNullable<JFormModalProps['size']>, string> = {\r\n 'sm': '!max-w-sm',\r\n 'md': '!max-w-md',\r\n 'lg': '!max-w-2xl',\r\n 'xl': '!max-w-4xl',\r\n '2xl': '!max-w-6xl',\r\n 'full': '!max-w-[95vw]'\r\n }\r\n return sizeMap[props.size]\r\n})\r\n\r\n// props.modelValue 변경 감지\r\nwatch(() => props.modelValue, (newValue) => {\r\n if (newValue != null) {\r\n internalFormValue.value = { ...newValue }\r\n } else {\r\n internalFormValue.value = {}\r\n }\r\n}, { deep: true })\r\n\r\n// 폼 변경 핸들러\r\nconst handleFormChange = (value: Record<string, any>) => {\r\n internalFormValue.value = value\r\n emit('update:modelValue', value)\r\n}\r\n\r\n// 필드 변경 핸들러\r\nconst handleFieldChange = (data: { field: string; value: any }) => {\r\n emit('change', data)\r\n}\r\n\r\n// 폼 제출 핸들러\r\nconst handleFormSubmit = (value: Record<string, any>) => {\r\n emit('submit', value)\r\n if (isConfirming.value) {\r\n emit('confirm', value)\r\n isConfirming.value = false\r\n }\r\n}\r\n\r\n// 폼 에러 핸들러\r\nconst handleFormError = (errors: any) => {\r\n emit('error', errors)\r\n // 검증 실패 시 confirm 방출을 차단\r\n isConfirming.value = false\r\n}\r\n\r\n// 모달 열기/닫기 핸들러\r\nconst onOpenChange = (value: boolean) => {\r\n emit('update:open', value)\r\n}\r\n\r\n// 확인 버튼 핸들러\r\nconst handleConfirm = () => {\r\n // 폼 제출 트리거 (검증 성공 시점에 handleFormSubmit에서 confirm 방출)\r\n isConfirming.value = true\r\n if (dynamicFormRef.value) {\r\n dynamicFormRef.value.submit()\r\n }\r\n}\r\n\r\n// 취소 버튼 핸들러\r\nconst handleCancel = () => {\r\n emit('cancel')\r\n // 폼 리셋\r\n if (dynamicFormRef.value) {\r\n dynamicFormRef.value.reset()\r\n }\r\n}\r\n\r\n// 외부에서 접근 가능한 메서드\r\ndefineExpose({\r\n reset: () => dynamicFormRef.value?.reset(),\r\n submit: () => dynamicFormRef.value?.submit(),\r\n formState: computed(() => dynamicFormRef.value?.formState),\r\n})\r\n</script>\r\n"],"names":["props","__props","emit","__emit","dynamicFormRef","ref","internalFormValue","isConfirming","sizeClass","computed","watch","newValue","handleFormChange","value","handleFieldChange","data","handleFormSubmit","handleFormError","errors","onOpenChange","handleConfirm","handleCancel","__expose","_createBlock","_unref","Dialog","_createVNode","DialogContent","DialogHeader","DialogTitle","DialogBody","_createElementVNode","_hoisted_1","_createElementBlock","_hoisted_2","_toDisplayString","JDynamicForm","_renderSlot","_ctx","DialogFooter","_Fragment","JButton"],"mappings":"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"}
|