@j-solution/components 1.8.0 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. package/README.md +415 -416
  2. package/assets/jwms-portal-frontend-Di6lStzZ.css +1 -0
  3. package/assets/styles/j-components.css +1 -1
  4. package/assets/styles/main.css +29 -29
  5. package/assets/styles/themes.css +422 -422
  6. package/components/atoms/JAvatar.vue.cjs.map +1 -1
  7. package/components/atoms/JAvatar.vue.js.map +1 -1
  8. package/components/atoms/JBadge.vue.cjs.map +1 -1
  9. package/components/atoms/JBadge.vue.js.map +1 -1
  10. package/components/atoms/JButton.vue.cjs +1 -1
  11. package/components/atoms/JButton.vue.js +1 -1
  12. package/components/atoms/JButton.vue2.cjs.map +1 -1
  13. package/components/atoms/JButton.vue2.js.map +1 -1
  14. package/components/atoms/JCombo.vue.cjs.map +1 -1
  15. package/components/atoms/JCombo.vue.js.map +1 -1
  16. package/components/atoms/JDatepicker.vue.cjs.map +1 -1
  17. package/components/atoms/JDatepicker.vue.js.map +1 -1
  18. package/components/atoms/JDivider.vue.cjs.map +1 -1
  19. package/components/atoms/JDivider.vue.js.map +1 -1
  20. package/components/atoms/JEditor.vue.cjs +1 -1
  21. package/components/atoms/JEditor.vue.js +2 -2
  22. package/components/atoms/JEditor.vue2.cjs.map +1 -1
  23. package/components/atoms/JEditor.vue2.js.map +1 -1
  24. package/components/atoms/JGrid.vue.cjs +1 -1
  25. package/components/atoms/JGrid.vue.js +2 -2
  26. package/components/atoms/JGrid.vue2.cjs +1 -1
  27. package/components/atoms/JGrid.vue2.cjs.map +1 -1
  28. package/components/atoms/JGrid.vue2.js +72 -85
  29. package/components/atoms/JGrid.vue2.js.map +1 -1
  30. package/components/atoms/JIcon.vue.cjs.map +1 -1
  31. package/components/atoms/JIcon.vue.js.map +1 -1
  32. package/components/atoms/JImage.vue.cjs.map +1 -1
  33. package/components/atoms/JImage.vue.js.map +1 -1
  34. package/components/atoms/JKbd.vue.cjs.map +1 -1
  35. package/components/atoms/JKbd.vue.js.map +1 -1
  36. package/components/atoms/JLabel.vue.cjs.map +1 -1
  37. package/components/atoms/JLabel.vue.js.map +1 -1
  38. package/components/atoms/JPreview.vue.cjs +1 -1
  39. package/components/atoms/JPreview.vue.js +7 -7
  40. package/components/atoms/JPreview.vue2.cjs.map +1 -1
  41. package/components/atoms/JPreview.vue2.js.map +1 -1
  42. package/components/atoms/JProgress.vue.cjs.map +1 -1
  43. package/components/atoms/JProgress.vue.js.map +1 -1
  44. package/components/atoms/JRadio.vue.cjs.map +1 -1
  45. package/components/atoms/JRadio.vue.js.map +1 -1
  46. package/components/atoms/JSearchCombo.vue.cjs.map +1 -1
  47. package/components/atoms/JSearchCombo.vue.js.map +1 -1
  48. package/components/atoms/JSectionTitle.vue2.cjs +1 -1
  49. package/components/atoms/JSectionTitle.vue2.cjs.map +1 -1
  50. package/components/atoms/JSectionTitle.vue2.js +5 -8
  51. package/components/atoms/JSectionTitle.vue2.js.map +1 -1
  52. package/components/atoms/JSpinner.vue.cjs.map +1 -1
  53. package/components/atoms/JSpinner.vue.js.map +1 -1
  54. package/components/atoms/JToast.vue.cjs.map +1 -1
  55. package/components/atoms/JToast.vue.js.map +1 -1
  56. package/components/atoms/JTooltip.vue.cjs.map +1 -1
  57. package/components/atoms/JTooltip.vue.js.map +1 -1
  58. package/components/molecules/JAlert.vue.cjs +1 -1
  59. package/components/molecules/JAlert.vue.cjs.map +1 -1
  60. package/components/molecules/JAlert.vue.js +2 -5
  61. package/components/molecules/JAlert.vue.js.map +1 -1
  62. package/components/molecules/JBreadcrumb.vue.cjs.map +1 -1
  63. package/components/molecules/JBreadcrumb.vue.js.map +1 -1
  64. package/components/molecules/JEmptyState.vue2.cjs +1 -1
  65. package/components/molecules/JEmptyState.vue2.cjs.map +1 -1
  66. package/components/molecules/JEmptyState.vue2.js +15 -18
  67. package/components/molecules/JEmptyState.vue2.js.map +1 -1
  68. package/components/molecules/JFormField.vue2.cjs +1 -1
  69. package/components/molecules/JFormField.vue2.cjs.map +1 -1
  70. package/components/molecules/JFormField.vue2.js +2 -5
  71. package/components/molecules/JFormField.vue2.js.map +1 -1
  72. package/components/molecules/JTabs.vue.cjs +1 -1
  73. package/components/molecules/JTabs.vue.js +1 -1
  74. package/components/molecules/JTabs.vue2.cjs.map +1 -1
  75. package/components/molecules/JTabs.vue2.js.map +1 -1
  76. package/components/molecules/JTitlebar.vue.cjs +1 -1
  77. package/components/molecules/JTitlebar.vue.cjs.map +1 -1
  78. package/components/molecules/JTitlebar.vue.js +16 -19
  79. package/components/molecules/JTitlebar.vue.js.map +1 -1
  80. package/components/organisms/JDynamicForm.vue2.cjs +1 -1
  81. package/components/organisms/JDynamicForm.vue2.cjs.map +1 -1
  82. package/components/organisms/JDynamicForm.vue2.js +2 -5
  83. package/components/organisms/JDynamicForm.vue2.js.map +1 -1
  84. package/components/organisms/JDynamicTabs.vue.cjs.map +1 -1
  85. package/components/organisms/JDynamicTabs.vue.js.map +1 -1
  86. package/components/organisms/JFilterBar.vue.cjs +1 -1
  87. package/components/organisms/JFilterBar.vue.js +2 -2
  88. package/components/organisms/JFilterBar.vue2.cjs +1 -1
  89. package/components/organisms/JFilterBar.vue2.cjs.map +1 -1
  90. package/components/organisms/JFilterBar.vue2.js +14 -12
  91. package/components/organisms/JFilterBar.vue2.js.map +1 -1
  92. package/components/organisms/JFormModal.vue.cjs +1 -1
  93. package/components/organisms/JFormModal.vue.cjs.map +1 -1
  94. package/components/organisms/JFormModal.vue.js +14 -17
  95. package/components/organisms/JFormModal.vue.js.map +1 -1
  96. package/components/organisms/JModal.vue.cjs +1 -1
  97. package/components/organisms/JModal.vue.cjs.map +1 -1
  98. package/components/organisms/JModal.vue.js +2 -5
  99. package/components/organisms/JModal.vue.js.map +1 -1
  100. package/components/organisms/JPageContainer.vue.cjs.map +1 -1
  101. package/components/organisms/JPageContainer.vue.js.map +1 -1
  102. package/components/organisms/JSearchPanel.vue2.cjs +1 -1
  103. package/components/organisms/JSearchPanel.vue2.cjs.map +1 -1
  104. package/components/organisms/JSearchPanel.vue2.js +20 -23
  105. package/components/organisms/JSearchPanel.vue2.js.map +1 -1
  106. package/components/organisms/JSidebar/JSidebar.vue.cjs +2 -0
  107. package/components/organisms/JSidebar/JSidebar.vue.cjs.map +1 -0
  108. package/components/organisms/JSidebar/JSidebar.vue.js +189 -0
  109. package/components/organisms/JSidebar/JSidebar.vue.js.map +1 -0
  110. package/components/organisms/JSidebar/JSidebar.vue2.cjs +2 -0
  111. package/components/organisms/JSidebar/JSidebar.vue2.cjs.map +1 -0
  112. package/components/organisms/JSidebar/JSidebar.vue2.js +5 -0
  113. package/components/organisms/JSidebar/JSidebar.vue2.js.map +1 -0
  114. package/components/organisms/JSidebar/JSidebarGroup.vue.cjs +2 -0
  115. package/components/organisms/JSidebar/JSidebarGroup.vue.cjs.map +1 -0
  116. package/components/organisms/JSidebar/JSidebarGroup.vue.js +89 -0
  117. package/components/organisms/JSidebar/JSidebarGroup.vue.js.map +1 -0
  118. package/components/organisms/JSidebar/JSidebarGroup.vue2.cjs +2 -0
  119. package/components/organisms/JSidebar/JSidebarGroup.vue2.cjs.map +1 -0
  120. package/components/organisms/JSidebar/JSidebarGroup.vue2.js +5 -0
  121. package/components/organisms/JSidebar/JSidebarGroup.vue2.js.map +1 -0
  122. package/components/organisms/JSidebar/JSidebarItem.vue.cjs +2 -0
  123. package/components/organisms/JSidebar/JSidebarItem.vue.cjs.map +1 -0
  124. package/components/organisms/JSidebar/JSidebarItem.vue.js +79 -0
  125. package/components/organisms/JSidebar/JSidebarItem.vue.js.map +1 -0
  126. package/components/organisms/JSidebar/JSidebarItem.vue2.cjs +2 -0
  127. package/components/organisms/JSidebar/JSidebarItem.vue2.cjs.map +1 -0
  128. package/components/organisms/JSidebar/JSidebarItem.vue2.js +5 -0
  129. package/components/organisms/JSidebar/JSidebarItem.vue2.js.map +1 -0
  130. package/components/organisms/JSidebarAdvanced.vue.cjs +1 -1
  131. package/components/organisms/JSidebarAdvanced.vue.js +7 -7
  132. package/components/organisms/JSidebarAdvanced.vue2.cjs.map +1 -1
  133. package/components/organisms/JSidebarAdvanced.vue2.js.map +1 -1
  134. package/components/organisms/JSidebarSimple.vue.cjs +1 -1
  135. package/components/organisms/JSidebarSimple.vue.js +2 -2
  136. package/components/organisms/JSidebarSimple.vue2.cjs.map +1 -1
  137. package/components/organisms/JSidebarSimple.vue2.js.map +1 -1
  138. package/components/shadcn/AccordionTrigger.vue.cjs.map +1 -1
  139. package/components/shadcn/AccordionTrigger.vue.js.map +1 -1
  140. package/components/shadcn/Card.vue.cjs.map +1 -1
  141. package/components/shadcn/Card.vue.js.map +1 -1
  142. package/components/shadcn/CardContent.vue.cjs.map +1 -1
  143. package/components/shadcn/CardContent.vue.js.map +1 -1
  144. package/components/shadcn/CardDescription.vue.cjs.map +1 -1
  145. package/components/shadcn/CardDescription.vue.js.map +1 -1
  146. package/components/shadcn/CardFooter.vue.cjs.map +1 -1
  147. package/components/shadcn/CardFooter.vue.js.map +1 -1
  148. package/components/shadcn/CardHeader.vue.cjs.map +1 -1
  149. package/components/shadcn/CardHeader.vue.js.map +1 -1
  150. package/components/shadcn/CardTitle.vue.cjs.map +1 -1
  151. package/components/shadcn/CardTitle.vue.js.map +1 -1
  152. package/components/shadcn/Checkbox.vue.cjs.map +1 -1
  153. package/components/shadcn/Checkbox.vue.js.map +1 -1
  154. package/components/shadcn/Combobox.vue.cjs.map +1 -1
  155. package/components/shadcn/Combobox.vue.js.map +1 -1
  156. package/components/shadcn/ComboboxAnchor.vue.cjs.map +1 -1
  157. package/components/shadcn/ComboboxAnchor.vue.js.map +1 -1
  158. package/components/shadcn/ComboboxEmpty.vue.cjs.map +1 -1
  159. package/components/shadcn/ComboboxEmpty.vue.js.map +1 -1
  160. package/components/shadcn/ComboboxGroup.vue.cjs.map +1 -1
  161. package/components/shadcn/ComboboxGroup.vue.js.map +1 -1
  162. package/components/shadcn/ComboboxInput.vue.cjs.map +1 -1
  163. package/components/shadcn/ComboboxInput.vue.js.map +1 -1
  164. package/components/shadcn/ComboboxItem.vue.cjs.map +1 -1
  165. package/components/shadcn/ComboboxItem.vue.js.map +1 -1
  166. package/components/shadcn/ComboboxList.vue.cjs.map +1 -1
  167. package/components/shadcn/ComboboxList.vue.js.map +1 -1
  168. package/components/shadcn/ComboboxTrigger.vue.cjs.map +1 -1
  169. package/components/shadcn/ComboboxTrigger.vue.js.map +1 -1
  170. package/components/shadcn/ContextMenu.vue.cjs.map +1 -1
  171. package/components/shadcn/ContextMenu.vue.js.map +1 -1
  172. package/components/shadcn/ContextMenuContent.vue.cjs.map +1 -1
  173. package/components/shadcn/ContextMenuContent.vue.js.map +1 -1
  174. package/components/shadcn/ContextMenuGroup.vue.cjs.map +1 -1
  175. package/components/shadcn/ContextMenuGroup.vue.js.map +1 -1
  176. package/components/shadcn/ContextMenuItem.vue.cjs.map +1 -1
  177. package/components/shadcn/ContextMenuItem.vue.js.map +1 -1
  178. package/components/shadcn/ContextMenuLabel.vue.cjs.map +1 -1
  179. package/components/shadcn/ContextMenuLabel.vue.js.map +1 -1
  180. package/components/shadcn/ContextMenuSeparator.vue.cjs.map +1 -1
  181. package/components/shadcn/ContextMenuSeparator.vue.js.map +1 -1
  182. package/components/shadcn/ContextMenuSub.vue.cjs.map +1 -1
  183. package/components/shadcn/ContextMenuSub.vue.js.map +1 -1
  184. package/components/shadcn/ContextMenuSubContent.vue.cjs.map +1 -1
  185. package/components/shadcn/ContextMenuSubContent.vue.js.map +1 -1
  186. package/components/shadcn/ContextMenuSubTrigger.vue.cjs.map +1 -1
  187. package/components/shadcn/ContextMenuSubTrigger.vue.js.map +1 -1
  188. package/components/shadcn/ContextMenuTrigger.vue.cjs.map +1 -1
  189. package/components/shadcn/ContextMenuTrigger.vue.js.map +1 -1
  190. package/components/shadcn/Field.vue.cjs.map +1 -1
  191. package/components/shadcn/Field.vue.js.map +1 -1
  192. package/components/shadcn/FieldContent.vue.cjs.map +1 -1
  193. package/components/shadcn/FieldContent.vue.js.map +1 -1
  194. package/components/shadcn/FieldDescription.vue.cjs.map +1 -1
  195. package/components/shadcn/FieldDescription.vue.js.map +1 -1
  196. package/components/shadcn/FieldError.vue.cjs.map +1 -1
  197. package/components/shadcn/FieldError.vue.js.map +1 -1
  198. package/components/shadcn/FieldGroup.vue.cjs.map +1 -1
  199. package/components/shadcn/FieldGroup.vue.js.map +1 -1
  200. package/components/shadcn/FieldLabel.vue.cjs.map +1 -1
  201. package/components/shadcn/FieldLabel.vue.js.map +1 -1
  202. package/components/shadcn/Input.vue.cjs.map +1 -1
  203. package/components/shadcn/Input.vue.js.map +1 -1
  204. package/components/shadcn/Label.vue.cjs.map +1 -1
  205. package/components/shadcn/Label.vue.js.map +1 -1
  206. package/components/shadcn/RadioGroup.vue.cjs.map +1 -1
  207. package/components/shadcn/RadioGroup.vue.js.map +1 -1
  208. package/components/shadcn/RadioGroupItem.vue.cjs.map +1 -1
  209. package/components/shadcn/RadioGroupItem.vue.js.map +1 -1
  210. package/components/shadcn/Select.vue.cjs.map +1 -1
  211. package/components/shadcn/Select.vue.js.map +1 -1
  212. package/components/shadcn/SelectContent.vue.cjs.map +1 -1
  213. package/components/shadcn/SelectContent.vue.js.map +1 -1
  214. package/components/shadcn/SelectGroup.vue.cjs.map +1 -1
  215. package/components/shadcn/SelectGroup.vue.js.map +1 -1
  216. package/components/shadcn/SelectItem.vue.cjs.map +1 -1
  217. package/components/shadcn/SelectItem.vue.js.map +1 -1
  218. package/components/shadcn/SelectLabel.vue.cjs.map +1 -1
  219. package/components/shadcn/SelectLabel.vue.js.map +1 -1
  220. package/components/shadcn/SelectScrollDownButton.vue2.cjs.map +1 -1
  221. package/components/shadcn/SelectScrollDownButton.vue2.js.map +1 -1
  222. package/components/shadcn/SelectScrollUpButton.vue2.cjs.map +1 -1
  223. package/components/shadcn/SelectScrollUpButton.vue2.js.map +1 -1
  224. package/components/shadcn/SelectTrigger.vue.cjs.map +1 -1
  225. package/components/shadcn/SelectTrigger.vue.js.map +1 -1
  226. package/components/shadcn/SelectValue.vue.cjs.map +1 -1
  227. package/components/shadcn/SelectValue.vue.js.map +1 -1
  228. package/components/shadcn/Separator.vue.cjs.map +1 -1
  229. package/components/shadcn/Separator.vue.js.map +1 -1
  230. package/components/shadcn/Switch.vue.cjs.map +1 -1
  231. package/components/shadcn/Switch.vue.js.map +1 -1
  232. package/components/shadcn/Tabs.vue.cjs.map +1 -1
  233. package/components/shadcn/Tabs.vue.js.map +1 -1
  234. package/components/shadcn/TabsContent.vue.cjs.map +1 -1
  235. package/components/shadcn/TabsContent.vue.js.map +1 -1
  236. package/components/shadcn/TabsTrigger.vue.cjs.map +1 -1
  237. package/components/shadcn/TabsTrigger.vue.js.map +1 -1
  238. package/components/shadcn/Textarea.vue.cjs.map +1 -1
  239. package/components/shadcn/Textarea.vue.js.map +1 -1
  240. package/components/shadcn/Toaster.vue.cjs.map +1 -1
  241. package/components/shadcn/Toaster.vue.js.map +1 -1
  242. package/components/shadcn/index.cjs.map +1 -1
  243. package/components/shadcn/index.js.map +1 -1
  244. package/components/shadcn/resizable/ResizableHandle.vue.cjs.map +1 -1
  245. package/components/shadcn/resizable/ResizableHandle.vue.js.map +1 -1
  246. package/components/shadcn/resizable/ResizablePanelGroup.vue.cjs.map +1 -1
  247. package/components/shadcn/resizable/ResizablePanelGroup.vue.js.map +1 -1
  248. package/components/templates/JLayout.vue.cjs.map +1 -1
  249. package/components/templates/JLayout.vue.js.map +1 -1
  250. package/components/templates/JLayoutSimple.vue.cjs +1 -1
  251. package/components/templates/JLayoutSimple.vue.cjs.map +1 -1
  252. package/components/templates/JLayoutSimple.vue.js +36 -30
  253. package/components/templates/JLayoutSimple.vue.js.map +1 -1
  254. package/index.cjs +1 -1
  255. package/index.js +22 -20
  256. package/lib/styleTypePreset.cjs.map +1 -1
  257. package/lib/styleTypePreset.js.map +1 -1
  258. package/lib/theme-utils.cjs.map +1 -1
  259. package/lib/theme-utils.js.map +1 -1
  260. package/package.json +1 -1
  261. package/tailwind.config.js +81 -81
  262. package/types/index.d.ts +119 -107
  263. package/types/sidebar.types.cjs +2 -0
  264. package/types/sidebar.types.cjs.map +1 -0
  265. package/types/sidebar.types.js +5 -0
  266. package/types/sidebar.types.js.map +1 -0
  267. package/assets/jwms-portal-frontend-BtHTA-UF.css +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"JPageContainer.vue.js","sources":["../../../../src/components/organisms/JPageContainer.vue"],"sourcesContent":["<script setup lang=\"ts\">\r\nimport { computed } from 'vue'\r\nimport JBreadcrumb from '@/components/molecules/JBreadcrumb.vue'\r\nimport JTitlebar from '@/components/molecules/JTitlebar.vue'\r\nimport { cn } from '@/lib/utils'\r\nimport type { BreadcrumbItem } from '@/components/molecules/JBreadcrumb.vue'\r\nimport type { TitlebarButton } from '@/components/molecules/JTitlebar.vue'\r\n\r\n/**\r\n * JPageContainer - 기본 페이지 컨테이너 컴포넌트 (organisms)\r\n * Page Container Component\r\n * \r\n * @description\r\n * 페이지의 기본 레이아웃을 담당하는 컨테이너 컴포넌트입니다.\r\n * 브레드크럼, 제목 영역(JTitlebar), 콘텐츠 영역을 포함합니다.\r\n * \r\n * @example\r\n * ```vue\r\n * <JPageContainer\r\n * :breadcrumb-items=\"breadcrumbItems\"\r\n * title=\"페이지 제목\"\r\n * :titlebar-buttons=\"buttons\"\r\n * >\r\n * <div>페이지 콘텐츠</div>\r\n * </JPageContainer>\r\n * ```\r\n */\r\n\r\ntype StyleType =\r\n | 'default' // 기본 스타일\r\n | 'minimal' // 최소 스타일\r\n\r\nconst props = withDefaults(\r\n defineProps<{\r\n /** 브레드크럼 아이템 목록 */\r\n breadcrumbItems?: BreadcrumbItem[]\r\n /** 브레드크럼 표시 여부 */\r\n showBreadcrumb?: boolean\r\n /** JTitlebar의 모든 props 전달 */\r\n title?: string\r\n icon?: string\r\n description?: string\r\n titlebarButtons?: TitlebarButton[]\r\n titlebarStyletype?: 'default' | 'primary' | 'accent' | 'neutral' | 'elevated'\r\n /** JTitlebar 표시 여부 */\r\n showTitlebar?: boolean\r\n /** 스타일 타입 */\r\n styletype?: StyleType\r\n /** 콘텐츠 영역 스크롤 가능 여부 */\r\n contentScroll?: boolean\r\n /** 추가 CSS 클래스 */\r\n class?: string\r\n }>(),\r\n {\r\n breadcrumbItems: () => [],\r\n showBreadcrumb: true,\r\n showTitlebar: true,\r\n styletype: 'default',\r\n contentScroll: true,\r\n }\r\n)\r\n\r\nconst emit = defineEmits<{\r\n /** 브레드크럼 아이템 클릭 이벤트 */\r\n breadcrumbClick: [item: BreadcrumbItem, index: number]\r\n /** 타이틀바 버튼 클릭 이벤트 */\r\n titlebarButtonClick: [button: TitlebarButton]\r\n}>()\r\n\r\n/**\r\n * 스타일 프리셋\r\n */\r\nconst STYLE_PRESETS: Record<StyleType, {\r\n containerClass: string\r\n contentClass: string\r\n}> = {\r\n default: {\r\n containerClass: 'flex flex-col h-full w-full bg-background',\r\n contentClass: 'flex-1 p-3 pt-2 pb-2 gap-2 bg-background text-foreground',\r\n },\r\n minimal: {\r\n containerClass: 'flex flex-col h-full w-full bg-background',\r\n contentClass: 'flex-1 p-1 gap-1 bg-background text-foreground',\r\n },\r\n}\r\n\r\nconst preset = computed(() => {\r\n return STYLE_PRESETS[props.styletype] ?? STYLE_PRESETS.default\r\n})\r\n\r\n/**\r\n * 콘텐츠 클래스 (스크롤 포함)\r\n */\r\nconst contentClasses = computed(() => {\r\n return cn(\r\n preset.value.contentClass,\r\n props.contentScroll ? 'overflow-auto' : 'overflow-hidden'\r\n )\r\n})\r\n\r\n/**\r\n * 브레드크럼 아이템 클릭 핸들러\r\n */\r\nconst handleBreadcrumbClick = (item: BreadcrumbItem, index: number) => {\r\n emit('breadcrumbClick', item, index)\r\n}\r\n\r\n/**\r\n * 타이틀바 버튼 클릭 핸들러\r\n */\r\nconst handleTitlebarButtonClick = (button: TitlebarButton) => {\r\n emit('titlebarButtonClick', button)\r\n}\r\n</script>\r\n\r\n<template>\r\n <div :class=\"cn(preset.containerClass, props.class)\">\r\n <!-- 브레드크럼 -->\r\n <JBreadcrumb\r\n v-if=\"showBreadcrumb && breadcrumbItems && breadcrumbItems.length > 0\"\r\n :items=\"breadcrumbItems\"\r\n @item-click=\"handleBreadcrumbClick\"\r\n />\r\n\r\n <!-- 제목 영역 (JTitlebar) -->\r\n <JTitlebar\r\n v-if=\"showTitlebar\"\r\n :title=\"title\"\r\n :icon=\"icon\"\r\n :description=\"description\"\r\n :buttons=\"titlebarButtons\"\r\n :styletype=\"titlebarStyletype\"\r\n @button-click=\"handleTitlebarButtonClick\"\r\n >\r\n <template #buttons>\r\n <slot name=\"titlebar-buttons\" />\r\n </template>\r\n </JTitlebar>\r\n\r\n <!-- 콘텐츠 영역 -->\r\n <div :class=\"contentClasses\">\r\n <slot />\r\n </div>\r\n </div>\r\n</template>\r\n"],"names":["props","__props","emit","__emit","STYLE_PRESETS","preset","computed","contentClasses","cn","handleBreadcrumbClick","item","index","handleTitlebarButtonClick","button","_createElementBlock","_normalizeClass","_unref","_createBlock","JBreadcrumb","JTitlebar","_renderSlot","_ctx","_createElementVNode"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAgCA,UAAMA,IAAQC,GA8BRC,IAAOC,GAUPC,IAGD;AAAA,MACH,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,cAAc;AAAA,MAAA;AAAA,MAEhB,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,cAAc;AAAA,MAAA;AAAA,IAChB,GAGIC,IAASC,EAAS,MACfF,EAAcJ,EAAM,SAAS,KAAKI,EAAc,OACxD,GAKKG,IAAiBD,EAAS,MACvBE;AAAA,MACLH,EAAO,MAAM;AAAA,MACbL,EAAM,gBAAgB,kBAAkB;AAAA,IAAA,CAE3C,GAKKS,IAAwB,CAACC,GAAsBC,MAAkB;AACrE,MAAAT,EAAK,mBAAmBQ,GAAMC,CAAK;AAAA,IACrC,GAKMC,IAA4B,CAACC,MAA2B;AAC5D,MAAAX,EAAK,uBAAuBW,CAAM;AAAA,IACpC;2BAIEC,EA2BM,OAAA;AAAA,MA3BA,OAAKC,EAAEC,KAAGX,EAAA,MAAO,gBAAgBL,EAAM,KAAK,CAAA;AAAA,IAAA;MAGxCC,EAAA,kBAAkBA,EAAA,mBAAmBA,EAAA,gBAAgB,SAAM,UADnEgB,EAIEC,GAAA;AAAA;QAFC,OAAOjB,EAAA;AAAA,QACP,aAAYQ;AAAA,MAAA;MAKPR,EAAA,qBADRgB,EAYYE,GAAA;AAAA;QAVT,OAAOlB,EAAA;AAAA,QACP,MAAMA,EAAA;AAAA,QACN,aAAaA,EAAA;AAAA,QACb,SAASA,EAAA;AAAA,QACT,WAAWA,EAAA;AAAA,QACX,eAAcW;AAAA,MAAA;QAEJ,WACT,MAAgC;AAAA,UAAhCQ,EAAgCC,EAAA,QAAA,kBAAA;AAAA,QAAA;;;MAKpCC,EAEM,OAAA;AAAA,QAFA,SAAOf,EAAA,KAAc;AAAA,MAAA;QACzBa,EAAQC,EAAA,QAAA,SAAA;AAAA,MAAA;;;;"}
1
+ {"version":3,"file":"JPageContainer.vue.js","sources":["../../../../src/components/organisms/JPageContainer.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport JBreadcrumb from '@/components/molecules/JBreadcrumb.vue'\nimport JTitlebar from '@/components/molecules/JTitlebar.vue'\nimport { cn } from '@/lib/utils'\nimport type { BreadcrumbItem } from '@/components/molecules/JBreadcrumb.vue'\nimport type { TitlebarButton } from '@/components/molecules/JTitlebar.vue'\n\n/**\n * JPageContainer - 기본 페이지 컨테이너 컴포넌트 (organisms)\n * Page Container Component\n * \n * @description\n * 페이지의 기본 레이아웃을 담당하는 컨테이너 컴포넌트입니다.\n * 브레드크럼, 제목 영역(JTitlebar), 콘텐츠 영역을 포함합니다.\n * \n * @example\n * ```vue\n * <JPageContainer\n * :breadcrumb-items=\"breadcrumbItems\"\n * title=\"페이지 제목\"\n * :titlebar-buttons=\"buttons\"\n * >\n * <div>페이지 콘텐츠</div>\n * </JPageContainer>\n * ```\n */\n\ntype StyleType =\n | 'default' // 기본 스타일\n | 'minimal' // 최소 스타일\n\nconst props = withDefaults(\n defineProps<{\n /** 브레드크럼 아이템 목록 */\n breadcrumbItems?: BreadcrumbItem[]\n /** 브레드크럼 표시 여부 */\n showBreadcrumb?: boolean\n /** JTitlebar의 모든 props 전달 */\n title?: string\n icon?: string\n description?: string\n titlebarButtons?: TitlebarButton[]\n titlebarStyletype?: 'default' | 'primary' | 'accent' | 'neutral' | 'elevated'\n /** JTitlebar 표시 여부 */\n showTitlebar?: boolean\n /** 스타일 타입 */\n styletype?: StyleType\n /** 콘텐츠 영역 스크롤 가능 여부 */\n contentScroll?: boolean\n /** 추가 CSS 클래스 */\n class?: string\n }>(),\n {\n breadcrumbItems: () => [],\n showBreadcrumb: true,\n showTitlebar: true,\n styletype: 'default',\n contentScroll: true,\n }\n)\n\nconst emit = defineEmits<{\n /** 브레드크럼 아이템 클릭 이벤트 */\n breadcrumbClick: [item: BreadcrumbItem, index: number]\n /** 타이틀바 버튼 클릭 이벤트 */\n titlebarButtonClick: [button: TitlebarButton]\n}>()\n\n/**\n * 스타일 프리셋\n */\nconst STYLE_PRESETS: Record<StyleType, {\n containerClass: string\n contentClass: string\n}> = {\n default: {\n containerClass: 'flex flex-col h-full w-full bg-background',\n contentClass: 'flex-1 p-3 pt-2 pb-2 gap-2 bg-background text-foreground',\n },\n minimal: {\n containerClass: 'flex flex-col h-full w-full bg-background',\n contentClass: 'flex-1 p-1 gap-1 bg-background text-foreground',\n },\n}\n\nconst preset = computed(() => {\n return STYLE_PRESETS[props.styletype] ?? STYLE_PRESETS.default\n})\n\n/**\n * 콘텐츠 클래스 (스크롤 포함)\n */\nconst contentClasses = computed(() => {\n return cn(\n preset.value.contentClass,\n props.contentScroll ? 'overflow-auto' : 'overflow-hidden'\n )\n})\n\n/**\n * 브레드크럼 아이템 클릭 핸들러\n */\nconst handleBreadcrumbClick = (item: BreadcrumbItem, index: number) => {\n emit('breadcrumbClick', item, index)\n}\n\n/**\n * 타이틀바 버튼 클릭 핸들러\n */\nconst handleTitlebarButtonClick = (button: TitlebarButton) => {\n emit('titlebarButtonClick', button)\n}\n</script>\n\n<template>\n <div :class=\"cn(preset.containerClass, props.class)\">\n <!-- 브레드크럼 -->\n <JBreadcrumb\n v-if=\"showBreadcrumb && breadcrumbItems && breadcrumbItems.length > 0\"\n :items=\"breadcrumbItems\"\n @item-click=\"handleBreadcrumbClick\"\n />\n\n <!-- 제목 영역 (JTitlebar) -->\n <JTitlebar\n v-if=\"showTitlebar\"\n :title=\"title\"\n :icon=\"icon\"\n :description=\"description\"\n :buttons=\"titlebarButtons\"\n :styletype=\"titlebarStyletype\"\n @button-click=\"handleTitlebarButtonClick\"\n >\n <template #buttons>\n <slot name=\"titlebar-buttons\" />\n </template>\n </JTitlebar>\n\n <!-- 콘텐츠 영역 -->\n <div :class=\"contentClasses\">\n <slot />\n </div>\n </div>\n</template>\n"],"names":["props","__props","emit","__emit","STYLE_PRESETS","preset","computed","contentClasses","cn","handleBreadcrumbClick","item","index","handleTitlebarButtonClick","button","_createElementBlock","_normalizeClass","_unref","_createBlock","JBreadcrumb","JTitlebar","_renderSlot","_ctx","_createElementVNode"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAgCA,UAAMA,IAAQC,GA8BRC,IAAOC,GAUPC,IAGD;AAAA,MACH,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,cAAc;AAAA,MAAA;AAAA,MAEhB,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,cAAc;AAAA,MAAA;AAAA,IAChB,GAGIC,IAASC,EAAS,MACfF,EAAcJ,EAAM,SAAS,KAAKI,EAAc,OACxD,GAKKG,IAAiBD,EAAS,MACvBE;AAAA,MACLH,EAAO,MAAM;AAAA,MACbL,EAAM,gBAAgB,kBAAkB;AAAA,IAAA,CAE3C,GAKKS,IAAwB,CAACC,GAAsBC,MAAkB;AACrE,MAAAT,EAAK,mBAAmBQ,GAAMC,CAAK;AAAA,IACrC,GAKMC,IAA4B,CAACC,MAA2B;AAC5D,MAAAX,EAAK,uBAAuBW,CAAM;AAAA,IACpC;2BAIEC,EA2BM,OAAA;AAAA,MA3BA,OAAKC,EAAEC,KAAGX,EAAA,MAAO,gBAAgBL,EAAM,KAAK,CAAA;AAAA,IAAA;MAGxCC,EAAA,kBAAkBA,EAAA,mBAAmBA,EAAA,gBAAgB,SAAM,UADnEgB,EAIEC,GAAA;AAAA;QAFC,OAAOjB,EAAA;AAAA,QACP,aAAYQ;AAAA,MAAA;MAKPR,EAAA,qBADRgB,EAYYE,GAAA;AAAA;QAVT,OAAOlB,EAAA;AAAA,QACP,MAAMA,EAAA;AAAA,QACN,aAAaA,EAAA;AAAA,QACb,SAASA,EAAA;AAAA,QACT,WAAWA,EAAA;AAAA,QACX,eAAcW;AAAA,MAAA;QAEJ,WACT,MAAgC;AAAA,UAAhCQ,EAAgCC,EAAA,QAAA,kBAAA;AAAA,QAAA;;;MAKpCC,EAEM,OAAA;AAAA,QAFA,SAAOf,EAAA,KAAc;AAAA,MAAA;QACzBa,EAAQC,EAAA,QAAA,SAAA;AAAA,MAAA;;;;"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue"),v=require("lucide-vue-next");require("../shadcn/index.cjs");const N=require("../atoms/JButton.vue.cjs");require("clsx");require("tailwind-merge");require("@internationalized/date");require("md-editor-v3");;/* empty css */;/* empty css */const V=require("../atoms/JBadge.vue.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");const C=require("./JDynamicForm.vue.cjs"),g=require("../shadcn/Card.vue.cjs"),w=require("../shadcn/CardHeader.vue.cjs"),B=require("../shadcn/CardTitle.vue.cjs"),E=require("../shadcn/CardContent.vue.cjs"),S={class:"flex items-center justify-between"},D={class:"flex items-center gap-3 flex-1"},F={key:2,class:"flex flex-wrap items-center gap-2 ml-2"},j=["onClick","aria-label"],T=e.defineComponent({__name:"JSearchPanel",props:{title:{default:"조회조건"},schema:{},modelValue:{},defaultCollapsed:{type:Boolean,default:!1},collapsible:{type:Boolean,default:!0}},emits:["update:modelValue","submit","reset"],setup(i,{emit:_}){const a=i,u=_,d=e.ref(null),n=e.ref(!a.defaultCollapsed),s=e.reactive(a.modelValue?{...a.modelValue}:{});let c=!1;e.watch(()=>a.modelValue,l=>{l&&!c&&(Object.keys(s).forEach(r=>{r in l||delete s[r]}),Object.assign(s,l)),c=!1},{deep:!0,immediate:!0});function y(l){c=!0,Object.assign(s,l),u("update:modelValue",{...l})}const m=e.computed(()=>{if(!a.schema)return[];const l=[];return a.schema.type==="simple"&&a.schema.fields?l.push(...a.schema.fields):a.schema.type==="sectioned"&&a.schema.sections?a.schema.sections.forEach(r=>{r.fields&&l.push(...r.fields)}):a.schema.type==="wizard"&&a.schema.steps&&a.schema.steps.forEach(r=>{r.fields&&l.push(...r.fields)}),l}),f=e.computed(()=>{if(!s||!a.schema)return[];const l=[],r=s;return m.value.forEach(t=>{const o=r[t.controlName];if(o==null||o===""||o==="ALL"||o==="SELECT"||t.type==="checkbox"&&o==="N"||t.type==="switch"&&o==="N")return;let p=String(o);if((t.type==="combo"||t.type==="searchcombo")&&t.options){const h=t.options.find(k=>k.value===o);h&&(p=h.label)}t.type==="checkbox"||t.type==="switch"?o==="Y"&&l.push({fieldName:t.controlName,label:t.label,value:t.inlineLabel||t.label}):l.push({fieldName:t.controlName,label:t.label,value:p})}),l});function b(l){u("submit",l)}function q(){d.value&&(d.value.reset(),u("reset"))}function x(l){if(!s||!a.schema)return;const r=m.value.find(t=>t.controlName===l);r&&(r.type==="checkbox"||r.type==="switch"?s[l]="N":s[l]="",c=!0,u("update:modelValue",{...s}))}return(l,r)=>(e.openBlock(),e.createBlock(e.unref(g.default),{class:"w-full"},{default:e.withCtx(()=>[e.createVNode(e.unref(w.default),{class:"pt-4 pb-3 px-6"},{default:e.withCtx(()=>[e.createElementVNode("div",S,[e.createElementVNode("div",D,[i.collapsible?(e.openBlock(),e.createElementBlock("button",{key:0,type:"button",onClick:r[0]||(r[0]=t=>n.value=!n.value),class:"flex items-center gap-2 font-semibold hover:text-primary transition-colors"},[e.createVNode(e.unref(v.ChevronDown),{class:e.normalizeClass(["h-4 w-4 transition-transform",n.value?"rotate-0":"-rotate-90"])},null,8,["class"]),e.createTextVNode(" "+e.toDisplayString(i.title),1)])):(e.openBlock(),e.createBlock(e.unref(B.default),{key:1,class:"mb-0"},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(i.title),1)]),_:1})),f.value.length>0?(e.openBlock(),e.createElementBlock("div",F,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(f.value,t=>(e.openBlock(),e.createBlock(e.unref(V.default),{key:t.fieldName,variant:"secondary",size:"sm",class:"flex items-center gap-1.5 pr-1"},{default:e.withCtx(()=>[e.createElementVNode("span",null,e.toDisplayString(t.label)+": "+e.toDisplayString(t.value),1),e.createElementVNode("button",{type:"button",onClick:e.withModifiers(o=>x(t.fieldName),["stop"]),class:"h-4 w-4 rounded-full hover:bg-destructive/20 hover:text-destructive transition-colors flex items-center justify-center","aria-label":`${t.label} 조건 제거`},[e.createVNode(e.unref(v.X),{class:"h-3 w-3"})],8,j)]),_:2},1024))),128))])):e.createCommentVNode("",!0)]),e.createVNode(e.unref(N.default),{variant:"outline",size:"sm",onClick:e.withModifiers(q,["stop"])},{default:e.withCtx(()=>[...r[1]||(r[1]=[e.createTextVNode(" 초기화 ",-1)])]),_:1})])]),_:1}),e.withDirectives(e.createVNode(e.unref(E.default),{class:"px-6 pb-6 pt-0"},{default:e.withCtx(()=>[e.createVNode(C.default,{ref_key:"dynamicFormRef",ref:d,schema:i.schema,"model-value":s,"onUpdate:modelValue":y,onSubmit:b},null,8,["schema","model-value"])]),_:1},512),[[e.vShow,n.value||!i.collapsible]])]),_:1}))}});exports.default=T;
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue"),v=require("lucide-vue-next");require("../shadcn/index.cjs");const N=require("../atoms/JButton.vue.cjs");require("clsx");require("tailwind-merge");require("@internationalized/date");require("md-editor-v3");;/* empty css */;/* empty css */const V=require("../atoms/JBadge.vue.cjs");require("@vueuse/core");require("reka-ui");;/* empty css */require("../shadcn/avatar-variants.cjs");require("dompurify");;/* empty css */require("ag-grid-vue3");require("ag-grid-community");require("ag-grid-enterprise");;/* empty css */;/* empty css */;/* empty css */require("vue-sonner");const C=require("./JDynamicForm.vue.cjs"),g=require("../shadcn/Card.vue.cjs"),w=require("../shadcn/CardHeader.vue.cjs"),B=require("../shadcn/CardTitle.vue.cjs"),E=require("../shadcn/CardContent.vue.cjs"),S={class:"flex items-center justify-between"},D={class:"flex items-center gap-3 flex-1"},F={key:2,class:"flex flex-wrap items-center gap-2 ml-2"},j=["onClick","aria-label"],T=e.defineComponent({__name:"JSearchPanel",props:{title:{default:"조회조건"},schema:{},modelValue:{},defaultCollapsed:{type:Boolean,default:!1},collapsible:{type:Boolean,default:!0}},emits:["update:modelValue","submit","reset"],setup(i,{emit:_}){const a=i,u=_,d=e.ref(null),n=e.ref(!a.defaultCollapsed),s=e.reactive(a.modelValue?{...a.modelValue}:{});let c=!1;e.watch(()=>a.modelValue,l=>{l&&!c&&(Object.keys(s).forEach(r=>{r in l||delete s[r]}),Object.assign(s,l)),c=!1},{deep:!0,immediate:!0});function y(l){c=!0,Object.assign(s,l),u("update:modelValue",{...l})}const m=e.computed(()=>{if(!a.schema)return[];const l=[];return a.schema.type==="simple"&&a.schema.fields?l.push(...a.schema.fields):a.schema.type==="sectioned"&&a.schema.sections?a.schema.sections.forEach(r=>{r.fields&&l.push(...r.fields)}):a.schema.type==="wizard"&&a.schema.steps&&a.schema.steps.forEach(r=>{r.fields&&l.push(...r.fields)}),l}),f=e.computed(()=>{if(!s||!a.schema)return[];const l=[],r=s;return m.value.forEach(t=>{const o=r[t.controlName];if(o==null||o===""||o==="ALL"||o==="SELECT"||t.type==="checkbox"&&o==="N"||t.type==="switch"&&o==="N")return;let p=String(o);if((t.type==="combo"||t.type==="searchcombo")&&t.options){const h=t.options.find(k=>k.value===o);h&&(p=h.label)}t.type==="checkbox"||t.type==="switch"?o==="Y"&&l.push({fieldName:t.controlName,label:t.label,value:t.inlineLabel||t.label}):l.push({fieldName:t.controlName,label:t.label,value:p})}),l});function b(l){u("submit",l)}function q(){d.value&&(d.value.reset(),u("reset"))}function x(l){if(!s||!a.schema)return;const r=m.value.find(t=>t.controlName===l);r&&(r.type==="checkbox"||r.type==="switch"?s[l]="N":s[l]="",c=!0,u("update:modelValue",{...s}))}return(l,r)=>(e.openBlock(),e.createBlock(e.unref(g.default),{class:"w-full"},{default:e.withCtx(()=>[e.createVNode(e.unref(w.default),{class:"pt-4 pb-3 px-6"},{default:e.withCtx(()=>[e.createElementVNode("div",S,[e.createElementVNode("div",D,[i.collapsible?(e.openBlock(),e.createElementBlock("button",{key:0,type:"button",onClick:r[0]||(r[0]=t=>n.value=!n.value),class:"flex items-center gap-2 font-semibold hover:text-primary transition-colors"},[e.createVNode(e.unref(v.ChevronDown),{class:e.normalizeClass(["h-4 w-4 transition-transform",n.value?"rotate-0":"-rotate-90"])},null,8,["class"]),e.createTextVNode(" "+e.toDisplayString(i.title),1)])):(e.openBlock(),e.createBlock(e.unref(B.default),{key:1,class:"mb-0"},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(i.title),1)]),_:1})),f.value.length>0?(e.openBlock(),e.createElementBlock("div",F,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(f.value,t=>(e.openBlock(),e.createBlock(e.unref(V.default),{key:t.fieldName,variant:"secondary",size:"sm",class:"flex items-center gap-1.5 pr-1"},{default:e.withCtx(()=>[e.createElementVNode("span",null,e.toDisplayString(t.label)+": "+e.toDisplayString(t.value),1),e.createElementVNode("button",{type:"button",onClick:e.withModifiers(o=>x(t.fieldName),["stop"]),class:"h-4 w-4 rounded-full hover:bg-destructive/20 hover:text-destructive transition-colors flex items-center justify-center","aria-label":`${t.label} 조건 제거`},[e.createVNode(e.unref(v.X),{class:"h-3 w-3"})],8,j)]),_:2},1024))),128))])):e.createCommentVNode("",!0)]),e.createVNode(e.unref(N.default),{variant:"outline",size:"sm",onClick:e.withModifiers(q,["stop"])},{default:e.withCtx(()=>[...r[1]||(r[1]=[e.createTextVNode(" 초기화 ",-1)])]),_:1})])]),_:1}),e.withDirectives(e.createVNode(e.unref(E.default),{class:"px-6 pb-6 pt-0"},{default:e.withCtx(()=>[e.createVNode(C.default,{ref_key:"dynamicFormRef",ref:d,schema:i.schema,"model-value":s,"onUpdate:modelValue":y,onSubmit:b},null,8,["schema","model-value"])]),_:1},512),[[e.vShow,n.value||!i.collapsible]])]),_:1}))}});exports.default=T;
2
2
  //# sourceMappingURL=JSearchPanel.vue2.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"JSearchPanel.vue2.cjs","sources":["../../../../src/components/organisms/JSearchPanel.vue"],"sourcesContent":["<template>\n <Card class=\"w-full\">\n <!-- 헤더: 제목, Badge 목록, 초기화 버튼 -->\n <CardHeader class=\"pt-4 pb-3 px-6\">\n <div class=\"flex items-center justify-between\">\n <div class=\"flex items-center gap-3 flex-1\">\n <button\n v-if=\"collapsible\"\n type=\"button\"\n @click=\"isExpanded = !isExpanded\"\n class=\"flex items-center gap-2 font-semibold hover:text-primary transition-colors\"\n >\n <ChevronDown\n :class=\"['h-4 w-4 transition-transform', isExpanded ? 'rotate-0' : '-rotate-90']\"\n />\n {{ title }}\n </button>\n <CardTitle v-else class=\"mb-0\">{{ title }}</CardTitle>\n \n <!-- 조건 Badge 목록 -->\n <div v-if=\"conditionBadges.length > 0\" class=\"flex flex-wrap items-center gap-2 ml-2\">\n <JBadge\n v-for=\"badge in conditionBadges\"\n :key=\"badge.fieldName\"\n variant=\"secondary\"\n size=\"sm\"\n class=\"flex items-center gap-1.5 pr-1\"\n >\n <span>{{ badge.label }}: {{ badge.value }}</span>\n <button\n type=\"button\"\n @click.stop=\"handleFieldReset(badge.fieldName)\"\n class=\"h-4 w-4 rounded-full hover:bg-destructive/20 hover:text-destructive transition-colors flex items-center justify-center\"\n :aria-label=\"`${badge.label} 조건 제거`\"\n >\n <X class=\"h-3 w-3\" />\n </button>\n </JBadge>\n </div>\n </div>\n \n <JButton\n variant=\"outline\"\n size=\"sm\"\n @click.stop=\"handleReset\"\n >\n 초기화\n </JButton>\n </div>\n </CardHeader>\n \n <!-- 폼 내용 -->\n <CardContent v-show=\"isExpanded || !collapsible\" class=\"px-6 pb-6 pt-0\">\n <JDynamicForm\n ref=\"dynamicFormRef\"\n :schema=\"schema\"\n :model-value=\"localModelValue\"\n @update:model-value=\"handleFormValueUpdate\"\n @submit=\"handleSubmit\"\n />\n </CardContent>\n </Card>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref, reactive, watch } from 'vue'\nimport { ChevronDown, X } from 'lucide-vue-next'\nimport { Card, CardHeader, CardTitle, CardContent } from '@/components/shadcn'\nimport { JButton, JBadge } from '@/components/atoms'\nimport JDynamicForm from './JDynamicForm.vue'\nimport type { FormSchema, DynamicFormField } from '@/types/dynamic-form'\nimport type { ComponentPublicInstance } from 'vue'\n\ninterface ConditionBadge {\n fieldName: string\n label: string\n value: string\n}\n\nexport interface JSearchPanelProps {\n /** 패널 제목 */\n title?: string\n /** JDynamicForm에 전달할 FormSchema */\n schema: FormSchema\n /** JDynamicForm의 v-model 값 */\n modelValue?: Record<string, any>\n /** 기본 접힘 상태 */\n defaultCollapsed?: boolean\n /** 접기/펼치기 가능 여부 */\n collapsible?: boolean\n}\n\nconst props = withDefaults(defineProps<JSearchPanelProps>(), {\n title: '조회조건',\n defaultCollapsed: false,\n collapsible: true,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: Record<string, any>]\n 'submit': [values: Record<string, any>]\n 'reset': []\n}>()\n\nconst dynamicFormRef = ref<ComponentPublicInstance & { reset: () => void } | null>(null)\nconst isExpanded = ref(!props.defaultCollapsed)\n\n// 로컬 modelValue - reactive로 관리하여 양방향 바인딩\nconst localModelValue = reactive<Record<string, any>>(props.modelValue ? { ...props.modelValue } : {})\n\n// 내부 변경인지 외부 변경인지 구분하는 플래그\nlet isInternalUpdate = false\n\n// props.modelValue 변경 시 localModelValue 동기화 (외부 변경)\nwatch(\n () => props.modelValue,\n (newValue) => {\n if (newValue && !isInternalUpdate) {\n // 기존 키 중 새 값에 없는 것은 삭제\n Object.keys(localModelValue).forEach(key => {\n if (!(key in newValue)) {\n delete localModelValue[key]\n }\n })\n // 새 값으로 업데이트\n Object.assign(localModelValue, newValue)\n }\n isInternalUpdate = false\n },\n { deep: true, immediate: true }\n)\n\n// JDynamicForm에서 값이 변경되었을 때 처리\nfunction handleFormValueUpdate(value: Record<string, any>) {\n // JDynamicForm에서 emit된 값을 localModelValue에 반영\n isInternalUpdate = true\n Object.assign(localModelValue, value)\n // 상위로 emit\n emit('update:modelValue', { ...value })\n}\n\n// 모든 필드 가져오기\nconst allFields = computed((): DynamicFormField[] => {\n if (!props.schema) return []\n \n const fields: DynamicFormField[] = []\n if (props.schema.type === 'simple' && props.schema.fields) {\n fields.push(...props.schema.fields)\n } else if (props.schema.type === 'sectioned' && props.schema.sections) {\n props.schema.sections.forEach(section => {\n if (section.fields) fields.push(...section.fields)\n })\n } else if (props.schema.type === 'wizard' && props.schema.steps) {\n props.schema.steps.forEach(step => {\n if (step.fields) fields.push(...step.fields)\n })\n }\n return fields\n})\n\n// 조건 Badge 목록 생성\nconst conditionBadges = computed((): ConditionBadge[] => {\n if (!localModelValue || !props.schema) {\n return []\n }\n\n const badges: ConditionBadge[] = []\n const formState = localModelValue\n\n allFields.value.forEach((field) => {\n const value = formState[field.controlName]\n \n // 빈 값 체크 (빈 문자열, null, undefined, 'ALL', 'SELECT' 제외)\n if (\n value === undefined ||\n value === null ||\n value === '' ||\n value === 'ALL' ||\n value === 'SELECT' ||\n (field.type === 'checkbox' && value === 'N') ||\n (field.type === 'switch' && value === 'N')\n ) {\n return\n }\n\n // 필드 타입에 따라 값 표시\n let displayValue = String(value)\n\n // 콤보/검색콤보인 경우 옵션의 label 찾기\n if ((field.type === 'combo' || field.type === 'searchcombo') && field.options) {\n const option = field.options.find(opt => opt.value === value)\n if (option) {\n displayValue = option.label\n }\n }\n\n // 체크박스/스위치는 'Y'일 때만 표시\n if (field.type === 'checkbox' || field.type === 'switch') {\n if (value === 'Y') {\n badges.push({\n fieldName: field.controlName,\n label: field.label,\n value: field.inlineLabel || field.label\n })\n }\n } else {\n badges.push({\n fieldName: field.controlName,\n label: field.label,\n value: displayValue\n })\n }\n })\n\n return badges\n})\n\n// submit 핸들러\nfunction handleSubmit(values: Record<string, any>) {\n emit('submit', values)\n}\n\n// 전체 초기화 핸들러\nfunction handleReset() {\n if (dynamicFormRef.value) {\n dynamicFormRef.value.reset()\n emit('reset')\n }\n}\n\n// 필드별 초기화 핸들러\nfunction handleFieldReset(fieldName: string) {\n if (!localModelValue || !props.schema) return\n \n const field = allFields.value.find(f => f.controlName === fieldName)\n \n if (field) {\n // 필드 타입에 따라 초기값 설정 - reactive 객체 직접 수정\n if (field.type === 'checkbox' || field.type === 'switch') {\n localModelValue[fieldName] = 'N'\n } else {\n localModelValue[fieldName] = ''\n }\n \n // localModelValue 변경 후 상위로 emit하여 JDynamicForm에도 반영\n isInternalUpdate = true\n emit('update:modelValue', { ...localModelValue })\n }\n}\n</script>\n\n<style scoped>\n/* 필요시 스타일 추가 */\n</style>\n\n"],"names":["props","__props","emit","__emit","dynamicFormRef","ref","isExpanded","localModelValue","reactive","isInternalUpdate","watch","newValue","key","handleFormValueUpdate","value","allFields","computed","fields","section","step","conditionBadges","badges","formState","field","displayValue","option","opt","handleSubmit","values","handleReset","handleFieldReset","fieldName","f","_createBlock","_unref","Card","_createVNode","CardHeader","_createElementVNode","_hoisted_1","_hoisted_2","_createElementBlock","_cache","$event","ChevronDown","_createTextVNode","CardTitle","_openBlock","_hoisted_3","_Fragment","_renderList","badge","JBadge","_toDisplayString","_withModifiers","X","JButton","CardContent","JDynamicForm","_vShow"],"mappings":"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"}
1
+ {"version":3,"file":"JSearchPanel.vue2.cjs","sources":["../../../../src/components/organisms/JSearchPanel.vue"],"sourcesContent":["<template>\n <Card class=\"w-full\">\n <!-- 헤더: 제목, Badge 목록, 초기화 버튼 -->\n <CardHeader class=\"pt-4 pb-3 px-6\">\n <div class=\"flex items-center justify-between\">\n <div class=\"flex items-center gap-3 flex-1\">\n <button\n v-if=\"collapsible\"\n type=\"button\"\n @click=\"isExpanded = !isExpanded\"\n class=\"flex items-center gap-2 font-semibold hover:text-primary transition-colors\"\n >\n <ChevronDown\n :class=\"['h-4 w-4 transition-transform', isExpanded ? 'rotate-0' : '-rotate-90']\"\n />\n {{ title }}\n </button>\n <CardTitle v-else class=\"mb-0\">{{ title }}</CardTitle>\n \n <!-- 조건 Badge 목록 -->\n <div v-if=\"conditionBadges.length > 0\" class=\"flex flex-wrap items-center gap-2 ml-2\">\n <JBadge\n v-for=\"badge in conditionBadges\"\n :key=\"badge.fieldName\"\n variant=\"secondary\"\n size=\"sm\"\n class=\"flex items-center gap-1.5 pr-1\"\n >\n <span>{{ badge.label }}: {{ badge.value }}</span>\n <button\n type=\"button\"\n @click.stop=\"handleFieldReset(badge.fieldName)\"\n class=\"h-4 w-4 rounded-full hover:bg-destructive/20 hover:text-destructive transition-colors flex items-center justify-center\"\n :aria-label=\"`${badge.label} 조건 제거`\"\n >\n <X class=\"h-3 w-3\" />\n </button>\n </JBadge>\n </div>\n </div>\n \n <JButton\n variant=\"outline\"\n size=\"sm\"\n @click.stop=\"handleReset\"\n >\n 초기화\n </JButton>\n </div>\n </CardHeader>\n \n <!-- 폼 내용 -->\n <CardContent v-show=\"isExpanded || !collapsible\" class=\"px-6 pb-6 pt-0\">\n <JDynamicForm\n ref=\"dynamicFormRef\"\n :schema=\"schema\"\n :model-value=\"localModelValue\"\n @update:model-value=\"handleFormValueUpdate\"\n @submit=\"handleSubmit\"\n />\n </CardContent>\n </Card>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref, reactive, watch } from 'vue'\nimport { ChevronDown, X } from 'lucide-vue-next'\nimport { Card, CardHeader, CardTitle, CardContent } from '@/components/shadcn'\nimport { JButton, JBadge } from '@/components/atoms'\nimport JDynamicForm from './JDynamicForm.vue'\nimport type { FormSchema, DynamicFormField } from '@/types/dynamic-form'\nimport type { ComponentPublicInstance } from 'vue'\n\ninterface ConditionBadge {\n fieldName: string\n label: string\n value: string\n}\n\nexport interface JSearchPanelProps {\n /** 패널 제목 */\n title?: string\n /** JDynamicForm에 전달할 FormSchema */\n schema: FormSchema\n /** JDynamicForm의 v-model 값 */\n modelValue?: Record<string, any>\n /** 기본 접힘 상태 */\n defaultCollapsed?: boolean\n /** 접기/펼치기 가능 여부 */\n collapsible?: boolean\n}\n\nconst props = withDefaults(defineProps<JSearchPanelProps>(), {\n title: '조회조건',\n defaultCollapsed: false,\n collapsible: true,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: Record<string, any>]\n 'submit': [values: Record<string, any>]\n 'reset': []\n}>()\n\nconst dynamicFormRef = ref<ComponentPublicInstance & { reset: () => void } | null>(null)\nconst isExpanded = ref(!props.defaultCollapsed)\n\n// 로컬 modelValue - reactive로 관리하여 양방향 바인딩\nconst localModelValue = reactive<Record<string, any>>(props.modelValue ? { ...props.modelValue } : {})\n\n// 내부 변경인지 외부 변경인지 구분하는 플래그\nlet isInternalUpdate = false\n\n// props.modelValue 변경 시 localModelValue 동기화 (외부 변경)\nwatch(\n () => props.modelValue,\n (newValue) => {\n if (newValue && !isInternalUpdate) {\n // 기존 키 중 새 값에 없는 것은 삭제\n Object.keys(localModelValue).forEach(key => {\n if (!(key in newValue)) {\n delete localModelValue[key]\n }\n })\n // 새 값으로 업데이트\n Object.assign(localModelValue, newValue)\n }\n isInternalUpdate = false\n },\n { deep: true, immediate: true }\n)\n\n// JDynamicForm에서 값이 변경되었을 때 처리\nfunction handleFormValueUpdate(value: Record<string, any>) {\n // JDynamicForm에서 emit된 값을 localModelValue에 반영\n isInternalUpdate = true\n Object.assign(localModelValue, value)\n // 상위로 emit\n emit('update:modelValue', { ...value })\n}\n\n// 모든 필드 가져오기\nconst allFields = computed((): DynamicFormField[] => {\n if (!props.schema) return []\n \n const fields: DynamicFormField[] = []\n if (props.schema.type === 'simple' && props.schema.fields) {\n fields.push(...props.schema.fields)\n } else if (props.schema.type === 'sectioned' && props.schema.sections) {\n props.schema.sections.forEach(section => {\n if (section.fields) fields.push(...section.fields)\n })\n } else if (props.schema.type === 'wizard' && props.schema.steps) {\n props.schema.steps.forEach(step => {\n if (step.fields) fields.push(...step.fields)\n })\n }\n return fields\n})\n\n// 조건 Badge 목록 생성\nconst conditionBadges = computed((): ConditionBadge[] => {\n if (!localModelValue || !props.schema) {\n return []\n }\n\n const badges: ConditionBadge[] = []\n const formState = localModelValue\n\n allFields.value.forEach((field) => {\n const value = formState[field.controlName]\n \n // 빈 값 체크 (빈 문자열, null, undefined, 'ALL', 'SELECT' 제외)\n if (\n value === undefined ||\n value === null ||\n value === '' ||\n value === 'ALL' ||\n value === 'SELECT' ||\n (field.type === 'checkbox' && value === 'N') ||\n (field.type === 'switch' && value === 'N')\n ) {\n return\n }\n\n // 필드 타입에 따라 값 표시\n let displayValue = String(value)\n\n // 콤보/검색콤보인 경우 옵션의 label 찾기\n if ((field.type === 'combo' || field.type === 'searchcombo') && field.options) {\n const option = field.options.find(opt => opt.value === value)\n if (option) {\n displayValue = option.label\n }\n }\n\n // 체크박스/스위치는 'Y'일 때만 표시\n if (field.type === 'checkbox' || field.type === 'switch') {\n if (value === 'Y') {\n badges.push({\n fieldName: field.controlName,\n label: field.label,\n value: field.inlineLabel || field.label\n })\n }\n } else {\n badges.push({\n fieldName: field.controlName,\n label: field.label,\n value: displayValue\n })\n }\n })\n\n return badges\n})\n\n// submit 핸들러\nfunction handleSubmit(values: Record<string, any>) {\n emit('submit', values)\n}\n\n// 전체 초기화 핸들러\nfunction handleReset() {\n if (dynamicFormRef.value) {\n dynamicFormRef.value.reset()\n emit('reset')\n }\n}\n\n// 필드별 초기화 핸들러\nfunction handleFieldReset(fieldName: string) {\n if (!localModelValue || !props.schema) return\n \n const field = allFields.value.find(f => f.controlName === fieldName)\n \n if (field) {\n // 필드 타입에 따라 초기값 설정 - reactive 객체 직접 수정\n if (field.type === 'checkbox' || field.type === 'switch') {\n localModelValue[fieldName] = 'N'\n } else {\n localModelValue[fieldName] = ''\n }\n \n // localModelValue 변경 후 상위로 emit하여 JDynamicForm에도 반영\n isInternalUpdate = true\n emit('update:modelValue', { ...localModelValue })\n }\n}\n</script>\n\n<style scoped>\n/* 필요시 스타일 추가 */\n</style>\n\n"],"names":["props","__props","emit","__emit","dynamicFormRef","ref","isExpanded","localModelValue","reactive","isInternalUpdate","watch","newValue","key","handleFormValueUpdate","value","allFields","computed","fields","section","step","conditionBadges","badges","formState","field","displayValue","option","opt","handleSubmit","values","handleReset","handleFieldReset","fieldName","f","_createBlock","_unref","Card","_createVNode","CardHeader","_createElementVNode","_hoisted_1","_hoisted_2","_createElementBlock","_cache","$event","ChevronDown","_createTextVNode","CardTitle","_openBlock","_hoisted_3","_Fragment","_renderList","badge","JBadge","_toDisplayString","_withModifiers","X","JButton","CardContent","JDynamicForm","_vShow"],"mappings":"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"}
@@ -18,9 +18,6 @@ import "dompurify";
18
18
  import "ag-grid-vue3";
19
19
  import "ag-grid-community";
20
20
  import "ag-grid-enterprise";
21
- /* empty css */
22
- /* empty css */
23
- /* empty css */
24
21
  /* empty css */
25
22
  /* empty css */
26
23
  /* empty css */
@@ -33,7 +30,7 @@ import K from "../shadcn/CardContent.vue.js";
33
30
  const Q = { class: "flex items-center justify-between" }, W = { class: "flex items-center gap-3 flex-1" }, Z = {
34
31
  key: 2,
35
32
  class: "flex flex-wrap items-center gap-2 ml-2"
36
- }, ee = ["onClick", "aria-label"], Le = /* @__PURE__ */ L({
33
+ }, ee = ["onClick", "aria-label"], Be = /* @__PURE__ */ L({
37
34
  __name: "JSearchPanel",
38
35
  props: {
39
36
  title: { default: "조회조건" },
@@ -44,13 +41,13 @@ const Q = { class: "flex items-center justify-between" }, W = { class: "flex ite
44
41
  },
45
42
  emits: ["update:modelValue", "submit", "reset"],
46
43
  setup(r, { emit: E }) {
47
- const l = r, u = E, b = V(null), p = V(!l.defaultCollapsed), a = z(l.modelValue ? { ...l.modelValue } : {});
44
+ const o = r, u = E, b = V(null), p = V(!o.defaultCollapsed), a = z(o.modelValue ? { ...o.modelValue } : {});
48
45
  let f = !1;
49
46
  D(
50
- () => l.modelValue,
47
+ () => o.modelValue,
51
48
  (t) => {
52
- t && !f && (Object.keys(a).forEach((o) => {
53
- o in t || delete a[o];
49
+ t && !f && (Object.keys(a).forEach((l) => {
50
+ l in t || delete a[l];
54
51
  }), Object.assign(a, t)), f = !1;
55
52
  },
56
53
  { deep: !0, immediate: !0 }
@@ -59,19 +56,19 @@ const Q = { class: "flex items-center justify-between" }, W = { class: "flex ite
59
56
  f = !0, Object.assign(a, t), u("update:modelValue", { ...t });
60
57
  }
61
58
  const _ = C(() => {
62
- if (!l.schema) return [];
59
+ if (!o.schema) return [];
63
60
  const t = [];
64
- return l.schema.type === "simple" && l.schema.fields ? t.push(...l.schema.fields) : l.schema.type === "sectioned" && l.schema.sections ? l.schema.sections.forEach((o) => {
65
- o.fields && t.push(...o.fields);
66
- }) : l.schema.type === "wizard" && l.schema.steps && l.schema.steps.forEach((o) => {
67
- o.fields && t.push(...o.fields);
61
+ return o.schema.type === "simple" && o.schema.fields ? t.push(...o.schema.fields) : o.schema.type === "sectioned" && o.schema.sections ? o.schema.sections.forEach((l) => {
62
+ l.fields && t.push(...l.fields);
63
+ }) : o.schema.type === "wizard" && o.schema.steps && o.schema.steps.forEach((l) => {
64
+ l.fields && t.push(...l.fields);
68
65
  }), t;
69
66
  }), k = C(() => {
70
- if (!a || !l.schema)
67
+ if (!a || !o.schema)
71
68
  return [];
72
- const t = [], o = a;
69
+ const t = [], l = a;
73
70
  return _.value.forEach((e) => {
74
- const s = o[e.controlName];
71
+ const s = l[e.controlName];
75
72
  if (s == null || s === "" || s === "ALL" || s === "SELECT" || e.type === "checkbox" && s === "N" || e.type === "switch" && s === "N")
76
73
  return;
77
74
  let w = String(s);
@@ -97,11 +94,11 @@ const Q = { class: "flex items-center justify-between" }, W = { class: "flex ite
97
94
  b.value && (b.value.reset(), u("reset"));
98
95
  }
99
96
  function F(t) {
100
- if (!a || !l.schema) return;
101
- const o = _.value.find((e) => e.controlName === t);
102
- o && (o.type === "checkbox" || o.type === "switch" ? a[t] = "N" : a[t] = "", f = !0, u("update:modelValue", { ...a }));
97
+ if (!a || !o.schema) return;
98
+ const l = _.value.find((e) => e.controlName === t);
99
+ l && (l.type === "checkbox" || l.type === "switch" ? a[t] = "N" : a[t] = "", f = !0, u("update:modelValue", { ...a }));
103
100
  }
104
- return (t, o) => (n(), v(i(q), { class: "w-full" }, {
101
+ return (t, l) => (n(), v(i(q), { class: "w-full" }, {
105
102
  default: m(() => [
106
103
  c(i(G), { class: "pt-4 pb-3 px-6" }, {
107
104
  default: m(() => [
@@ -110,7 +107,7 @@ const Q = { class: "flex items-center justify-between" }, W = { class: "flex ite
110
107
  r.collapsible ? (n(), y("button", {
111
108
  key: 0,
112
109
  type: "button",
113
- onClick: o[0] || (o[0] = (e) => p.value = !p.value),
110
+ onClick: l[0] || (l[0] = (e) => p.value = !p.value),
114
111
  class: "flex items-center gap-2 font-semibold hover:text-primary transition-colors"
115
112
  }, [
116
113
  c(i(A), {
@@ -153,7 +150,7 @@ const Q = { class: "flex items-center justify-between" }, W = { class: "flex ite
153
150
  size: "sm",
154
151
  onClick: g(B, ["stop"])
155
152
  }, {
156
- default: m(() => [...o[1] || (o[1] = [
153
+ default: m(() => [...l[1] || (l[1] = [
157
154
  x(" 초기화 ", -1)
158
155
  ])]),
159
156
  _: 1
@@ -183,6 +180,6 @@ const Q = { class: "flex items-center justify-between" }, W = { class: "flex ite
183
180
  }
184
181
  });
185
182
  export {
186
- Le as default
183
+ Be as default
187
184
  };
188
185
  //# sourceMappingURL=JSearchPanel.vue2.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"JSearchPanel.vue2.js","sources":["../../../../src/components/organisms/JSearchPanel.vue"],"sourcesContent":["<template>\n <Card class=\"w-full\">\n <!-- 헤더: 제목, Badge 목록, 초기화 버튼 -->\n <CardHeader class=\"pt-4 pb-3 px-6\">\n <div class=\"flex items-center justify-between\">\n <div class=\"flex items-center gap-3 flex-1\">\n <button\n v-if=\"collapsible\"\n type=\"button\"\n @click=\"isExpanded = !isExpanded\"\n class=\"flex items-center gap-2 font-semibold hover:text-primary transition-colors\"\n >\n <ChevronDown\n :class=\"['h-4 w-4 transition-transform', isExpanded ? 'rotate-0' : '-rotate-90']\"\n />\n {{ title }}\n </button>\n <CardTitle v-else class=\"mb-0\">{{ title }}</CardTitle>\n \n <!-- 조건 Badge 목록 -->\n <div v-if=\"conditionBadges.length > 0\" class=\"flex flex-wrap items-center gap-2 ml-2\">\n <JBadge\n v-for=\"badge in conditionBadges\"\n :key=\"badge.fieldName\"\n variant=\"secondary\"\n size=\"sm\"\n class=\"flex items-center gap-1.5 pr-1\"\n >\n <span>{{ badge.label }}: {{ badge.value }}</span>\n <button\n type=\"button\"\n @click.stop=\"handleFieldReset(badge.fieldName)\"\n class=\"h-4 w-4 rounded-full hover:bg-destructive/20 hover:text-destructive transition-colors flex items-center justify-center\"\n :aria-label=\"`${badge.label} 조건 제거`\"\n >\n <X class=\"h-3 w-3\" />\n </button>\n </JBadge>\n </div>\n </div>\n \n <JButton\n variant=\"outline\"\n size=\"sm\"\n @click.stop=\"handleReset\"\n >\n 초기화\n </JButton>\n </div>\n </CardHeader>\n \n <!-- 폼 내용 -->\n <CardContent v-show=\"isExpanded || !collapsible\" class=\"px-6 pb-6 pt-0\">\n <JDynamicForm\n ref=\"dynamicFormRef\"\n :schema=\"schema\"\n :model-value=\"localModelValue\"\n @update:model-value=\"handleFormValueUpdate\"\n @submit=\"handleSubmit\"\n />\n </CardContent>\n </Card>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref, reactive, watch } from 'vue'\nimport { ChevronDown, X } from 'lucide-vue-next'\nimport { Card, CardHeader, CardTitle, CardContent } from '@/components/shadcn'\nimport { JButton, JBadge } from '@/components/atoms'\nimport JDynamicForm from './JDynamicForm.vue'\nimport type { FormSchema, DynamicFormField } from '@/types/dynamic-form'\nimport type { ComponentPublicInstance } from 'vue'\n\ninterface ConditionBadge {\n fieldName: string\n label: string\n value: string\n}\n\nexport interface JSearchPanelProps {\n /** 패널 제목 */\n title?: string\n /** JDynamicForm에 전달할 FormSchema */\n schema: FormSchema\n /** JDynamicForm의 v-model 값 */\n modelValue?: Record<string, any>\n /** 기본 접힘 상태 */\n defaultCollapsed?: boolean\n /** 접기/펼치기 가능 여부 */\n collapsible?: boolean\n}\n\nconst props = withDefaults(defineProps<JSearchPanelProps>(), {\n title: '조회조건',\n defaultCollapsed: false,\n collapsible: true,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: Record<string, any>]\n 'submit': [values: Record<string, any>]\n 'reset': []\n}>()\n\nconst dynamicFormRef = ref<ComponentPublicInstance & { reset: () => void } | null>(null)\nconst isExpanded = ref(!props.defaultCollapsed)\n\n// 로컬 modelValue - reactive로 관리하여 양방향 바인딩\nconst localModelValue = reactive<Record<string, any>>(props.modelValue ? { ...props.modelValue } : {})\n\n// 내부 변경인지 외부 변경인지 구분하는 플래그\nlet isInternalUpdate = false\n\n// props.modelValue 변경 시 localModelValue 동기화 (외부 변경)\nwatch(\n () => props.modelValue,\n (newValue) => {\n if (newValue && !isInternalUpdate) {\n // 기존 키 중 새 값에 없는 것은 삭제\n Object.keys(localModelValue).forEach(key => {\n if (!(key in newValue)) {\n delete localModelValue[key]\n }\n })\n // 새 값으로 업데이트\n Object.assign(localModelValue, newValue)\n }\n isInternalUpdate = false\n },\n { deep: true, immediate: true }\n)\n\n// JDynamicForm에서 값이 변경되었을 때 처리\nfunction handleFormValueUpdate(value: Record<string, any>) {\n // JDynamicForm에서 emit된 값을 localModelValue에 반영\n isInternalUpdate = true\n Object.assign(localModelValue, value)\n // 상위로 emit\n emit('update:modelValue', { ...value })\n}\n\n// 모든 필드 가져오기\nconst allFields = computed((): DynamicFormField[] => {\n if (!props.schema) return []\n \n const fields: DynamicFormField[] = []\n if (props.schema.type === 'simple' && props.schema.fields) {\n fields.push(...props.schema.fields)\n } else if (props.schema.type === 'sectioned' && props.schema.sections) {\n props.schema.sections.forEach(section => {\n if (section.fields) fields.push(...section.fields)\n })\n } else if (props.schema.type === 'wizard' && props.schema.steps) {\n props.schema.steps.forEach(step => {\n if (step.fields) fields.push(...step.fields)\n })\n }\n return fields\n})\n\n// 조건 Badge 목록 생성\nconst conditionBadges = computed((): ConditionBadge[] => {\n if (!localModelValue || !props.schema) {\n return []\n }\n\n const badges: ConditionBadge[] = []\n const formState = localModelValue\n\n allFields.value.forEach((field) => {\n const value = formState[field.controlName]\n \n // 빈 값 체크 (빈 문자열, null, undefined, 'ALL', 'SELECT' 제외)\n if (\n value === undefined ||\n value === null ||\n value === '' ||\n value === 'ALL' ||\n value === 'SELECT' ||\n (field.type === 'checkbox' && value === 'N') ||\n (field.type === 'switch' && value === 'N')\n ) {\n return\n }\n\n // 필드 타입에 따라 값 표시\n let displayValue = String(value)\n\n // 콤보/검색콤보인 경우 옵션의 label 찾기\n if ((field.type === 'combo' || field.type === 'searchcombo') && field.options) {\n const option = field.options.find(opt => opt.value === value)\n if (option) {\n displayValue = option.label\n }\n }\n\n // 체크박스/스위치는 'Y'일 때만 표시\n if (field.type === 'checkbox' || field.type === 'switch') {\n if (value === 'Y') {\n badges.push({\n fieldName: field.controlName,\n label: field.label,\n value: field.inlineLabel || field.label\n })\n }\n } else {\n badges.push({\n fieldName: field.controlName,\n label: field.label,\n value: displayValue\n })\n }\n })\n\n return badges\n})\n\n// submit 핸들러\nfunction handleSubmit(values: Record<string, any>) {\n emit('submit', values)\n}\n\n// 전체 초기화 핸들러\nfunction handleReset() {\n if (dynamicFormRef.value) {\n dynamicFormRef.value.reset()\n emit('reset')\n }\n}\n\n// 필드별 초기화 핸들러\nfunction handleFieldReset(fieldName: string) {\n if (!localModelValue || !props.schema) return\n \n const field = allFields.value.find(f => f.controlName === fieldName)\n \n if (field) {\n // 필드 타입에 따라 초기값 설정 - reactive 객체 직접 수정\n if (field.type === 'checkbox' || field.type === 'switch') {\n localModelValue[fieldName] = 'N'\n } else {\n localModelValue[fieldName] = ''\n }\n \n // localModelValue 변경 후 상위로 emit하여 JDynamicForm에도 반영\n isInternalUpdate = true\n emit('update:modelValue', { ...localModelValue })\n }\n}\n</script>\n\n<style scoped>\n/* 필요시 스타일 추가 */\n</style>\n\n"],"names":["props","__props","emit","__emit","dynamicFormRef","ref","isExpanded","localModelValue","reactive","isInternalUpdate","watch","newValue","key","handleFormValueUpdate","value","allFields","computed","fields","section","step","conditionBadges","badges","formState","field","displayValue","option","opt","handleSubmit","values","handleReset","handleFieldReset","fieldName","f","_createBlock","_unref","Card","_createVNode","CardHeader","_createElementVNode","_hoisted_1","_hoisted_2","_createElementBlock","_cache","$event","ChevronDown","_createTextVNode","CardTitle","_openBlock","_hoisted_3","_Fragment","_renderList","badge","JBadge","_toDisplayString","_withModifiers","X","JButton","CardContent","JDynamicForm","_vShow"],"mappings":"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"}
1
+ {"version":3,"file":"JSearchPanel.vue2.js","sources":["../../../../src/components/organisms/JSearchPanel.vue"],"sourcesContent":["<template>\n <Card class=\"w-full\">\n <!-- 헤더: 제목, Badge 목록, 초기화 버튼 -->\n <CardHeader class=\"pt-4 pb-3 px-6\">\n <div class=\"flex items-center justify-between\">\n <div class=\"flex items-center gap-3 flex-1\">\n <button\n v-if=\"collapsible\"\n type=\"button\"\n @click=\"isExpanded = !isExpanded\"\n class=\"flex items-center gap-2 font-semibold hover:text-primary transition-colors\"\n >\n <ChevronDown\n :class=\"['h-4 w-4 transition-transform', isExpanded ? 'rotate-0' : '-rotate-90']\"\n />\n {{ title }}\n </button>\n <CardTitle v-else class=\"mb-0\">{{ title }}</CardTitle>\n \n <!-- 조건 Badge 목록 -->\n <div v-if=\"conditionBadges.length > 0\" class=\"flex flex-wrap items-center gap-2 ml-2\">\n <JBadge\n v-for=\"badge in conditionBadges\"\n :key=\"badge.fieldName\"\n variant=\"secondary\"\n size=\"sm\"\n class=\"flex items-center gap-1.5 pr-1\"\n >\n <span>{{ badge.label }}: {{ badge.value }}</span>\n <button\n type=\"button\"\n @click.stop=\"handleFieldReset(badge.fieldName)\"\n class=\"h-4 w-4 rounded-full hover:bg-destructive/20 hover:text-destructive transition-colors flex items-center justify-center\"\n :aria-label=\"`${badge.label} 조건 제거`\"\n >\n <X class=\"h-3 w-3\" />\n </button>\n </JBadge>\n </div>\n </div>\n \n <JButton\n variant=\"outline\"\n size=\"sm\"\n @click.stop=\"handleReset\"\n >\n 초기화\n </JButton>\n </div>\n </CardHeader>\n \n <!-- 폼 내용 -->\n <CardContent v-show=\"isExpanded || !collapsible\" class=\"px-6 pb-6 pt-0\">\n <JDynamicForm\n ref=\"dynamicFormRef\"\n :schema=\"schema\"\n :model-value=\"localModelValue\"\n @update:model-value=\"handleFormValueUpdate\"\n @submit=\"handleSubmit\"\n />\n </CardContent>\n </Card>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref, reactive, watch } from 'vue'\nimport { ChevronDown, X } from 'lucide-vue-next'\nimport { Card, CardHeader, CardTitle, CardContent } from '@/components/shadcn'\nimport { JButton, JBadge } from '@/components/atoms'\nimport JDynamicForm from './JDynamicForm.vue'\nimport type { FormSchema, DynamicFormField } from '@/types/dynamic-form'\nimport type { ComponentPublicInstance } from 'vue'\n\ninterface ConditionBadge {\n fieldName: string\n label: string\n value: string\n}\n\nexport interface JSearchPanelProps {\n /** 패널 제목 */\n title?: string\n /** JDynamicForm에 전달할 FormSchema */\n schema: FormSchema\n /** JDynamicForm의 v-model 값 */\n modelValue?: Record<string, any>\n /** 기본 접힘 상태 */\n defaultCollapsed?: boolean\n /** 접기/펼치기 가능 여부 */\n collapsible?: boolean\n}\n\nconst props = withDefaults(defineProps<JSearchPanelProps>(), {\n title: '조회조건',\n defaultCollapsed: false,\n collapsible: true,\n})\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: Record<string, any>]\n 'submit': [values: Record<string, any>]\n 'reset': []\n}>()\n\nconst dynamicFormRef = ref<ComponentPublicInstance & { reset: () => void } | null>(null)\nconst isExpanded = ref(!props.defaultCollapsed)\n\n// 로컬 modelValue - reactive로 관리하여 양방향 바인딩\nconst localModelValue = reactive<Record<string, any>>(props.modelValue ? { ...props.modelValue } : {})\n\n// 내부 변경인지 외부 변경인지 구분하는 플래그\nlet isInternalUpdate = false\n\n// props.modelValue 변경 시 localModelValue 동기화 (외부 변경)\nwatch(\n () => props.modelValue,\n (newValue) => {\n if (newValue && !isInternalUpdate) {\n // 기존 키 중 새 값에 없는 것은 삭제\n Object.keys(localModelValue).forEach(key => {\n if (!(key in newValue)) {\n delete localModelValue[key]\n }\n })\n // 새 값으로 업데이트\n Object.assign(localModelValue, newValue)\n }\n isInternalUpdate = false\n },\n { deep: true, immediate: true }\n)\n\n// JDynamicForm에서 값이 변경되었을 때 처리\nfunction handleFormValueUpdate(value: Record<string, any>) {\n // JDynamicForm에서 emit된 값을 localModelValue에 반영\n isInternalUpdate = true\n Object.assign(localModelValue, value)\n // 상위로 emit\n emit('update:modelValue', { ...value })\n}\n\n// 모든 필드 가져오기\nconst allFields = computed((): DynamicFormField[] => {\n if (!props.schema) return []\n \n const fields: DynamicFormField[] = []\n if (props.schema.type === 'simple' && props.schema.fields) {\n fields.push(...props.schema.fields)\n } else if (props.schema.type === 'sectioned' && props.schema.sections) {\n props.schema.sections.forEach(section => {\n if (section.fields) fields.push(...section.fields)\n })\n } else if (props.schema.type === 'wizard' && props.schema.steps) {\n props.schema.steps.forEach(step => {\n if (step.fields) fields.push(...step.fields)\n })\n }\n return fields\n})\n\n// 조건 Badge 목록 생성\nconst conditionBadges = computed((): ConditionBadge[] => {\n if (!localModelValue || !props.schema) {\n return []\n }\n\n const badges: ConditionBadge[] = []\n const formState = localModelValue\n\n allFields.value.forEach((field) => {\n const value = formState[field.controlName]\n \n // 빈 값 체크 (빈 문자열, null, undefined, 'ALL', 'SELECT' 제외)\n if (\n value === undefined ||\n value === null ||\n value === '' ||\n value === 'ALL' ||\n value === 'SELECT' ||\n (field.type === 'checkbox' && value === 'N') ||\n (field.type === 'switch' && value === 'N')\n ) {\n return\n }\n\n // 필드 타입에 따라 값 표시\n let displayValue = String(value)\n\n // 콤보/검색콤보인 경우 옵션의 label 찾기\n if ((field.type === 'combo' || field.type === 'searchcombo') && field.options) {\n const option = field.options.find(opt => opt.value === value)\n if (option) {\n displayValue = option.label\n }\n }\n\n // 체크박스/스위치는 'Y'일 때만 표시\n if (field.type === 'checkbox' || field.type === 'switch') {\n if (value === 'Y') {\n badges.push({\n fieldName: field.controlName,\n label: field.label,\n value: field.inlineLabel || field.label\n })\n }\n } else {\n badges.push({\n fieldName: field.controlName,\n label: field.label,\n value: displayValue\n })\n }\n })\n\n return badges\n})\n\n// submit 핸들러\nfunction handleSubmit(values: Record<string, any>) {\n emit('submit', values)\n}\n\n// 전체 초기화 핸들러\nfunction handleReset() {\n if (dynamicFormRef.value) {\n dynamicFormRef.value.reset()\n emit('reset')\n }\n}\n\n// 필드별 초기화 핸들러\nfunction handleFieldReset(fieldName: string) {\n if (!localModelValue || !props.schema) return\n \n const field = allFields.value.find(f => f.controlName === fieldName)\n \n if (field) {\n // 필드 타입에 따라 초기값 설정 - reactive 객체 직접 수정\n if (field.type === 'checkbox' || field.type === 'switch') {\n localModelValue[fieldName] = 'N'\n } else {\n localModelValue[fieldName] = ''\n }\n \n // localModelValue 변경 후 상위로 emit하여 JDynamicForm에도 반영\n isInternalUpdate = true\n emit('update:modelValue', { ...localModelValue })\n }\n}\n</script>\n\n<style scoped>\n/* 필요시 스타일 추가 */\n</style>\n\n"],"names":["props","__props","emit","__emit","dynamicFormRef","ref","isExpanded","localModelValue","reactive","isInternalUpdate","watch","newValue","key","handleFormValueUpdate","value","allFields","computed","fields","section","step","conditionBadges","badges","formState","field","displayValue","option","opt","handleSubmit","values","handleReset","handleFieldReset","fieldName","f","_createBlock","_unref","Card","_createVNode","CardHeader","_createElementVNode","_hoisted_1","_hoisted_2","_createElementBlock","_cache","$event","ChevronDown","_createTextVNode","CardTitle","_openBlock","_hoisted_3","_Fragment","_renderList","badge","JBadge","_toDisplayString","_withModifiers","X","JButton","CardContent","JDynamicForm","_vShow"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue"),C=require("../../../types/sidebar.types.cjs"),u=require("../../atoms/JIcon.vue.cjs"),S=require("../../atoms/JInput.vue.cjs"),F=require("./JSidebarGroup.vue.cjs"),h=require("./JSidebarItem.vue.cjs"),x=require("../../../lib/utils.cjs"),L={key:0,class:"p-2 flex-shrink-0"},I={class:"relative"},q={class:"flex-1 overflow-y-auto overflow-x-hidden px-1 py-1"},z={class:"text-[10px] text-yellow-500/60"},J={class:"overflow-hidden"},K={class:"flex justify-center py-1"},M={key:1,class:"text-center py-6 text-muted-foreground text-xs"},P={class:"flex-shrink-0 border-t border-border p-1"},O={key:0},T=e.defineComponent({__name:"JSidebar",props:{items:{},collapsed:{type:Boolean,default:!1},activePath:{},width:{default:"220px"},collapsedWidth:{default:"56px"},storageKey:{},showSearch:{type:Boolean,default:!0},showFavorites:{type:Boolean,default:!0}},emits:["update:collapsed","menu-click"],setup(n,{emit:_}){const r=n,k=_,a=e.ref(new Set(B()));function B(){if(!r.storageKey)return[];try{const t=localStorage.getItem(r.storageKey);return t?JSON.parse(t):[]}catch{return[]}}function w(){r.storageKey&&localStorage.setItem(r.storageKey,JSON.stringify([...a.value]))}function b(t){a.value.has(t)?a.value.delete(t):a.value.add(t),a.value=new Set(a.value),w()}const i=e.reactive({collapsed:r.collapsed,activePath:r.activePath,favorites:a.value,toggleFavorite:b});e.watch(()=>r.collapsed,t=>{i.collapsed=t}),e.watch(()=>r.activePath,t=>{i.activePath=t}),e.watch(a,t=>{i.favorites=t}),e.provide(C.SIDEBAR_INJECTION_KEY,i);const c=e.ref(""),y=e.computed(()=>{if(!r.storageKey||a.value.size===0)return[];const t=[],l=o=>{for(const s of o)s.menuType==="L"&&a.value.has(s.id)&&t.push(s),s.children&&l(s.children)};return l(r.items),t}),g=e.computed(()=>{const t=c.value.trim().toLowerCase();if(!t)return r.items;const l=o=>{const s=[];for(const p of o){const V=p.label.toLowerCase().includes(t),v=p.children?l(p.children):void 0;(V||v&&v.length>0)&&s.push({...p,children:v})}return s};return l(r.items)}),d=e.computed(()=>{const t=c.value.trim().toLowerCase();return t?y.value.filter(l=>l.label.toLowerCase().includes(t)):y.value}),m=e.ref(!0),f=(t,l)=>{k("menu-click",t,l)},E=()=>{k("update:collapsed",!r.collapsed)},N=e.computed(()=>r.collapsed?r.collapsedWidth:r.width);return(t,l)=>(e.openBlock(),e.createElementBlock("aside",{class:"h-full bg-background border-r border-border flex flex-col flex-shrink-0 overflow-hidden transition-[width] duration-200 ease-out",style:e.normalizeStyle({width:N.value})},[!n.collapsed&&n.showSearch?(e.openBlock(),e.createElementBlock("div",L,[e.createElementVNode("div",I,[e.createVNode(u.default,{name:"search",size:"sm",class:"absolute left-2 top-1/2 -translate-y-1/2 text-muted-foreground"}),e.createVNode(S.default,{modelValue:c.value,"onUpdate:modelValue":l[0]||(l[0]=o=>c.value=o),placeholder:"메뉴 검색...",class:"pl-8 h-7 text-xs"},null,8,["modelValue"])])])):e.createCommentVNode("",!0),e.createElementVNode("nav",q,[n.showFavorites&&n.storageKey&&d.value.length>0?(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[n.collapsed?(e.openBlock(),e.createElementBlock(e.Fragment,{key:1},[e.createElementVNode("div",K,[e.createVNode(u.default,{name:"star",size:"sm",class:"text-yellow-500"})]),(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(d.value,o=>(e.openBlock(),e.createBlock(h.default,{key:"fav-c-"+o.id,item:o,onMenuClick:f},null,8,["item"]))),128))],64)):(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[e.createElementVNode("button",{class:"flex items-center gap-1.5 w-full px-2 py-1.5 text-xs font-semibold text-yellow-600 cursor-pointer select-none transition-colors hover:text-yellow-700",onClick:l[1]||(l[1]=o=>m.value=!m.value)},[e.createVNode(u.default,{name:"chevronRight",size:"sm",class:e.normalizeClass(e.unref(x.cn)("flex-shrink-0 transition-transform duration-200",m.value&&"rotate-90"))},null,8,["class"]),e.createVNode(u.default,{name:"star",size:"sm",class:"flex-shrink-0 text-yellow-500"}),l[2]||(l[2]=e.createElementVNode("span",{class:"flex-1 text-left"},"즐겨찾기",-1)),e.createElementVNode("span",z,e.toDisplayString(d.value.length),1)]),e.createElementVNode("div",{class:e.normalizeClass(e.unref(x.cn)("grid transition-[grid-template-rows] duration-200 ease-out",m.value?"grid-rows-[1fr]":"grid-rows-[0fr]"))},[e.createElementVNode("div",J,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(d.value,o=>(e.openBlock(),e.createBlock(h.default,{key:"fav-"+o.id,item:o,onMenuClick:f},null,8,["item"]))),128))])],2)],64)),l[3]||(l[3]=e.createElementVNode("div",{class:"h-px bg-border mx-2 my-1"},null,-1))],64)):e.createCommentVNode("",!0),(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(g.value,o=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:o.id},[o.menuType==="F"?(e.openBlock(),e.createBlock(F.default,{key:0,item:o,onMenuClick:f},null,8,["item"])):o.menuType==="L"?(e.openBlock(),e.createBlock(h.default,{key:1,item:o,onMenuClick:f},null,8,["item"])):e.createCommentVNode("",!0)],64))),128)),g.value.length===0&&c.value.trim()?(e.openBlock(),e.createElementBlock("div",M," 검색 결과가 없습니다. ")):e.createCommentVNode("",!0)]),e.createElementVNode("div",P,[e.createElementVNode("button",{class:"flex items-center gap-2 w-full px-2 py-1 rounded-md text-xs text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors",onClick:E},[e.createVNode(u.default,{name:n.collapsed?"panelLeftOpen":"panelLeftClose",size:"sm"},null,8,["name"]),n.collapsed?e.createCommentVNode("",!0):(e.openBlock(),e.createElementBlock("span",O,"접기"))])])],4))}});exports.default=T;
2
+ //# sourceMappingURL=JSidebar.vue.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JSidebar.vue.cjs","sources":["../../../../../src/components/organisms/JSidebar/JSidebar.vue"],"sourcesContent":["<script setup lang=\"ts\">\r\nimport { ref, computed, provide, watch, reactive } from 'vue'\r\nimport type { SidebarMenuItem, SidebarState } from '@/types/sidebar.types'\r\nimport { SIDEBAR_INJECTION_KEY } from '@/types/sidebar.types'\r\nimport JIcon from '@/components/atoms/JIcon.vue'\r\nimport JInput from '@/components/atoms/JInput.vue'\r\nimport JSidebarGroup from './JSidebarGroup.vue'\r\nimport JSidebarItem from './JSidebarItem.vue'\r\nimport { cn } from '@/lib/utils'\r\n\r\n/**\r\n * JSidebar - 통합 사이드바 컴포넌트\r\n *\r\n * DB 트리 데이터 기반, provide/inject로 상태 공유.\r\n * collapsed 모드, 검색, 즐겨찾기(localStorage) 지원.\r\n * 라우터를 모름 — @menu-click으로 아이템을 올려보냄.\r\n */\r\n\r\nconst props = withDefaults(\r\n defineProps<{\r\n /** 메뉴 트리 데이터 */\r\n items: SidebarMenuItem[]\r\n /** 접힘 상태 (v-model:collapsed) */\r\n collapsed?: boolean\r\n /** 현재 활성 경로 */\r\n activePath?: string\r\n /** 펼침 너비 */\r\n width?: string\r\n /** 접힘 너비 */\r\n collapsedWidth?: string\r\n /** 즐겨찾기 localStorage 키 (없으면 즐겨찾기 비활성) */\r\n storageKey?: string\r\n /** 검색 표시 여부 */\r\n showSearch?: boolean\r\n /** 즐겨찾기 섹션 표시 여부 */\r\n showFavorites?: boolean\r\n }>(),\r\n {\r\n collapsed: false,\r\n width: '220px',\r\n collapsedWidth: '56px',\r\n showSearch: true,\r\n showFavorites: true,\r\n },\r\n)\r\n\r\nconst emit = defineEmits<{\r\n 'update:collapsed': [value: boolean]\r\n 'menu-click': [item: SidebarMenuItem, event: MouseEvent]\r\n}>()\r\n\r\n// ── 즐겨찾기 (localStorage) ──\r\nconst favorites = ref<Set<string>>(new Set(loadFavorites()))\r\n\r\nfunction loadFavorites(): string[] {\r\n if (!props.storageKey) return []\r\n try {\r\n const raw = localStorage.getItem(props.storageKey)\r\n return raw ? JSON.parse(raw) : []\r\n } catch {\r\n return []\r\n }\r\n}\r\n\r\nfunction saveFavorites() {\r\n if (!props.storageKey) return\r\n localStorage.setItem(props.storageKey, JSON.stringify([...favorites.value]))\r\n}\r\n\r\nfunction toggleFavorite(id: string) {\r\n if (favorites.value.has(id)) {\r\n favorites.value.delete(id)\r\n } else {\r\n favorites.value.add(id)\r\n }\r\n favorites.value = new Set(favorites.value) // trigger reactivity\r\n saveFavorites()\r\n}\r\n\r\n// ── provide/inject 상태 ──\r\nconst sidebarState = reactive<SidebarState>({\r\n collapsed: props.collapsed,\r\n activePath: props.activePath,\r\n favorites: favorites.value,\r\n toggleFavorite,\r\n})\r\n\r\n// props 변경 시 state 동기화\r\nwatch(() => props.collapsed, (v) => { sidebarState.collapsed = v })\r\nwatch(() => props.activePath, (v) => { sidebarState.activePath = v })\r\nwatch(favorites, (v) => { sidebarState.favorites = v })\r\n\r\nprovide(SIDEBAR_INJECTION_KEY, sidebarState)\r\n\r\n// ── 검색 ──\r\nconst searchQuery = ref('')\r\n\r\n/** 즐겨찾기 아이템 (L 타입만, 트리 평탄화) */\r\nconst favoriteItems = computed(() => {\r\n if (!props.storageKey || favorites.value.size === 0) return []\r\n const result: SidebarMenuItem[] = []\r\n const flatten = (items: SidebarMenuItem[]) => {\r\n for (const item of items) {\r\n if (item.menuType === 'L' && favorites.value.has(item.id)) {\r\n result.push(item)\r\n }\r\n if (item.children) flatten(item.children)\r\n }\r\n }\r\n flatten(props.items)\r\n return result\r\n})\r\n\r\n/** 검색 필터링 (재귀) */\r\nconst filteredItems = computed(() => {\r\n const q = searchQuery.value.trim().toLowerCase()\r\n if (!q) return props.items\r\n\r\n const filter = (items: SidebarMenuItem[]): SidebarMenuItem[] => {\r\n const result: SidebarMenuItem[] = []\r\n for (const item of items) {\r\n const matchLabel = item.label.toLowerCase().includes(q)\r\n const filteredChildren = item.children ? filter(item.children) : undefined\r\n if (matchLabel || (filteredChildren && filteredChildren.length > 0)) {\r\n result.push({ ...item, children: filteredChildren })\r\n }\r\n }\r\n return result\r\n }\r\n return filter(props.items)\r\n})\r\n\r\n/** 즐겨찾기 검색 필터링 */\r\nconst filteredFavorites = computed(() => {\r\n const q = searchQuery.value.trim().toLowerCase()\r\n if (!q) return favoriteItems.value\r\n return favoriteItems.value.filter(item => item.label.toLowerCase().includes(q))\r\n})\r\n\r\n// ── 즐겨찾기 그룹 펼침 ──\r\nconst favoritesExpanded = ref(true)\r\n\r\n// ── 이벤트 핸들러 ──\r\nconst handleMenuClick = (item: SidebarMenuItem, event: MouseEvent) => {\r\n emit('menu-click', item, event)\r\n}\r\n\r\nconst toggleCollapsed = () => {\r\n emit('update:collapsed', !props.collapsed)\r\n}\r\n\r\n// ── 사이드바 너비 ──\r\nconst sidebarWidth = computed(() => props.collapsed ? props.collapsedWidth : props.width)\r\n</script>\r\n\r\n<template>\r\n <aside\r\n class=\"h-full bg-background border-r border-border flex flex-col flex-shrink-0 overflow-hidden transition-[width] duration-200 ease-out\"\r\n :style=\"{ width: sidebarWidth }\"\r\n >\r\n <!-- 검색바 (펼침 + showSearch) -->\r\n <div v-if=\"!collapsed && showSearch\" class=\"p-2 flex-shrink-0\">\r\n <div class=\"relative\">\r\n <JIcon\r\n name=\"search\"\r\n size=\"sm\"\r\n class=\"absolute left-2 top-1/2 -translate-y-1/2 text-muted-foreground\"\r\n />\r\n <JInput\r\n v-model=\"searchQuery\"\r\n placeholder=\"메뉴 검색...\"\r\n class=\"pl-8 h-7 text-xs\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <!-- 메뉴 영역 -->\r\n <nav class=\"flex-1 overflow-y-auto overflow-x-hidden px-1 py-1\">\r\n <!-- 즐겨찾기 섹션 -->\r\n <template v-if=\"showFavorites && storageKey && filteredFavorites.length > 0\">\r\n <!-- 펼침: 즐겨찾기 그룹 -->\r\n <template v-if=\"!collapsed\">\r\n <button\r\n class=\"flex items-center gap-1.5 w-full px-2 py-1.5 text-xs font-semibold text-yellow-600 cursor-pointer select-none transition-colors hover:text-yellow-700\"\r\n @click=\"favoritesExpanded = !favoritesExpanded\"\r\n >\r\n <JIcon\r\n name=\"chevronRight\"\r\n size=\"sm\"\r\n :class=\"cn(\r\n 'flex-shrink-0 transition-transform duration-200',\r\n favoritesExpanded && 'rotate-90'\r\n )\"\r\n />\r\n <JIcon name=\"star\" size=\"sm\" class=\"flex-shrink-0 text-yellow-500\" />\r\n <span class=\"flex-1 text-left\">즐겨찾기</span>\r\n <span class=\"text-[10px] text-yellow-500/60\">{{ filteredFavorites.length }}</span>\r\n </button>\r\n <div\r\n :class=\"cn(\r\n 'grid transition-[grid-template-rows] duration-200 ease-out',\r\n favoritesExpanded ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'\r\n )\"\r\n >\r\n <div class=\"overflow-hidden\">\r\n <JSidebarItem\r\n v-for=\"fav in filteredFavorites\"\r\n :key=\"'fav-' + fav.id\"\r\n :item=\"fav\"\r\n @menu-click=\"handleMenuClick\"\r\n />\r\n </div>\r\n </div>\r\n </template>\r\n <!-- 접힘: 별 아이콘 + 구분선 -->\r\n <template v-else>\r\n <div class=\"flex justify-center py-1\">\r\n <JIcon name=\"star\" size=\"sm\" class=\"text-yellow-500\" />\r\n </div>\r\n <JSidebarItem\r\n v-for=\"fav in filteredFavorites\"\r\n :key=\"'fav-c-' + fav.id\"\r\n :item=\"fav\"\r\n @menu-click=\"handleMenuClick\"\r\n />\r\n </template>\r\n <div class=\"h-px bg-border mx-2 my-1\" />\r\n </template>\r\n\r\n <!-- 메인 메뉴 -->\r\n <template v-for=\"item in filteredItems\" :key=\"item.id\">\r\n <JSidebarGroup\r\n v-if=\"item.menuType === 'F'\"\r\n :item=\"item\"\r\n @menu-click=\"handleMenuClick\"\r\n />\r\n <JSidebarItem\r\n v-else-if=\"item.menuType === 'L'\"\r\n :item=\"item\"\r\n @menu-click=\"handleMenuClick\"\r\n />\r\n </template>\r\n\r\n <!-- 검색 결과 없음 -->\r\n <div\r\n v-if=\"filteredItems.length === 0 && searchQuery.trim()\"\r\n class=\"text-center py-6 text-muted-foreground text-xs\"\r\n >\r\n 검색 결과가 없습니다.\r\n </div>\r\n </nav>\r\n\r\n <!-- 하단: collapse 토글 -->\r\n <div class=\"flex-shrink-0 border-t border-border p-1\">\r\n <button\r\n class=\"flex items-center gap-2 w-full px-2 py-1 rounded-md text-xs text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors\"\r\n @click=\"toggleCollapsed\"\r\n >\r\n <JIcon\r\n :name=\"collapsed ? 'panelLeftOpen' : 'panelLeftClose'\"\r\n size=\"sm\"\r\n />\r\n <span v-if=\"!collapsed\">접기</span>\r\n </button>\r\n </div>\r\n </aside>\r\n</template>\r\n"],"names":["props","__props","emit","__emit","favorites","ref","loadFavorites","raw","saveFavorites","toggleFavorite","id","sidebarState","reactive","watch","v","provide","SIDEBAR_INJECTION_KEY","searchQuery","favoriteItems","computed","result","flatten","items","item","filteredItems","q","filter","matchLabel","filteredChildren","filteredFavorites","favoritesExpanded","handleMenuClick","event","toggleCollapsed","sidebarWidth","_createElementBlock","_openBlock","_hoisted_1","_createElementVNode","_hoisted_2","_createVNode","JIcon","JInput","$event","_hoisted_3","_Fragment","_hoisted_6","_renderList","fav","_createBlock","JSidebarItem","_cache","_unref","cn","_hoisted_4","_toDisplayString","_hoisted_5","JSidebarGroup","_hoisted_7","_hoisted_8"],"mappings":"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"}
@@ -0,0 +1,189 @@
1
+ import { defineComponent as T, ref as C, reactive as W, watch as S, provide as q, computed as w, createElementBlock as r, openBlock as s, normalizeStyle as D, createCommentVNode as m, createElementVNode as a, createVNode as d, Fragment as u, normalizeClass as B, unref as I, toDisplayString as R, renderList as _, createBlock as k } from "vue";
2
+ import { SIDEBAR_INJECTION_KEY as j } from "../../../types/sidebar.types.js";
3
+ import v from "../../atoms/JIcon.vue.js";
4
+ import A from "../../atoms/JInput.vue.js";
5
+ import Q from "./JSidebarGroup.vue.js";
6
+ import L from "./JSidebarItem.vue.js";
7
+ import { cn as N } from "../../../lib/utils.js";
8
+ const U = {
9
+ key: 0,
10
+ class: "p-2 flex-shrink-0"
11
+ }, Y = { class: "relative" }, G = { class: "flex-1 overflow-y-auto overflow-x-hidden px-1 py-1" }, H = { class: "text-[10px] text-yellow-500/60" }, X = { class: "overflow-hidden" }, Z = { class: "flex justify-center py-1" }, ee = {
12
+ key: 1,
13
+ class: "text-center py-6 text-muted-foreground text-xs"
14
+ }, te = { class: "flex-shrink-0 border-t border-border p-1" }, le = { key: 0 }, ue = /* @__PURE__ */ T({
15
+ __name: "JSidebar",
16
+ props: {
17
+ items: {},
18
+ collapsed: { type: Boolean, default: !1 },
19
+ activePath: {},
20
+ width: { default: "220px" },
21
+ collapsedWidth: { default: "56px" },
22
+ storageKey: {},
23
+ showSearch: { type: Boolean, default: !0 },
24
+ showFavorites: { type: Boolean, default: !0 }
25
+ },
26
+ emits: ["update:collapsed", "menu-click"],
27
+ setup(i, { emit: E }) {
28
+ const o = i, z = E, n = C(new Set(V()));
29
+ function V() {
30
+ if (!o.storageKey) return [];
31
+ try {
32
+ const e = localStorage.getItem(o.storageKey);
33
+ return e ? JSON.parse(e) : [];
34
+ } catch {
35
+ return [];
36
+ }
37
+ }
38
+ function M() {
39
+ o.storageKey && localStorage.setItem(o.storageKey, JSON.stringify([...n.value]));
40
+ }
41
+ function P(e) {
42
+ n.value.has(e) ? n.value.delete(e) : n.value.add(e), n.value = new Set(n.value), M();
43
+ }
44
+ const p = W({
45
+ collapsed: o.collapsed,
46
+ activePath: o.activePath,
47
+ favorites: n.value,
48
+ toggleFavorite: P
49
+ });
50
+ S(() => o.collapsed, (e) => {
51
+ p.collapsed = e;
52
+ }), S(() => o.activePath, (e) => {
53
+ p.activePath = e;
54
+ }), S(n, (e) => {
55
+ p.favorites = e;
56
+ }), q(j, p);
57
+ const f = C(""), F = w(() => {
58
+ if (!o.storageKey || n.value.size === 0) return [];
59
+ const e = [], t = (l) => {
60
+ for (const c of l)
61
+ c.menuType === "L" && n.value.has(c.id) && e.push(c), c.children && t(c.children);
62
+ };
63
+ return t(o.items), e;
64
+ }), K = w(() => {
65
+ const e = f.value.trim().toLowerCase();
66
+ if (!e) return o.items;
67
+ const t = (l) => {
68
+ const c = [];
69
+ for (const g of l) {
70
+ const O = g.label.toLowerCase().includes(e), b = g.children ? t(g.children) : void 0;
71
+ (O || b && b.length > 0) && c.push({ ...g, children: b });
72
+ }
73
+ return c;
74
+ };
75
+ return t(o.items);
76
+ }), h = w(() => {
77
+ const e = f.value.trim().toLowerCase();
78
+ return e ? F.value.filter((t) => t.label.toLowerCase().includes(e)) : F.value;
79
+ }), x = C(!0), y = (e, t) => {
80
+ z("menu-click", e, t);
81
+ }, $ = () => {
82
+ z("update:collapsed", !o.collapsed);
83
+ }, J = w(() => o.collapsed ? o.collapsedWidth : o.width);
84
+ return (e, t) => (s(), r("aside", {
85
+ class: "h-full bg-background border-r border-border flex flex-col flex-shrink-0 overflow-hidden transition-[width] duration-200 ease-out",
86
+ style: D({ width: J.value })
87
+ }, [
88
+ !i.collapsed && i.showSearch ? (s(), r("div", U, [
89
+ a("div", Y, [
90
+ d(v, {
91
+ name: "search",
92
+ size: "sm",
93
+ class: "absolute left-2 top-1/2 -translate-y-1/2 text-muted-foreground"
94
+ }),
95
+ d(A, {
96
+ modelValue: f.value,
97
+ "onUpdate:modelValue": t[0] || (t[0] = (l) => f.value = l),
98
+ placeholder: "메뉴 검색...",
99
+ class: "pl-8 h-7 text-xs"
100
+ }, null, 8, ["modelValue"])
101
+ ])
102
+ ])) : m("", !0),
103
+ a("nav", G, [
104
+ i.showFavorites && i.storageKey && h.value.length > 0 ? (s(), r(u, { key: 0 }, [
105
+ i.collapsed ? (s(), r(u, { key: 1 }, [
106
+ a("div", Z, [
107
+ d(v, {
108
+ name: "star",
109
+ size: "sm",
110
+ class: "text-yellow-500"
111
+ })
112
+ ]),
113
+ (s(!0), r(u, null, _(h.value, (l) => (s(), k(L, {
114
+ key: "fav-c-" + l.id,
115
+ item: l,
116
+ onMenuClick: y
117
+ }, null, 8, ["item"]))), 128))
118
+ ], 64)) : (s(), r(u, { key: 0 }, [
119
+ a("button", {
120
+ class: "flex items-center gap-1.5 w-full px-2 py-1.5 text-xs font-semibold text-yellow-600 cursor-pointer select-none transition-colors hover:text-yellow-700",
121
+ onClick: t[1] || (t[1] = (l) => x.value = !x.value)
122
+ }, [
123
+ d(v, {
124
+ name: "chevronRight",
125
+ size: "sm",
126
+ class: B(I(N)(
127
+ "flex-shrink-0 transition-transform duration-200",
128
+ x.value && "rotate-90"
129
+ ))
130
+ }, null, 8, ["class"]),
131
+ d(v, {
132
+ name: "star",
133
+ size: "sm",
134
+ class: "flex-shrink-0 text-yellow-500"
135
+ }),
136
+ t[2] || (t[2] = a("span", { class: "flex-1 text-left" }, "즐겨찾기", -1)),
137
+ a("span", H, R(h.value.length), 1)
138
+ ]),
139
+ a("div", {
140
+ class: B(I(N)(
141
+ "grid transition-[grid-template-rows] duration-200 ease-out",
142
+ x.value ? "grid-rows-[1fr]" : "grid-rows-[0fr]"
143
+ ))
144
+ }, [
145
+ a("div", X, [
146
+ (s(!0), r(u, null, _(h.value, (l) => (s(), k(L, {
147
+ key: "fav-" + l.id,
148
+ item: l,
149
+ onMenuClick: y
150
+ }, null, 8, ["item"]))), 128))
151
+ ])
152
+ ], 2)
153
+ ], 64)),
154
+ t[3] || (t[3] = a("div", { class: "h-px bg-border mx-2 my-1" }, null, -1))
155
+ ], 64)) : m("", !0),
156
+ (s(!0), r(u, null, _(K.value, (l) => (s(), r(u, {
157
+ key: l.id
158
+ }, [
159
+ l.menuType === "F" ? (s(), k(Q, {
160
+ key: 0,
161
+ item: l,
162
+ onMenuClick: y
163
+ }, null, 8, ["item"])) : l.menuType === "L" ? (s(), k(L, {
164
+ key: 1,
165
+ item: l,
166
+ onMenuClick: y
167
+ }, null, 8, ["item"])) : m("", !0)
168
+ ], 64))), 128)),
169
+ K.value.length === 0 && f.value.trim() ? (s(), r("div", ee, " 검색 결과가 없습니다. ")) : m("", !0)
170
+ ]),
171
+ a("div", te, [
172
+ a("button", {
173
+ class: "flex items-center gap-2 w-full px-2 py-1 rounded-md text-xs text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors",
174
+ onClick: $
175
+ }, [
176
+ d(v, {
177
+ name: i.collapsed ? "panelLeftOpen" : "panelLeftClose",
178
+ size: "sm"
179
+ }, null, 8, ["name"]),
180
+ i.collapsed ? m("", !0) : (s(), r("span", le, "접기"))
181
+ ])
182
+ ])
183
+ ], 4));
184
+ }
185
+ });
186
+ export {
187
+ ue as default
188
+ };
189
+ //# sourceMappingURL=JSidebar.vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JSidebar.vue.js","sources":["../../../../../src/components/organisms/JSidebar/JSidebar.vue"],"sourcesContent":["<script setup lang=\"ts\">\r\nimport { ref, computed, provide, watch, reactive } from 'vue'\r\nimport type { SidebarMenuItem, SidebarState } from '@/types/sidebar.types'\r\nimport { SIDEBAR_INJECTION_KEY } from '@/types/sidebar.types'\r\nimport JIcon from '@/components/atoms/JIcon.vue'\r\nimport JInput from '@/components/atoms/JInput.vue'\r\nimport JSidebarGroup from './JSidebarGroup.vue'\r\nimport JSidebarItem from './JSidebarItem.vue'\r\nimport { cn } from '@/lib/utils'\r\n\r\n/**\r\n * JSidebar - 통합 사이드바 컴포넌트\r\n *\r\n * DB 트리 데이터 기반, provide/inject로 상태 공유.\r\n * collapsed 모드, 검색, 즐겨찾기(localStorage) 지원.\r\n * 라우터를 모름 — @menu-click으로 아이템을 올려보냄.\r\n */\r\n\r\nconst props = withDefaults(\r\n defineProps<{\r\n /** 메뉴 트리 데이터 */\r\n items: SidebarMenuItem[]\r\n /** 접힘 상태 (v-model:collapsed) */\r\n collapsed?: boolean\r\n /** 현재 활성 경로 */\r\n activePath?: string\r\n /** 펼침 너비 */\r\n width?: string\r\n /** 접힘 너비 */\r\n collapsedWidth?: string\r\n /** 즐겨찾기 localStorage 키 (없으면 즐겨찾기 비활성) */\r\n storageKey?: string\r\n /** 검색 표시 여부 */\r\n showSearch?: boolean\r\n /** 즐겨찾기 섹션 표시 여부 */\r\n showFavorites?: boolean\r\n }>(),\r\n {\r\n collapsed: false,\r\n width: '220px',\r\n collapsedWidth: '56px',\r\n showSearch: true,\r\n showFavorites: true,\r\n },\r\n)\r\n\r\nconst emit = defineEmits<{\r\n 'update:collapsed': [value: boolean]\r\n 'menu-click': [item: SidebarMenuItem, event: MouseEvent]\r\n}>()\r\n\r\n// ── 즐겨찾기 (localStorage) ──\r\nconst favorites = ref<Set<string>>(new Set(loadFavorites()))\r\n\r\nfunction loadFavorites(): string[] {\r\n if (!props.storageKey) return []\r\n try {\r\n const raw = localStorage.getItem(props.storageKey)\r\n return raw ? JSON.parse(raw) : []\r\n } catch {\r\n return []\r\n }\r\n}\r\n\r\nfunction saveFavorites() {\r\n if (!props.storageKey) return\r\n localStorage.setItem(props.storageKey, JSON.stringify([...favorites.value]))\r\n}\r\n\r\nfunction toggleFavorite(id: string) {\r\n if (favorites.value.has(id)) {\r\n favorites.value.delete(id)\r\n } else {\r\n favorites.value.add(id)\r\n }\r\n favorites.value = new Set(favorites.value) // trigger reactivity\r\n saveFavorites()\r\n}\r\n\r\n// ── provide/inject 상태 ──\r\nconst sidebarState = reactive<SidebarState>({\r\n collapsed: props.collapsed,\r\n activePath: props.activePath,\r\n favorites: favorites.value,\r\n toggleFavorite,\r\n})\r\n\r\n// props 변경 시 state 동기화\r\nwatch(() => props.collapsed, (v) => { sidebarState.collapsed = v })\r\nwatch(() => props.activePath, (v) => { sidebarState.activePath = v })\r\nwatch(favorites, (v) => { sidebarState.favorites = v })\r\n\r\nprovide(SIDEBAR_INJECTION_KEY, sidebarState)\r\n\r\n// ── 검색 ──\r\nconst searchQuery = ref('')\r\n\r\n/** 즐겨찾기 아이템 (L 타입만, 트리 평탄화) */\r\nconst favoriteItems = computed(() => {\r\n if (!props.storageKey || favorites.value.size === 0) return []\r\n const result: SidebarMenuItem[] = []\r\n const flatten = (items: SidebarMenuItem[]) => {\r\n for (const item of items) {\r\n if (item.menuType === 'L' && favorites.value.has(item.id)) {\r\n result.push(item)\r\n }\r\n if (item.children) flatten(item.children)\r\n }\r\n }\r\n flatten(props.items)\r\n return result\r\n})\r\n\r\n/** 검색 필터링 (재귀) */\r\nconst filteredItems = computed(() => {\r\n const q = searchQuery.value.trim().toLowerCase()\r\n if (!q) return props.items\r\n\r\n const filter = (items: SidebarMenuItem[]): SidebarMenuItem[] => {\r\n const result: SidebarMenuItem[] = []\r\n for (const item of items) {\r\n const matchLabel = item.label.toLowerCase().includes(q)\r\n const filteredChildren = item.children ? filter(item.children) : undefined\r\n if (matchLabel || (filteredChildren && filteredChildren.length > 0)) {\r\n result.push({ ...item, children: filteredChildren })\r\n }\r\n }\r\n return result\r\n }\r\n return filter(props.items)\r\n})\r\n\r\n/** 즐겨찾기 검색 필터링 */\r\nconst filteredFavorites = computed(() => {\r\n const q = searchQuery.value.trim().toLowerCase()\r\n if (!q) return favoriteItems.value\r\n return favoriteItems.value.filter(item => item.label.toLowerCase().includes(q))\r\n})\r\n\r\n// ── 즐겨찾기 그룹 펼침 ──\r\nconst favoritesExpanded = ref(true)\r\n\r\n// ── 이벤트 핸들러 ──\r\nconst handleMenuClick = (item: SidebarMenuItem, event: MouseEvent) => {\r\n emit('menu-click', item, event)\r\n}\r\n\r\nconst toggleCollapsed = () => {\r\n emit('update:collapsed', !props.collapsed)\r\n}\r\n\r\n// ── 사이드바 너비 ──\r\nconst sidebarWidth = computed(() => props.collapsed ? props.collapsedWidth : props.width)\r\n</script>\r\n\r\n<template>\r\n <aside\r\n class=\"h-full bg-background border-r border-border flex flex-col flex-shrink-0 overflow-hidden transition-[width] duration-200 ease-out\"\r\n :style=\"{ width: sidebarWidth }\"\r\n >\r\n <!-- 검색바 (펼침 + showSearch) -->\r\n <div v-if=\"!collapsed && showSearch\" class=\"p-2 flex-shrink-0\">\r\n <div class=\"relative\">\r\n <JIcon\r\n name=\"search\"\r\n size=\"sm\"\r\n class=\"absolute left-2 top-1/2 -translate-y-1/2 text-muted-foreground\"\r\n />\r\n <JInput\r\n v-model=\"searchQuery\"\r\n placeholder=\"메뉴 검색...\"\r\n class=\"pl-8 h-7 text-xs\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <!-- 메뉴 영역 -->\r\n <nav class=\"flex-1 overflow-y-auto overflow-x-hidden px-1 py-1\">\r\n <!-- 즐겨찾기 섹션 -->\r\n <template v-if=\"showFavorites && storageKey && filteredFavorites.length > 0\">\r\n <!-- 펼침: 즐겨찾기 그룹 -->\r\n <template v-if=\"!collapsed\">\r\n <button\r\n class=\"flex items-center gap-1.5 w-full px-2 py-1.5 text-xs font-semibold text-yellow-600 cursor-pointer select-none transition-colors hover:text-yellow-700\"\r\n @click=\"favoritesExpanded = !favoritesExpanded\"\r\n >\r\n <JIcon\r\n name=\"chevronRight\"\r\n size=\"sm\"\r\n :class=\"cn(\r\n 'flex-shrink-0 transition-transform duration-200',\r\n favoritesExpanded && 'rotate-90'\r\n )\"\r\n />\r\n <JIcon name=\"star\" size=\"sm\" class=\"flex-shrink-0 text-yellow-500\" />\r\n <span class=\"flex-1 text-left\">즐겨찾기</span>\r\n <span class=\"text-[10px] text-yellow-500/60\">{{ filteredFavorites.length }}</span>\r\n </button>\r\n <div\r\n :class=\"cn(\r\n 'grid transition-[grid-template-rows] duration-200 ease-out',\r\n favoritesExpanded ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'\r\n )\"\r\n >\r\n <div class=\"overflow-hidden\">\r\n <JSidebarItem\r\n v-for=\"fav in filteredFavorites\"\r\n :key=\"'fav-' + fav.id\"\r\n :item=\"fav\"\r\n @menu-click=\"handleMenuClick\"\r\n />\r\n </div>\r\n </div>\r\n </template>\r\n <!-- 접힘: 별 아이콘 + 구분선 -->\r\n <template v-else>\r\n <div class=\"flex justify-center py-1\">\r\n <JIcon name=\"star\" size=\"sm\" class=\"text-yellow-500\" />\r\n </div>\r\n <JSidebarItem\r\n v-for=\"fav in filteredFavorites\"\r\n :key=\"'fav-c-' + fav.id\"\r\n :item=\"fav\"\r\n @menu-click=\"handleMenuClick\"\r\n />\r\n </template>\r\n <div class=\"h-px bg-border mx-2 my-1\" />\r\n </template>\r\n\r\n <!-- 메인 메뉴 -->\r\n <template v-for=\"item in filteredItems\" :key=\"item.id\">\r\n <JSidebarGroup\r\n v-if=\"item.menuType === 'F'\"\r\n :item=\"item\"\r\n @menu-click=\"handleMenuClick\"\r\n />\r\n <JSidebarItem\r\n v-else-if=\"item.menuType === 'L'\"\r\n :item=\"item\"\r\n @menu-click=\"handleMenuClick\"\r\n />\r\n </template>\r\n\r\n <!-- 검색 결과 없음 -->\r\n <div\r\n v-if=\"filteredItems.length === 0 && searchQuery.trim()\"\r\n class=\"text-center py-6 text-muted-foreground text-xs\"\r\n >\r\n 검색 결과가 없습니다.\r\n </div>\r\n </nav>\r\n\r\n <!-- 하단: collapse 토글 -->\r\n <div class=\"flex-shrink-0 border-t border-border p-1\">\r\n <button\r\n class=\"flex items-center gap-2 w-full px-2 py-1 rounded-md text-xs text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors\"\r\n @click=\"toggleCollapsed\"\r\n >\r\n <JIcon\r\n :name=\"collapsed ? 'panelLeftOpen' : 'panelLeftClose'\"\r\n size=\"sm\"\r\n />\r\n <span v-if=\"!collapsed\">접기</span>\r\n </button>\r\n </div>\r\n </aside>\r\n</template>\r\n"],"names":["props","__props","emit","__emit","favorites","ref","loadFavorites","raw","saveFavorites","toggleFavorite","id","sidebarState","reactive","watch","v","provide","SIDEBAR_INJECTION_KEY","searchQuery","favoriteItems","computed","result","flatten","items","item","filteredItems","q","filter","matchLabel","filteredChildren","filteredFavorites","favoritesExpanded","handleMenuClick","event","toggleCollapsed","sidebarWidth","_createElementBlock","_openBlock","_hoisted_1","_createElementVNode","_hoisted_2","_createVNode","JIcon","JInput","$event","_hoisted_3","_Fragment","_hoisted_6","_renderList","fav","_createBlock","JSidebarItem","_cache","_unref","cn","_hoisted_4","_toDisplayString","_hoisted_5","JSidebarGroup","_hoisted_7","_hoisted_8"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("./JSidebar.vue.cjs");exports.default=e.default;
2
+ //# sourceMappingURL=JSidebar.vue2.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JSidebar.vue2.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}