@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
@@ -0,0 +1,5 @@
1
+ import f from "./JSidebar.vue.js";
2
+ export {
3
+ f as default
4
+ };
5
+ //# sourceMappingURL=JSidebar.vue2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JSidebar.vue2.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue"),v=require("../../../types/sidebar.types.cjs"),g=require("../../atoms/JIcon.vue.cjs"),m=require("./JSidebarItem.vue.cjs"),d=require("../../../lib/utils.cjs"),h={key:1},y={class:"flex-1 truncate text-left"},B={key:0,class:"text-[10px] text-muted-foreground/60 flex-shrink-0"},x={class:"overflow-hidden ml-3 pl-2 border-l border-border/50"},E=e.defineComponent({__name:"JSidebarGroup",props:{item:{}},emits:["menu-click"],setup(c,{emit:p}){const i=c,k=p,u=e.inject(v.SIDEBAR_INJECTION_KEY),l=e.ref(!1),s=e.computed(()=>i.item.children?i.item.children.filter(t=>t.menuType==="L").length:0),f=()=>{l.value=!l.value},a=(t,r)=>{k("menu-click",t,r)},_=e.computed(()=>{if(!u.activePath||!i.item.children)return!1;const t=r=>{for(const o of r)if(o.menuType==="L"&&o.path===u.activePath||o.children&&t(o.children))return!0;return!1};return t(i.item.children)});return e.watch(_,t=>{t&&(l.value=!0)},{immediate:!0}),(t,r)=>{const o=e.resolveComponent("JSidebarGroup",!0);return e.unref(u).collapsed?(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[r[0]||(r[0]=e.createElementVNode("div",{class:"h-px bg-border mx-2 my-1"},null,-1)),(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(c.item.children,n=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:n.id},[n.menuType==="L"?(e.openBlock(),e.createBlock(m.default,{key:0,item:n,onMenuClick:a},null,8,["item"])):e.createCommentVNode("",!0)],64))),128))],64)):(e.openBlock(),e.createElementBlock("div",h,[e.createElementVNode("button",{class:"flex items-center gap-1.5 w-full px-2 py-1.5 mt-1 text-xs font-semibold text-muted-foreground cursor-pointer select-none transition-colors hover:text-foreground",onClick:f},[e.createVNode(g.default,{name:"chevronRight",size:"sm",class:e.normalizeClass(e.unref(d.cn)("flex-shrink-0 transition-transform duration-200",l.value&&"rotate-90"))},null,8,["class"]),e.createElementVNode("span",y,e.toDisplayString(c.item.label),1),s.value>0?(e.openBlock(),e.createElementBlock("span",B,e.toDisplayString(s.value),1)):e.createCommentVNode("",!0)]),e.createElementVNode("div",{class:e.normalizeClass(e.unref(d.cn)("grid transition-[grid-template-rows] duration-200 ease-out",l.value?"grid-rows-[1fr]":"grid-rows-[0fr]"))},[e.createElementVNode("div",x,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(c.item.children,n=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:n.id},[n.menuType==="L"?(e.openBlock(),e.createBlock(m.default,{key:0,item:n,onMenuClick:a},null,8,["item"])):n.menuType==="F"?(e.openBlock(),e.createBlock(o,{key:1,item:n,onMenuClick:a},null,8,["item"])):e.createCommentVNode("",!0)],64))),128))])],2)]))}}});exports.default=E;
2
+ //# sourceMappingURL=JSidebarGroup.vue.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JSidebarGroup.vue.cjs","sources":["../../../../../src/components/organisms/JSidebar/JSidebarGroup.vue"],"sourcesContent":["<script setup lang=\"ts\">\r\nimport { ref, computed, inject, watch } from 'vue'\r\nimport type { SidebarMenuItem } from '@/types/sidebar.types'\r\nimport { SIDEBAR_INJECTION_KEY } from '@/types/sidebar.types'\r\nimport JIcon from '@/components/atoms/JIcon.vue'\r\nimport JSidebarItem from './JSidebarItem.vue'\r\nimport { cn } from '@/lib/utils'\r\n\r\n/**\r\n * JSidebarGroup - 그룹 헤더 (menuType='F' 전용)\r\n * 폴더 타입 메뉴를 렌더링. 펼침/접힘 토글 + 하위 메뉴 재귀 렌더링.\r\n * 링크(L)는 JSidebarItem에서 처리.\r\n */\r\n\r\nconst props = defineProps<{\r\n item: SidebarMenuItem\r\n}>()\r\n\r\nconst emit = defineEmits<{\r\n 'menu-click': [item: SidebarMenuItem, event: MouseEvent]\r\n}>()\r\n\r\nconst state = inject(SIDEBAR_INJECTION_KEY)!\r\n\r\nconst isExpanded = ref(false)\r\n\r\n/** 직계 자식 중 L 타입 개수 (재귀하지 않음) */\r\nconst childCount = computed(() => {\r\n if (!props.item.children) return 0\r\n return props.item.children.filter(c => c.menuType === 'L').length\r\n})\r\n\r\nconst toggleExpand = () => {\r\n isExpanded.value = !isExpanded.value\r\n}\r\n\r\n/** 자식의 menu-click을 상위로 전파 */\r\nconst handleChildClick = (item: SidebarMenuItem, event: MouseEvent) => {\r\n emit('menu-click', item, event)\r\n}\r\n\r\n/** 현재 경로가 이 그룹의 자식에 포함되면 자동 펼침 */\r\nconst hasActiveChild = computed(() => {\r\n if (!state.activePath || !props.item.children) return false\r\n const checkActive = (items: SidebarMenuItem[]): boolean => {\r\n for (const child of items) {\r\n if (child.menuType === 'L' && child.path === state.activePath) return true\r\n if (child.children && checkActive(child.children)) return true\r\n }\r\n return false\r\n }\r\n return checkActive(props.item.children)\r\n})\r\n\r\nwatch(hasActiveChild, (active) => {\r\n if (active) isExpanded.value = true\r\n}, { immediate: true })\r\n</script>\r\n\r\n<template>\r\n <!-- Collapsed: 구분선만 -->\r\n <template v-if=\"state.collapsed\">\r\n <div class=\"h-px bg-border mx-2 my-1\" />\r\n <!-- collapsed 상태에서 자식 아이템 직접 렌더링 -->\r\n <template v-for=\"child in item.children\" :key=\"child.id\">\r\n <JSidebarItem\r\n v-if=\"child.menuType === 'L'\"\r\n :item=\"child\"\r\n @menu-click=\"handleChildClick\"\r\n />\r\n <!-- 중첩 폴더는 collapsed에서 무시 (1단계만) -->\r\n </template>\r\n </template>\r\n\r\n <!-- Expanded: 그룹 헤더 + 하위 메뉴 -->\r\n <div v-else>\r\n <!-- 그룹 헤더 -->\r\n <button\r\n class=\"flex items-center gap-1.5 w-full px-2 py-1.5 mt-1 text-xs font-semibold text-muted-foreground cursor-pointer select-none transition-colors hover:text-foreground\"\r\n @click=\"toggleExpand\"\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 isExpanded && 'rotate-90'\r\n )\"\r\n />\r\n <span class=\"flex-1 truncate text-left\">{{ item.label }}</span>\r\n <span\r\n v-if=\"childCount > 0\"\r\n class=\"text-[10px] text-muted-foreground/60 flex-shrink-0\"\r\n >\r\n {{ childCount }}\r\n </span>\r\n </button>\r\n\r\n <!-- 하위 메뉴 (grid-template-rows 애니메이션) -->\r\n <div\r\n :class=\"cn(\r\n 'grid transition-[grid-template-rows] duration-200 ease-out',\r\n isExpanded ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'\r\n )\"\r\n >\r\n <div class=\"overflow-hidden ml-3 pl-2 border-l border-border/50\">\r\n <template v-for=\"child in item.children\" :key=\"child.id\">\r\n <JSidebarItem\r\n v-if=\"child.menuType === 'L'\"\r\n :item=\"child\"\r\n @menu-click=\"handleChildClick\"\r\n />\r\n <!-- 중첩 폴더: 재귀 -->\r\n <JSidebarGroup\r\n v-else-if=\"child.menuType === 'F'\"\r\n :item=\"child\"\r\n @menu-click=\"handleChildClick\"\r\n />\r\n </template>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n"],"names":["props","__props","emit","__emit","state","inject","SIDEBAR_INJECTION_KEY","isExpanded","ref","childCount","computed","c","toggleExpand","handleChildClick","item","event","hasActiveChild","checkActive","items","child","watch","active","_unref","_createElementBlock","_Fragment","_createElementVNode","_openBlock","_renderList","_createBlock","JSidebarItem","_hoisted_1","_createVNode","JIcon","cn","_hoisted_2","_toDisplayString","_hoisted_3","_hoisted_4","_component_JSidebarGroup"],"mappings":"wjBAcA,MAAMA,EAAQC,EAIRC,EAAOC,EAIPC,EAAQC,EAAAA,OAAOC,uBAAqB,EAEpCC,EAAaC,EAAAA,IAAI,EAAK,EAGtBC,EAAaC,EAAAA,SAAS,IACrBV,EAAM,KAAK,SACTA,EAAM,KAAK,SAAS,UAAYW,EAAE,WAAa,GAAG,EAAE,OAD1B,CAElC,EAEKC,EAAe,IAAM,CACzBL,EAAW,MAAQ,CAACA,EAAW,KACjC,EAGMM,EAAmB,CAACC,EAAuBC,IAAsB,CACrEb,EAAK,aAAcY,EAAMC,CAAK,CAChC,EAGMC,EAAiBN,EAAAA,SAAS,IAAM,CACpC,GAAI,CAACN,EAAM,YAAc,CAACJ,EAAM,KAAK,SAAU,MAAO,GACtD,MAAMiB,EAAeC,GAAsC,CACzD,UAAWC,KAASD,EAElB,GADIC,EAAM,WAAa,KAAOA,EAAM,OAASf,EAAM,YAC/Ce,EAAM,UAAYF,EAAYE,EAAM,QAAQ,EAAG,MAAO,GAE5D,MAAO,EACT,EACA,OAAOF,EAAYjB,EAAM,KAAK,QAAQ,CACxC,CAAC,EAEDoB,OAAAA,QAAMJ,EAAiBK,GAAW,CAC5BA,MAAmB,MAAQ,GACjC,EAAG,CAAE,UAAW,GAAM,yDAKJ,OAAAC,QAAAlB,CAAA,EAAM,yBAAtBmB,EAAAA,mBAWWC,WAAA,CAAA,IAAA,GAAA,aAVTC,EAAAA,mBAAwC,MAAA,CAAnC,MAAM,0BAAA,EAA0B,KAAA,EAAA,IAErCC,EAAAA,UAAA,EAAA,EAAAH,EAAAA,mBAOWC,WAAA,KAAAG,EAAAA,WAPe1B,EAAA,KAAK,SAAdkB,mDAA8B,IAAAA,EAAM,EAAA,GAE3CA,EAAM,WAAQ,mBADtBS,EAAAA,YAIEC,EAAAA,QAAA,OAFC,KAAMV,EACN,YAAYN,CAAA,kFAOnBU,EAAAA,mBA8CM,MAAAO,EAAA,CA5CJL,EAAAA,mBAmBS,SAAA,CAlBP,MAAM,mKACL,QAAOb,CAAA,GAERmB,EAAAA,YAOEC,EAAAA,QAAA,CANA,KAAK,eACL,KAAK,KACJ,uBAAOV,EAAAA,MAAAW,IAAA,oDAA6E1B,EAAA,OAAU,WAAA,sBAKjGkB,qBAA+D,OAA/DS,EAA+DC,EAAAA,gBAApBlC,EAAA,KAAK,KAAK,EAAA,CAAA,EAE7CQ,EAAA,MAAU,iBADlBc,EAAAA,mBAKO,OALPa,EAKOD,EAAAA,gBADF1B,EAAA,KAAU,EAAA,CAAA,iCAKjBgB,EAAAA,mBAqBM,MAAA,CApBH,uBAAOH,EAAAA,MAAAW,IAAA,+DAAoF1B,EAAA,MAAU,kBAAA,iBAAA,KAKtGkB,EAAAA,mBAcM,MAdNY,EAcM,EAbJX,EAAAA,UAAA,EAAA,EAAAH,EAAAA,mBAYWC,WAAA,KAAAG,EAAAA,WAZe1B,EAAA,KAAK,SAAdkB,mDAA8B,IAAAA,EAAM,EAAA,GAE3CA,EAAM,WAAQ,mBADtBS,EAAAA,YAIEC,EAAAA,QAAA,OAFC,KAAMV,EACN,YAAYN,CAAA,oBAIFM,EAAM,WAAQ,mBAD3BS,EAAAA,YAIEU,EAAA,OAFC,KAAMnB,EACN,YAAYN,CAAA"}
@@ -0,0 +1,89 @@
1
+ import { defineComponent as L, inject as N, ref as S, computed as v, watch as B, resolveComponent as J, createElementBlock as n, unref as f, openBlock as e, Fragment as s, createElementVNode as c, renderList as x, createBlock as p, createCommentVNode as h, createVNode as A, normalizeClass as y, toDisplayString as g } from "vue";
2
+ import { SIDEBAR_INJECTION_KEY as G } from "../../../types/sidebar.types.js";
3
+ import I from "../../atoms/JIcon.vue.js";
4
+ import _ from "./JSidebarItem.vue.js";
5
+ import { cn as C } from "../../../lib/utils.js";
6
+ const M = { key: 1 }, V = { class: "flex-1 truncate text-left" }, z = {
7
+ key: 0,
8
+ class: "text-[10px] text-muted-foreground/60 flex-shrink-0"
9
+ }, D = { class: "overflow-hidden ml-3 pl-2 border-l border-border/50" }, K = /* @__PURE__ */ L({
10
+ __name: "JSidebarGroup",
11
+ props: {
12
+ item: {}
13
+ },
14
+ emits: ["menu-click"],
15
+ setup(u, { emit: b }) {
16
+ const a = u, E = b, m = N(G), l = S(!1), k = v(() => a.item.children ? a.item.children.filter((t) => t.menuType === "L").length : 0), w = () => {
17
+ l.value = !l.value;
18
+ }, d = (t, o) => {
19
+ E("menu-click", t, o);
20
+ }, T = v(() => {
21
+ if (!m.activePath || !a.item.children) return !1;
22
+ const t = (o) => {
23
+ for (const i of o)
24
+ if (i.menuType === "L" && i.path === m.activePath || i.children && t(i.children)) return !0;
25
+ return !1;
26
+ };
27
+ return t(a.item.children);
28
+ });
29
+ return B(T, (t) => {
30
+ t && (l.value = !0);
31
+ }, { immediate: !0 }), (t, o) => {
32
+ const i = J("JSidebarGroup", !0);
33
+ return f(m).collapsed ? (e(), n(s, { key: 0 }, [
34
+ o[0] || (o[0] = c("div", { class: "h-px bg-border mx-2 my-1" }, null, -1)),
35
+ (e(!0), n(s, null, x(u.item.children, (r) => (e(), n(s, {
36
+ key: r.id
37
+ }, [
38
+ r.menuType === "L" ? (e(), p(_, {
39
+ key: 0,
40
+ item: r,
41
+ onMenuClick: d
42
+ }, null, 8, ["item"])) : h("", !0)
43
+ ], 64))), 128))
44
+ ], 64)) : (e(), n("div", M, [
45
+ c("button", {
46
+ class: "flex items-center gap-1.5 w-full px-2 py-1.5 mt-1 text-xs font-semibold text-muted-foreground cursor-pointer select-none transition-colors hover:text-foreground",
47
+ onClick: w
48
+ }, [
49
+ A(I, {
50
+ name: "chevronRight",
51
+ size: "sm",
52
+ class: y(f(C)(
53
+ "flex-shrink-0 transition-transform duration-200",
54
+ l.value && "rotate-90"
55
+ ))
56
+ }, null, 8, ["class"]),
57
+ c("span", V, g(u.item.label), 1),
58
+ k.value > 0 ? (e(), n("span", z, g(k.value), 1)) : h("", !0)
59
+ ]),
60
+ c("div", {
61
+ class: y(f(C)(
62
+ "grid transition-[grid-template-rows] duration-200 ease-out",
63
+ l.value ? "grid-rows-[1fr]" : "grid-rows-[0fr]"
64
+ ))
65
+ }, [
66
+ c("div", D, [
67
+ (e(!0), n(s, null, x(u.item.children, (r) => (e(), n(s, {
68
+ key: r.id
69
+ }, [
70
+ r.menuType === "L" ? (e(), p(_, {
71
+ key: 0,
72
+ item: r,
73
+ onMenuClick: d
74
+ }, null, 8, ["item"])) : r.menuType === "F" ? (e(), p(i, {
75
+ key: 1,
76
+ item: r,
77
+ onMenuClick: d
78
+ }, null, 8, ["item"])) : h("", !0)
79
+ ], 64))), 128))
80
+ ])
81
+ ], 2)
82
+ ]));
83
+ };
84
+ }
85
+ });
86
+ export {
87
+ K as default
88
+ };
89
+ //# sourceMappingURL=JSidebarGroup.vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JSidebarGroup.vue.js","sources":["../../../../../src/components/organisms/JSidebar/JSidebarGroup.vue"],"sourcesContent":["<script setup lang=\"ts\">\r\nimport { ref, computed, inject, watch } from 'vue'\r\nimport type { SidebarMenuItem } from '@/types/sidebar.types'\r\nimport { SIDEBAR_INJECTION_KEY } from '@/types/sidebar.types'\r\nimport JIcon from '@/components/atoms/JIcon.vue'\r\nimport JSidebarItem from './JSidebarItem.vue'\r\nimport { cn } from '@/lib/utils'\r\n\r\n/**\r\n * JSidebarGroup - 그룹 헤더 (menuType='F' 전용)\r\n * 폴더 타입 메뉴를 렌더링. 펼침/접힘 토글 + 하위 메뉴 재귀 렌더링.\r\n * 링크(L)는 JSidebarItem에서 처리.\r\n */\r\n\r\nconst props = defineProps<{\r\n item: SidebarMenuItem\r\n}>()\r\n\r\nconst emit = defineEmits<{\r\n 'menu-click': [item: SidebarMenuItem, event: MouseEvent]\r\n}>()\r\n\r\nconst state = inject(SIDEBAR_INJECTION_KEY)!\r\n\r\nconst isExpanded = ref(false)\r\n\r\n/** 직계 자식 중 L 타입 개수 (재귀하지 않음) */\r\nconst childCount = computed(() => {\r\n if (!props.item.children) return 0\r\n return props.item.children.filter(c => c.menuType === 'L').length\r\n})\r\n\r\nconst toggleExpand = () => {\r\n isExpanded.value = !isExpanded.value\r\n}\r\n\r\n/** 자식의 menu-click을 상위로 전파 */\r\nconst handleChildClick = (item: SidebarMenuItem, event: MouseEvent) => {\r\n emit('menu-click', item, event)\r\n}\r\n\r\n/** 현재 경로가 이 그룹의 자식에 포함되면 자동 펼침 */\r\nconst hasActiveChild = computed(() => {\r\n if (!state.activePath || !props.item.children) return false\r\n const checkActive = (items: SidebarMenuItem[]): boolean => {\r\n for (const child of items) {\r\n if (child.menuType === 'L' && child.path === state.activePath) return true\r\n if (child.children && checkActive(child.children)) return true\r\n }\r\n return false\r\n }\r\n return checkActive(props.item.children)\r\n})\r\n\r\nwatch(hasActiveChild, (active) => {\r\n if (active) isExpanded.value = true\r\n}, { immediate: true })\r\n</script>\r\n\r\n<template>\r\n <!-- Collapsed: 구분선만 -->\r\n <template v-if=\"state.collapsed\">\r\n <div class=\"h-px bg-border mx-2 my-1\" />\r\n <!-- collapsed 상태에서 자식 아이템 직접 렌더링 -->\r\n <template v-for=\"child in item.children\" :key=\"child.id\">\r\n <JSidebarItem\r\n v-if=\"child.menuType === 'L'\"\r\n :item=\"child\"\r\n @menu-click=\"handleChildClick\"\r\n />\r\n <!-- 중첩 폴더는 collapsed에서 무시 (1단계만) -->\r\n </template>\r\n </template>\r\n\r\n <!-- Expanded: 그룹 헤더 + 하위 메뉴 -->\r\n <div v-else>\r\n <!-- 그룹 헤더 -->\r\n <button\r\n class=\"flex items-center gap-1.5 w-full px-2 py-1.5 mt-1 text-xs font-semibold text-muted-foreground cursor-pointer select-none transition-colors hover:text-foreground\"\r\n @click=\"toggleExpand\"\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 isExpanded && 'rotate-90'\r\n )\"\r\n />\r\n <span class=\"flex-1 truncate text-left\">{{ item.label }}</span>\r\n <span\r\n v-if=\"childCount > 0\"\r\n class=\"text-[10px] text-muted-foreground/60 flex-shrink-0\"\r\n >\r\n {{ childCount }}\r\n </span>\r\n </button>\r\n\r\n <!-- 하위 메뉴 (grid-template-rows 애니메이션) -->\r\n <div\r\n :class=\"cn(\r\n 'grid transition-[grid-template-rows] duration-200 ease-out',\r\n isExpanded ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'\r\n )\"\r\n >\r\n <div class=\"overflow-hidden ml-3 pl-2 border-l border-border/50\">\r\n <template v-for=\"child in item.children\" :key=\"child.id\">\r\n <JSidebarItem\r\n v-if=\"child.menuType === 'L'\"\r\n :item=\"child\"\r\n @menu-click=\"handleChildClick\"\r\n />\r\n <!-- 중첩 폴더: 재귀 -->\r\n <JSidebarGroup\r\n v-else-if=\"child.menuType === 'F'\"\r\n :item=\"child\"\r\n @menu-click=\"handleChildClick\"\r\n />\r\n </template>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n"],"names":["props","__props","emit","__emit","state","inject","SIDEBAR_INJECTION_KEY","isExpanded","ref","childCount","computed","c","toggleExpand","handleChildClick","item","event","hasActiveChild","checkActive","items","child","watch","active","_unref","_createElementBlock","_Fragment","_createElementVNode","_openBlock","_renderList","_createBlock","JSidebarItem","_hoisted_1","_createVNode","JIcon","cn","_hoisted_2","_toDisplayString","_hoisted_3","_hoisted_4","_component_JSidebarGroup"],"mappings":";;;;;;;;;;;;;;;AAcA,UAAMA,IAAQC,GAIRC,IAAOC,GAIPC,IAAQC,EAAOC,CAAqB,GAEpCC,IAAaC,EAAI,EAAK,GAGtBC,IAAaC,EAAS,MACrBV,EAAM,KAAK,WACTA,EAAM,KAAK,SAAS,OAAO,OAAKW,EAAE,aAAa,GAAG,EAAE,SAD1B,CAElC,GAEKC,IAAe,MAAM;AACzB,MAAAL,EAAW,QAAQ,CAACA,EAAW;AAAA,IACjC,GAGMM,IAAmB,CAACC,GAAuBC,MAAsB;AACrE,MAAAb,EAAK,cAAcY,GAAMC,CAAK;AAAA,IAChC,GAGMC,IAAiBN,EAAS,MAAM;AACpC,UAAI,CAACN,EAAM,cAAc,CAACJ,EAAM,KAAK,SAAU,QAAO;AACtD,YAAMiB,IAAc,CAACC,MAAsC;AACzD,mBAAWC,KAASD;AAElB,cADIC,EAAM,aAAa,OAAOA,EAAM,SAASf,EAAM,cAC/Ce,EAAM,YAAYF,EAAYE,EAAM,QAAQ,EAAG,QAAO;AAE5D,eAAO;AAAA,MACT;AACA,aAAOF,EAAYjB,EAAM,KAAK,QAAQ;AAAA,IACxC,CAAC;AAED,WAAAoB,EAAMJ,GAAgB,CAACK,MAAW;AAChC,MAAIA,QAAmB,QAAQ;AAAA,IACjC,GAAG,EAAE,WAAW,IAAM;;AAKJ,aAAAC,EAAAlB,CAAA,EAAM,kBAAtBmB,EAWWC,GAAA,EAAA,KAAA,KAAA;AAAA,wBAVTC,EAAwC,OAAA,EAAnC,OAAM,2BAAA,GAA0B,MAAA,EAAA;AAAA,SAErCC,EAAA,EAAA,GAAAH,EAOWC,GAAA,MAAAG,EAPe1B,EAAA,KAAK,WAAdkB;UAA8B,KAAAA,EAAM;AAAA,QAAA;UAE3CA,EAAM,aAAQ,YADtBS,EAIEC,GAAA;AAAA;YAFC,MAAMV;AAAA,YACN,aAAYN;AAAA,UAAA;;sBAOnBU,EA8CM,OAAAO,GAAA;AAAA,QA5CJL,EAmBS,UAAA;AAAA,UAlBP,OAAM;AAAA,UACL,SAAOb;AAAA,QAAA;UAERmB,EAOEC,GAAA;AAAA,YANA,MAAK;AAAA,YACL,MAAK;AAAA,YACJ,SAAOV,EAAAW,CAAA;AAAA;cAA6E1B,EAAA,SAAU;AAAA,YAAA;;UAKjGkB,EAA+D,QAA/DS,GAA+DC,EAApBlC,EAAA,KAAK,KAAK,GAAA,CAAA;AAAA,UAE7CQ,EAAA,QAAU,UADlBc,EAKO,QALPa,GAKOD,EADF1B,EAAA,KAAU,GAAA,CAAA;;QAKjBgB,EAqBM,OAAA;AAAA,UApBH,SAAOH,EAAAW,CAAA;AAAA;YAAoF1B,EAAA,QAAU,oBAAA;AAAA,UAAA;;UAKtGkB,EAcM,OAdNY,GAcM;AAAA,aAbJX,EAAA,EAAA,GAAAH,EAYWC,GAAA,MAAAG,EAZe1B,EAAA,KAAK,WAAdkB;cAA8B,KAAAA,EAAM;AAAA,YAAA;cAE3CA,EAAM,aAAQ,YADtBS,EAIEC,GAAA;AAAA;gBAFC,MAAMV;AAAA,gBACN,aAAYN;AAAA,cAAA,yBAIFM,EAAM,aAAQ,YAD3BS,EAIEU,GAAA;AAAA;gBAFC,MAAMnB;AAAA,gBACN,aAAYN;AAAA,cAAA;;;;;;;;"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("./JSidebarGroup.vue.cjs");exports.default=e.default;
2
+ //# sourceMappingURL=JSidebarGroup.vue2.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JSidebarGroup.vue2.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ import f from "./JSidebarGroup.vue.js";
2
+ export {
3
+ f as default
4
+ };
5
+ //# sourceMappingURL=JSidebarGroup.vue2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JSidebarGroup.vue2.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue"),p=require("../../../types/sidebar.types.cjs"),a=require("../../atoms/JIcon.vue.cjs"),f=require("../../atoms/JTooltip.vue.cjs"),l=require("../../../lib/utils.cjs"),v={key:0,class:"absolute left-0 top-1 bottom-1 w-[3px] rounded-r bg-primary"},y={class:"flex-1 truncate text-xs"},g=e.defineComponent({__name:"JSidebarItem",props:{item:{}},emits:["menu-click"],setup(t,{emit:u}){const o=t,m=u,i=e.inject(p.SIDEBAR_INJECTION_KEY),r=e.computed(()=>!o.item.path||!i.activePath?!1:i.activePath===o.item.path),c=e.computed(()=>i.favorites.has(o.item.id)),s=n=>{o.item.disabled||m("menu-click",o.item,n)},d=n=>{n.stopPropagation(),i.toggleFavorite(o.item.id)};return(n,k)=>e.unref(i).collapsed?(e.openBlock(),e.createBlock(f.default,{key:0,content:t.item.label,side:"right",size:"sm"},{trigger:e.withCtx(()=>[e.createElementVNode("div",{class:e.normalizeClass(e.unref(l.cn)("flex items-center justify-center py-1.5 mx-1 rounded-md cursor-pointer transition-colors",r.value?"bg-primary/10 text-primary":"text-foreground hover:bg-accent/50",t.item.disabled&&"opacity-40 cursor-not-allowed")),onClick:s},[e.createVNode(a.default,{name:t.item.icon||"file",size:"sm"},null,8,["name"])],2)]),_:1},8,["content"])):(e.openBlock(),e.createElementBlock("div",{key:1,class:e.normalizeClass(e.unref(l.cn)("group flex items-center gap-2 py-1 px-2 rounded-md cursor-pointer transition-colors relative",r.value?"bg-primary/10 text-primary font-medium":"text-foreground hover:bg-accent/50",t.item.disabled&&"opacity-40 cursor-not-allowed")),onClick:s},[r.value?(e.openBlock(),e.createElementBlock("div",v)):e.createCommentVNode("",!0),t.item.icon?(e.openBlock(),e.createBlock(a.default,{key:1,name:t.item.icon,size:"sm",class:"flex-shrink-0"},null,8,["name"])):e.createCommentVNode("",!0),e.createElementVNode("span",y,e.toDisplayString(t.item.label),1),e.createElementVNode("button",{class:e.normalizeClass(e.unref(l.cn)("flex-shrink-0 p-0.5 rounded transition-opacity",c.value?"opacity-100 text-yellow-500":"opacity-0 group-hover:opacity-100 text-muted-foreground hover:text-yellow-500")),onClick:d},[e.createVNode(a.default,{name:"star",size:"sm",class:e.normalizeClass(c.value?"fill-yellow-500":void 0)},null,8,["class"])],2)],2))}});exports.default=g;
2
+ //# sourceMappingURL=JSidebarItem.vue.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JSidebarItem.vue.cjs","sources":["../../../../../src/components/organisms/JSidebar/JSidebarItem.vue"],"sourcesContent":["<script setup lang=\"ts\">\r\nimport { computed, inject } from 'vue'\r\nimport type { SidebarMenuItem } from '@/types/sidebar.types'\r\nimport { SIDEBAR_INJECTION_KEY } from '@/types/sidebar.types'\r\nimport JIcon from '@/components/atoms/JIcon.vue'\r\nimport JTooltip from '@/components/atoms/JTooltip.vue'\r\nimport { cn } from '@/lib/utils'\r\n\r\n/**\r\n * JSidebarItem - 메뉴 아이템 (menuType='L' 전용)\r\n * 링크 타입 메뉴만 렌더링. 폴더(F)는 JSidebarGroup에서 처리.\r\n */\r\n\r\nconst props = defineProps<{\r\n item: SidebarMenuItem\r\n}>()\r\n\r\nconst emit = defineEmits<{\r\n 'menu-click': [item: SidebarMenuItem, event: MouseEvent]\r\n}>()\r\n\r\nconst state = inject(SIDEBAR_INJECTION_KEY)!\r\n\r\nconst isActive = computed(() => {\r\n if (!props.item.path || !state.activePath) return false\r\n return state.activePath === props.item.path\r\n})\r\n\r\nconst isFav = computed(() => state.favorites.has(props.item.id))\r\n\r\nconst handleClick = (event: MouseEvent) => {\r\n if (props.item.disabled) return\r\n emit('menu-click', props.item, event)\r\n}\r\n\r\nconst handleFavoriteClick = (event: MouseEvent) => {\r\n event.stopPropagation()\r\n state.toggleFavorite(props.item.id)\r\n}\r\n</script>\r\n\r\n<template>\r\n <!-- Collapsed: 아이콘 + Tooltip -->\r\n <JTooltip\r\n v-if=\"state.collapsed\"\r\n :content=\"item.label\"\r\n side=\"right\"\r\n size=\"sm\"\r\n >\r\n <template #trigger>\r\n <div\r\n :class=\"cn(\r\n 'flex items-center justify-center py-1.5 mx-1 rounded-md cursor-pointer transition-colors',\r\n isActive\r\n ? 'bg-primary/10 text-primary'\r\n : 'text-foreground hover:bg-accent/50',\r\n item.disabled && 'opacity-40 cursor-not-allowed'\r\n )\"\r\n @click=\"handleClick\"\r\n >\r\n <JIcon :name=\"item.icon || 'file'\" size=\"sm\" />\r\n </div>\r\n </template>\r\n </JTooltip>\r\n\r\n <!-- Expanded: 아이콘 + 라벨 + 즐겨찾기 -->\r\n <div\r\n v-else\r\n :class=\"cn(\r\n 'group flex items-center gap-2 py-1 px-2 rounded-md cursor-pointer transition-colors relative',\r\n isActive\r\n ? 'bg-primary/10 text-primary font-medium'\r\n : 'text-foreground hover:bg-accent/50',\r\n item.disabled && 'opacity-40 cursor-not-allowed'\r\n )\"\r\n @click=\"handleClick\"\r\n >\r\n <!-- 활성 좌측 바 -->\r\n <div\r\n v-if=\"isActive\"\r\n class=\"absolute left-0 top-1 bottom-1 w-[3px] rounded-r bg-primary\"\r\n />\r\n\r\n <JIcon\r\n v-if=\"item.icon\"\r\n :name=\"item.icon\"\r\n size=\"sm\"\r\n class=\"flex-shrink-0\"\r\n />\r\n <span class=\"flex-1 truncate text-xs\">{{ item.label }}</span>\r\n\r\n <!-- 즐겨찾기 별 -->\r\n <button\r\n :class=\"cn(\r\n 'flex-shrink-0 p-0.5 rounded transition-opacity',\r\n isFav\r\n ? 'opacity-100 text-yellow-500'\r\n : 'opacity-0 group-hover:opacity-100 text-muted-foreground hover:text-yellow-500'\r\n )\"\r\n @click=\"handleFavoriteClick\"\r\n >\r\n <JIcon\r\n name=\"star\"\r\n size=\"sm\"\r\n :class=\"isFav ? 'fill-yellow-500' : undefined\"\r\n />\r\n </button>\r\n </div>\r\n</template>\r\n"],"names":["props","__props","emit","__emit","state","inject","SIDEBAR_INJECTION_KEY","isActive","computed","isFav","handleClick","event","handleFavoriteClick","_unref","_createBlock","JTooltip","_createElementVNode","cn","_createVNode","JIcon","_createElementBlock","_openBlock","_hoisted_1","_hoisted_2","_toDisplayString","_normalizeClass"],"mappings":"0fAaA,MAAMA,EAAQC,EAIRC,EAAOC,EAIPC,EAAQC,EAAAA,OAAOC,uBAAqB,EAEpCC,EAAWC,EAAAA,SAAS,IACpB,CAACR,EAAM,KAAK,MAAQ,CAACI,EAAM,WAAmB,GAC3CA,EAAM,aAAeJ,EAAM,KAAK,IACxC,EAEKS,EAAQD,WAAS,IAAMJ,EAAM,UAAU,IAAIJ,EAAM,KAAK,EAAE,CAAC,EAEzDU,EAAeC,GAAsB,CACrCX,EAAM,KAAK,UACfE,EAAK,aAAcF,EAAM,KAAMW,CAAK,CACtC,EAEMC,EAAuBD,GAAsB,CACjDA,EAAM,gBAAA,EACNP,EAAM,eAAeJ,EAAM,KAAK,EAAE,CACpC,eAMUa,EAAAA,MAAAT,CAAA,EAAM,yBADdU,EAAAA,YAoBWC,UAAA,OAlBR,QAASd,EAAA,KAAK,MACf,KAAK,QACL,KAAK,IAAA,GAEM,kBACT,IAWM,CAXNe,EAAAA,mBAWM,MAAA,CAVH,uBAAOH,EAAAA,MAAAI,IAAA,6FAAsHV,EAAA,wEAAqHN,EAAA,KAAK,UAAQ,+BAAA,GAO/P,QAAOS,CAAA,GAERQ,EAAAA,YAA+CC,EAAAA,QAAA,CAAvC,KAAMlB,EAAA,KAAK,MAAI,OAAY,KAAK,IAAA,6DAM9CmB,EAAAA,mBAyCM,MAAA,OAvCH,uBAAOP,EAAAA,MAAAI,IAAA,iGAAkHV,EAAA,oFAAqHN,EAAA,KAAK,UAAQ,+BAAA,GAO3P,QAAOS,CAAA,GAIAH,EAAA,OADRc,EAAAA,UAAA,EAAAD,EAAAA,mBAGE,MAHFE,CAGE,+BAGMrB,EAAA,KAAK,oBADba,EAAAA,YAKEK,EAAAA,QAAA,OAHC,KAAMlB,EAAA,KAAK,KACZ,KAAK,KACL,MAAM,eAAA,gDAERe,qBAA6D,OAA7DO,EAA6DC,EAAAA,gBAApBvB,EAAA,KAAK,KAAK,EAAA,CAAA,EAGnDe,EAAAA,mBAcS,SAAA,CAbN,uBAAOH,EAAAA,MAAAI,IAAA,mDAAwER,EAAA,sHAM/E,QAAOG,CAAA,GAERM,EAAAA,YAIEC,EAAAA,QAAA,CAHA,KAAK,OACL,KAAK,KACJ,MAAKM,EAAAA,eAAEhB,EAAA,MAAK,kBAAuB,MAAS,CAAA"}
@@ -0,0 +1,79 @@
1
+ import { defineComponent as b, inject as C, computed as p, createBlock as f, createElementBlock as y, unref as r, openBlock as n, withCtx as w, createElementVNode as l, normalizeClass as a, createVNode as v, createCommentVNode as x, toDisplayString as z } from "vue";
2
+ import { SIDEBAR_INJECTION_KEY as E } from "../../../types/sidebar.types.js";
3
+ import s from "../../atoms/JIcon.vue.js";
4
+ import N from "../../atoms/JTooltip.vue.js";
5
+ import { cn as m } from "../../../lib/utils.js";
6
+ const B = {
7
+ key: 0,
8
+ class: "absolute left-0 top-1 bottom-1 w-[3px] rounded-r bg-primary"
9
+ }, I = { class: "flex-1 truncate text-xs" }, A = /* @__PURE__ */ b({
10
+ __name: "JSidebarItem",
11
+ props: {
12
+ item: {}
13
+ },
14
+ emits: ["menu-click"],
15
+ setup(e, { emit: g }) {
16
+ const t = e, h = g, i = C(E), c = p(() => !t.item.path || !i.activePath ? !1 : i.activePath === t.item.path), d = p(() => i.favorites.has(t.item.id)), u = (o) => {
17
+ t.item.disabled || h("menu-click", t.item, o);
18
+ }, k = (o) => {
19
+ o.stopPropagation(), i.toggleFavorite(t.item.id);
20
+ };
21
+ return (o, F) => r(i).collapsed ? (n(), f(N, {
22
+ key: 0,
23
+ content: e.item.label,
24
+ side: "right",
25
+ size: "sm"
26
+ }, {
27
+ trigger: w(() => [
28
+ l("div", {
29
+ class: a(r(m)(
30
+ "flex items-center justify-center py-1.5 mx-1 rounded-md cursor-pointer transition-colors",
31
+ c.value ? "bg-primary/10 text-primary" : "text-foreground hover:bg-accent/50",
32
+ e.item.disabled && "opacity-40 cursor-not-allowed"
33
+ )),
34
+ onClick: u
35
+ }, [
36
+ v(s, {
37
+ name: e.item.icon || "file",
38
+ size: "sm"
39
+ }, null, 8, ["name"])
40
+ ], 2)
41
+ ]),
42
+ _: 1
43
+ }, 8, ["content"])) : (n(), y("div", {
44
+ key: 1,
45
+ class: a(r(m)(
46
+ "group flex items-center gap-2 py-1 px-2 rounded-md cursor-pointer transition-colors relative",
47
+ c.value ? "bg-primary/10 text-primary font-medium" : "text-foreground hover:bg-accent/50",
48
+ e.item.disabled && "opacity-40 cursor-not-allowed"
49
+ )),
50
+ onClick: u
51
+ }, [
52
+ c.value ? (n(), y("div", B)) : x("", !0),
53
+ e.item.icon ? (n(), f(s, {
54
+ key: 1,
55
+ name: e.item.icon,
56
+ size: "sm",
57
+ class: "flex-shrink-0"
58
+ }, null, 8, ["name"])) : x("", !0),
59
+ l("span", I, z(e.item.label), 1),
60
+ l("button", {
61
+ class: a(r(m)(
62
+ "flex-shrink-0 p-0.5 rounded transition-opacity",
63
+ d.value ? "opacity-100 text-yellow-500" : "opacity-0 group-hover:opacity-100 text-muted-foreground hover:text-yellow-500"
64
+ )),
65
+ onClick: k
66
+ }, [
67
+ v(s, {
68
+ name: "star",
69
+ size: "sm",
70
+ class: a(d.value ? "fill-yellow-500" : void 0)
71
+ }, null, 8, ["class"])
72
+ ], 2)
73
+ ], 2));
74
+ }
75
+ });
76
+ export {
77
+ A as default
78
+ };
79
+ //# sourceMappingURL=JSidebarItem.vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JSidebarItem.vue.js","sources":["../../../../../src/components/organisms/JSidebar/JSidebarItem.vue"],"sourcesContent":["<script setup lang=\"ts\">\r\nimport { computed, inject } from 'vue'\r\nimport type { SidebarMenuItem } from '@/types/sidebar.types'\r\nimport { SIDEBAR_INJECTION_KEY } from '@/types/sidebar.types'\r\nimport JIcon from '@/components/atoms/JIcon.vue'\r\nimport JTooltip from '@/components/atoms/JTooltip.vue'\r\nimport { cn } from '@/lib/utils'\r\n\r\n/**\r\n * JSidebarItem - 메뉴 아이템 (menuType='L' 전용)\r\n * 링크 타입 메뉴만 렌더링. 폴더(F)는 JSidebarGroup에서 처리.\r\n */\r\n\r\nconst props = defineProps<{\r\n item: SidebarMenuItem\r\n}>()\r\n\r\nconst emit = defineEmits<{\r\n 'menu-click': [item: SidebarMenuItem, event: MouseEvent]\r\n}>()\r\n\r\nconst state = inject(SIDEBAR_INJECTION_KEY)!\r\n\r\nconst isActive = computed(() => {\r\n if (!props.item.path || !state.activePath) return false\r\n return state.activePath === props.item.path\r\n})\r\n\r\nconst isFav = computed(() => state.favorites.has(props.item.id))\r\n\r\nconst handleClick = (event: MouseEvent) => {\r\n if (props.item.disabled) return\r\n emit('menu-click', props.item, event)\r\n}\r\n\r\nconst handleFavoriteClick = (event: MouseEvent) => {\r\n event.stopPropagation()\r\n state.toggleFavorite(props.item.id)\r\n}\r\n</script>\r\n\r\n<template>\r\n <!-- Collapsed: 아이콘 + Tooltip -->\r\n <JTooltip\r\n v-if=\"state.collapsed\"\r\n :content=\"item.label\"\r\n side=\"right\"\r\n size=\"sm\"\r\n >\r\n <template #trigger>\r\n <div\r\n :class=\"cn(\r\n 'flex items-center justify-center py-1.5 mx-1 rounded-md cursor-pointer transition-colors',\r\n isActive\r\n ? 'bg-primary/10 text-primary'\r\n : 'text-foreground hover:bg-accent/50',\r\n item.disabled && 'opacity-40 cursor-not-allowed'\r\n )\"\r\n @click=\"handleClick\"\r\n >\r\n <JIcon :name=\"item.icon || 'file'\" size=\"sm\" />\r\n </div>\r\n </template>\r\n </JTooltip>\r\n\r\n <!-- Expanded: 아이콘 + 라벨 + 즐겨찾기 -->\r\n <div\r\n v-else\r\n :class=\"cn(\r\n 'group flex items-center gap-2 py-1 px-2 rounded-md cursor-pointer transition-colors relative',\r\n isActive\r\n ? 'bg-primary/10 text-primary font-medium'\r\n : 'text-foreground hover:bg-accent/50',\r\n item.disabled && 'opacity-40 cursor-not-allowed'\r\n )\"\r\n @click=\"handleClick\"\r\n >\r\n <!-- 활성 좌측 바 -->\r\n <div\r\n v-if=\"isActive\"\r\n class=\"absolute left-0 top-1 bottom-1 w-[3px] rounded-r bg-primary\"\r\n />\r\n\r\n <JIcon\r\n v-if=\"item.icon\"\r\n :name=\"item.icon\"\r\n size=\"sm\"\r\n class=\"flex-shrink-0\"\r\n />\r\n <span class=\"flex-1 truncate text-xs\">{{ item.label }}</span>\r\n\r\n <!-- 즐겨찾기 별 -->\r\n <button\r\n :class=\"cn(\r\n 'flex-shrink-0 p-0.5 rounded transition-opacity',\r\n isFav\r\n ? 'opacity-100 text-yellow-500'\r\n : 'opacity-0 group-hover:opacity-100 text-muted-foreground hover:text-yellow-500'\r\n )\"\r\n @click=\"handleFavoriteClick\"\r\n >\r\n <JIcon\r\n name=\"star\"\r\n size=\"sm\"\r\n :class=\"isFav ? 'fill-yellow-500' : undefined\"\r\n />\r\n </button>\r\n </div>\r\n</template>\r\n"],"names":["props","__props","emit","__emit","state","inject","SIDEBAR_INJECTION_KEY","isActive","computed","isFav","handleClick","event","handleFavoriteClick","_unref","_createBlock","JTooltip","_createElementVNode","cn","_createVNode","JIcon","_createElementBlock","_openBlock","_hoisted_1","_hoisted_2","_toDisplayString","_normalizeClass"],"mappings":";;;;;;;;;;;;;;;AAaA,UAAMA,IAAQC,GAIRC,IAAOC,GAIPC,IAAQC,EAAOC,CAAqB,GAEpCC,IAAWC,EAAS,MACpB,CAACR,EAAM,KAAK,QAAQ,CAACI,EAAM,aAAmB,KAC3CA,EAAM,eAAeJ,EAAM,KAAK,IACxC,GAEKS,IAAQD,EAAS,MAAMJ,EAAM,UAAU,IAAIJ,EAAM,KAAK,EAAE,CAAC,GAEzDU,IAAc,CAACC,MAAsB;AACzC,MAAIX,EAAM,KAAK,YACfE,EAAK,cAAcF,EAAM,MAAMW,CAAK;AAAA,IACtC,GAEMC,IAAsB,CAACD,MAAsB;AACjD,MAAAA,EAAM,gBAAA,GACNP,EAAM,eAAeJ,EAAM,KAAK,EAAE;AAAA,IACpC;qBAMUa,EAAAT,CAAA,EAAM,kBADdU,EAoBWC,GAAA;AAAA;MAlBR,SAASd,EAAA,KAAK;AAAA,MACf,MAAK;AAAA,MACL,MAAK;AAAA,IAAA;MAEM,WACT,MAWM;AAAA,QAXNe,EAWM,OAAA;AAAA,UAVH,SAAOH,EAAAI,CAAA;AAAA;YAAsHV,EAAA;YAAqHN,EAAA,KAAK,YAAQ;AAAA,UAAA;UAO/P,SAAOS;AAAA,QAAA;UAERQ,EAA+CC,GAAA;AAAA,YAAvC,MAAMlB,EAAA,KAAK,QAAI;AAAA,YAAY,MAAK;AAAA,UAAA;;;;gCAM9CmB,EAyCM,OAAA;AAAA;MAvCH,SAAOP,EAAAI,CAAA;AAAA;QAAkHV,EAAA;QAAqHN,EAAA,KAAK,YAAQ;AAAA,MAAA;MAO3P,SAAOS;AAAA,IAAA;MAIAH,EAAA,SADRc,EAAA,GAAAD,EAGE,OAHFE,CAGE;MAGMrB,EAAA,KAAK,aADba,EAKEK,GAAA;AAAA;QAHC,MAAMlB,EAAA,KAAK;AAAA,QACZ,MAAK;AAAA,QACL,OAAM;AAAA,MAAA;MAERe,EAA6D,QAA7DO,GAA6DC,EAApBvB,EAAA,KAAK,KAAK,GAAA,CAAA;AAAA,MAGnDe,EAcS,UAAA;AAAA,QAbN,SAAOH,EAAAI,CAAA;AAAA;UAAwER,EAAA;;QAM/E,SAAOG;AAAA,MAAA;QAERM,EAIEC,GAAA;AAAA,UAHA,MAAK;AAAA,UACL,MAAK;AAAA,UACJ,OAAKM,EAAEhB,EAAA,QAAK,oBAAuB,MAAS;AAAA,QAAA;;;;;"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("./JSidebarItem.vue.cjs");exports.default=e.default;
2
+ //# sourceMappingURL=JSidebarItem.vue2.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JSidebarItem.vue2.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ import f from "./JSidebarItem.vue.js";
2
+ export {
3
+ f as default
4
+ };
5
+ //# sourceMappingURL=JSidebarItem.vue2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JSidebarItem.vue2.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -3,5 +3,5 @@
3
3
  for (const [t_key, t_val] of t_opts)
4
4
  t_merged[t_key] = t_val;
5
5
  return t_merged;
6
- };,u=t(e.default,[["__scopeId","data-v-63f4ba27"]]);exports.default=u;
6
+ };,u=t(e.default,[["__scopeId","data-v-16c5d086"]]);exports.default=u;
7
7
  //# sourceMappingURL=JSidebarAdvanced.vue.cjs.map
@@ -1,13 +1,13 @@
1
1
  import o from "./JSidebarAdvanced.vue2.js";
2
2
  /* empty css */
3
- const a = (a_comp, a_opts) => {
4
- const a_merged = a_comp.__vccOpts || a_comp;
5
- for (const [a_key, a_val] of a_opts)
6
- a_merged[a_key] = a_val;
7
- return a_merged;
3
+ const r = (r_comp, r_opts) => {
4
+ const r_merged = r_comp.__vccOpts || r_comp;
5
+ for (const [r_key, r_val] of r_opts)
6
+ r_merged[r_key] = r_val;
7
+ return r_merged;
8
8
  };
9
- const e = /* @__PURE__ */ a(o, [["__scopeId", "data-v-63f4ba27"]]);
9
+ const c = /* @__PURE__ */ r(o, [["__scopeId", "data-v-16c5d086"]]);
10
10
  export {
11
- e as default
11
+ c as default
12
12
  };
13
13
  //# sourceMappingURL=JSidebarAdvanced.vue.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"JSidebarAdvanced.vue2.cjs","sources":["../../../../src/components/organisms/JSidebarAdvanced.vue"],"sourcesContent":["<script setup lang=\"ts\">\r\nimport { ref, computed, watch } from 'vue'\r\nimport { useRoute } from 'vue-router'\r\nimport type { SidebarMenuItem, MenuPermission, MenuClickEvent } from '@/types/sidebar-menu.types'\r\nimport JDynamicMenuItem from './JSidebarSimple/JDynamicMenuItem.vue'\r\nimport JInput from '@/components/atoms/JInput.vue'\r\nimport JIcon from '@/components/atoms/JIcon.vue'\r\nimport { cn } from '@/lib/utils'\r\n\r\n/**\r\n * JSidebarAdvanced - 고급 사이드바 컴포넌트\r\n * Advanced Sidebar Component\r\n * \r\n * @description\r\n * 검색, 즐겨찾기, 다단계 메뉴를 지원하는 고급 사이드바 컴포넌트입니다.\r\n * 기본 메뉴와 즐겨찾기 탭을 제공합니다.\r\n * \r\n * @example\r\n * ```vue\r\n * <JSidebarAdvanced\r\n * :menu-items=\"menuItems\"\r\n * :permissions=\"userPermissions\"\r\n * :favorites=\"favoriteMenuKeys\"\r\n * @menu-click=\"handleMenuClick\"\r\n * @favorite-change=\"handleFavoriteChange\"\r\n * />\r\n * ```\r\n * \r\n * @example JSON 메뉴 데이터 예시\r\n * ```json\r\n * [\r\n * {\r\n * \"label\": \"대시보드\",\r\n * \"icon\": \"house\",\r\n * \"menuType\": \"L\",\r\n * \"menuKey\": 1,\r\n * \"path\": \"/dashboard\"\r\n * },\r\n * {\r\n * \"label\": \"재고 관리\",\r\n * \"icon\": \"package\",\r\n * \"menuType\": \"F\",\r\n * \"menuKey\": 2,\r\n * \"children\": [\r\n * {\r\n * \"label\": \"재고 현황\",\r\n * \"menuType\": \"L\",\r\n * \"menuKey\": 21,\r\n * \"path\": \"/inventory/status\"\r\n * },\r\n * {\r\n * \"label\": \"입고 관리\",\r\n * \"menuType\": \"F\",\r\n * \"menuKey\": 22,\r\n * \"children\": [\r\n * {\r\n * \"label\": \"입고 등록\",\r\n * \"menuType\": \"L\",\r\n * \"menuKey\": 221,\r\n * \"path\": \"/inventory/receiving/register\"\r\n * }\r\n * ]\r\n * }\r\n * ]\r\n * }\r\n * ]\r\n * ```\r\n */\r\n\r\ntype TabType = 'menu' | 'favorites'\r\n\r\ntype StyleType = 'default' | 'minimal'\r\n\r\nconst props = withDefaults(\r\n defineProps<{\r\n /** 메뉴 아이템 목록 */\r\n menuItems: SidebarMenuItem[]\r\n /** 권한 목록 */\r\n permissions?: MenuPermission[]\r\n /** 즐겨찾기 메뉴 키 목록 */\r\n favorites?: (number | string)[]\r\n /** 스타일 타입 */\r\n styletype?: StyleType\r\n /** 추가 CSS 클래스 */\r\n class?: string\r\n /** 너비 */\r\n width?: string\r\n /** 표시 여부 */\r\n isVisible?: boolean\r\n }>(),\r\n {\r\n permissions: () => [],\r\n favorites: () => [],\r\n styletype: 'minimal',\r\n width: '280px',\r\n isVisible: true,\r\n },\r\n)\r\n\r\nconst emit = defineEmits<{\r\n /** 메뉴 클릭 이벤트 */\r\n menuClick: [event: MenuClickEvent]\r\n /** 즐겨찾기 변경 이벤트 */\r\n favoriteChange: [menuKey: number | string | undefined, isFavorite: boolean]\r\n}>()\r\n\r\n// vue-router가 설정되지 않은 경우를 대비 (Storybook에서 router가 제공됨)\r\nconst route = useRoute()\r\n\r\n/**\r\n * 현재 활성 탭\r\n */\r\nconst activeTab = ref<TabType>('menu')\r\n\r\n/**\r\n * 검색어\r\n */\r\nconst searchQuery = ref('')\r\n\r\n/**\r\n * 현재 활성화된 경로\r\n */\r\nconst activePath = computed(() => route.path)\r\n\r\n/**\r\n * 확장된 메뉴 키 목록\r\n */\r\nconst expandedKeys = ref<Set<number | string>>(new Set())\r\n\r\n/**\r\n * 즐겨찾기 메뉴 아이템 목록\r\n * 즐겨찾기는 dept 없이 1단계로만 평탄화하여 표시\r\n */\r\nconst favoriteMenuItems = computed(() => {\r\n if (!Array.isArray(props.favorites) || props.favorites.length === 0) {\r\n return []\r\n }\r\n\r\n if (!Array.isArray(props.menuItems) || props.menuItems.length === 0) {\r\n return []\r\n }\r\n\r\n /**\r\n * 메뉴 아이템을 재귀적으로 순회하며 즐겨찾기만 추출\r\n * 즐겨찾기에서는 dept 없이 1단계로만 평탄화\r\n * menuType L(Link)만 포함하고 F(Folder)는 제외\r\n */\r\n const flattenFavorites = (items: SidebarMenuItem[]): SidebarMenuItem[] => {\r\n const result: SidebarMenuItem[] = []\r\n\r\n if (!Array.isArray(items)) {\r\n return result\r\n }\r\n\r\n for (const item of items) {\r\n const key = item.menuKey || item.label\r\n const isFavorite = Array.isArray(props.favorites) && props.favorites.includes(key)\r\n\r\n // 즐겨찾기이고 menuType이 L(Link)인 경우만 추가 (F는 제외)\r\n if (isFavorite && item.menuType === 'L') {\r\n result.push({\r\n ...item,\r\n children: undefined, // children 제거하여 1단계로만 표시\r\n })\r\n }\r\n\r\n // 하위 메뉴도 재귀적으로 탐색\r\n if (item.children && Array.isArray(item.children) && item.children.length > 0) {\r\n const childFavorites = flattenFavorites(item.children)\r\n result.push(...childFavorites)\r\n }\r\n }\r\n\r\n return result\r\n }\r\n\r\n return flattenFavorites(props.menuItems)\r\n})\r\n\r\n/**\r\n * 검색어로 필터링된 메뉴 아이템\r\n * 재귀적으로 children까지 검색\r\n */\r\nconst filteredMenuItems = computed(() => {\r\n if (!Array.isArray(props.menuItems) || props.menuItems.length === 0) {\r\n return []\r\n }\r\n\r\n if (!searchQuery.value || searchQuery.value.trim() === '') {\r\n return props.menuItems\r\n }\r\n\r\n const query = searchQuery.value.toLowerCase().trim()\r\n\r\n /**\r\n * 메뉴 아이템을 재귀적으로 검색\r\n */\r\n const searchInMenu = (items: SidebarMenuItem[]): SidebarMenuItem[] => {\r\n const result: SidebarMenuItem[] = []\r\n\r\n if (!Array.isArray(items)) {\r\n return result\r\n }\r\n\r\n for (const item of items) {\r\n const matchesLabel = item.label?.toLowerCase().includes(query) ?? false\r\n\r\n // 하위 메뉴 검색\r\n let filteredChildren: SidebarMenuItem[] | undefined = undefined\r\n if (item.children && Array.isArray(item.children) && item.children.length > 0) {\r\n filteredChildren = searchInMenu(item.children)\r\n }\r\n\r\n // 현재 메뉴나 하위 메뉴 중 하나라도 매칭되면 포함\r\n if (matchesLabel || (Array.isArray(filteredChildren) && filteredChildren.length > 0)) {\r\n result.push({\r\n ...item,\r\n children: filteredChildren,\r\n })\r\n }\r\n }\r\n\r\n return result\r\n }\r\n\r\n return searchInMenu(props.menuItems)\r\n})\r\n\r\n/**\r\n * 검색 결과에 따라 부모 메뉴 자동 확장\r\n * computed 외부에서 watch를 통해 처리\r\n */\r\nwatch(\r\n () => filteredMenuItems.value,\r\n (filtered) => {\r\n if (!searchQuery.value || searchQuery.value.trim() === '') {\r\n return\r\n }\r\n\r\n // 검색 결과에서 매칭된 하위 메뉴가 있는 부모를 찾아 확장\r\n const findParentsWithMatches = (items: SidebarMenuItem[]): Set<number | string> => {\r\n const keysToExpand = new Set<number | string>()\r\n\r\n const traverse = (menuItems: SidebarMenuItem[]): void => {\r\n if (!Array.isArray(menuItems)) {\r\n return\r\n }\r\n\r\n for (const item of menuItems) {\r\n if (item.children && Array.isArray(item.children) && item.children.length > 0) {\r\n const key = item.menuKey || item.label\r\n keysToExpand.add(key)\r\n traverse(item.children)\r\n }\r\n }\r\n }\r\n\r\n traverse(items)\r\n return keysToExpand\r\n }\r\n\r\n const keysToExpand = findParentsWithMatches(filtered)\r\n keysToExpand.forEach(key => {\r\n expandedKeys.value.add(key)\r\n })\r\n },\r\n { immediate: false }\r\n)\r\n\r\n/**\r\n * 검색어로 필터링된 즐겨찾기 메뉴 아이템\r\n * 즐겨찾기는 이미 평탄화되어 있으므로 단순 필터링만 수행\r\n */\r\nconst filteredFavoriteItems = computed(() => {\r\n if (!Array.isArray(favoriteMenuItems.value) || favoriteMenuItems.value.length === 0) {\r\n return []\r\n }\r\n\r\n if (!searchQuery.value || searchQuery.value.trim() === '') {\r\n return favoriteMenuItems.value\r\n }\r\n\r\n const query = searchQuery.value.toLowerCase().trim()\r\n\r\n // 즐겨찾기는 이미 평탄화되어 1단계이므로 단순 필터링만 수행\r\n return favoriteMenuItems.value.filter((item) =>\r\n item.label?.toLowerCase().includes(query) ?? false\r\n )\r\n})\r\n\r\n\r\n/**\r\n * 탭 변경 핸들러\r\n * 탭 전환 시 검색 쿼리를 초기화하여 각 탭의 독립적인 검색 상태 유지\r\n */\r\nconst handleTabChange = (tab: TabType) => {\r\n if (activeTab.value !== tab) {\r\n activeTab.value = tab\r\n // 탭 전환 시 검색 쿼리 초기화 (선택적 - UX 고려)\r\n // 검색 쿼리를 유지하려면 아래 라인을 제거하세요\r\n searchQuery.value = ''\r\n }\r\n}\r\n\r\n/**\r\n * 확장 상태 변경 핸들러\r\n */\r\nconst handleExpandChange = (menuKey: number | string | undefined, expanded: boolean) => {\r\n if (!menuKey) return\r\n\r\n if (expanded) {\r\n expandedKeys.value.add(menuKey)\r\n } else {\r\n expandedKeys.value.delete(menuKey)\r\n }\r\n}\r\n\r\n/**\r\n * 메뉴 클릭 핸들러\r\n */\r\nconst handleMenuClick = (event: MenuClickEvent) => {\r\n emit('menuClick', event)\r\n}\r\n\r\n/**\r\n * 즐겨찾기 토글 핸들러\r\n */\r\nconst handleFavoriteToggle = (menuKey: number | string | undefined) => {\r\n if (!menuKey) return\r\n\r\n const isFavorite = props.favorites?.includes(menuKey) ?? false\r\n emit('favoriteChange', menuKey, !isFavorite)\r\n}\r\n\r\n/**\r\n * 메뉴 아이템에서 특정 menuKey를 찾는 헬퍼 함수\r\n */\r\nconst findMenuItemByKey = (items: SidebarMenuItem[], targetKey: number | string): SidebarMenuItem | null => {\r\n for (const item of items) {\r\n const key = item.menuKey || item.label\r\n if (key === targetKey) {\r\n return item\r\n }\r\n if (item.children && item.children.length > 0) {\r\n const found = findMenuItemByKey(item.children, targetKey)\r\n if (found) return found\r\n }\r\n }\r\n return null\r\n}\r\n\r\n/**\r\n * 메뉴가 즐겨찾기인지 확인 (L 타입만 즐겨찾기 가능)\r\n */\r\nconst isFavorite = (menuKey: number | string | undefined): boolean => {\r\n if (!menuKey) return false\r\n if (!props.favorites?.includes(menuKey)) return false\r\n \r\n // menuType이 L인 경우만 즐겨찾기로 인정\r\n const menuItem = findMenuItemByKey(props.menuItems, menuKey)\r\n return menuItem?.menuType === 'L'\r\n}\r\n\r\n/**\n * 스타일 프리셋\n */\nconst STYLE_PRESETS: Record<StyleType, {\n containerClass: string\n tabContainerClass: string\n tabButtonClass: string\n searchContainerClass: string\n menuContainerClass: string\n}> = {\n default: {\n containerClass: 'h-full bg-background border-r border-border flex flex-col',\n tabContainerClass: 'flex border-b border-border',\n tabButtonClass: 'flex-1 px-3 py-1.5 text-xs font-medium transition-colors border-b-2 hover:bg-accent/50',\n searchContainerClass: 'p-1.5 border-b border-border',\n menuContainerClass: 'flex-1 overflow-y-auto p-1.5 space-y-0.5',\n },\n minimal: {\n containerClass: 'h-full bg-background border-r border-border flex flex-col',\n tabContainerClass: 'flex border-b border-border',\n tabButtonClass: 'flex-1 px-2 py-1 text-xs font-medium transition-colors border-b-2',\n searchContainerClass: 'p-1 border-b border-border',\n menuContainerClass: 'flex-1 overflow-y-auto p-1 space-y-0.5',\n },\n}\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 rootClasses = computed(() => {\r\n return cn(\r\n preset.value.containerClass,\r\n props.class\r\n )\r\n})\r\n</script>\r\n\r\n<template>\r\n <Transition name=\"slide\">\r\n <aside v-show=\"props.isVisible\" :class=\"rootClasses\" :style=\"{ width }\">\r\n <!-- 탭 헤더 -->\r\n <div :class=\"preset.tabContainerClass\">\r\n <button\r\n :class=\"cn(\r\n preset.tabButtonClass,\r\n activeTab === 'menu'\r\n ? 'border-primary text-primary'\r\n : 'border-transparent text-muted-foreground hover:text-foreground'\r\n )\"\r\n @click=\"handleTabChange('menu')\"\r\n >\r\n 기본메뉴\r\n </button>\r\n <button\r\n :class=\"cn(\r\n preset.tabButtonClass,\r\n activeTab === 'favorites'\r\n ? 'border-primary text-primary'\r\n : 'border-transparent text-muted-foreground hover:text-foreground'\r\n )\"\r\n @click=\"handleTabChange('favorites')\"\r\n >\r\n 즐겨찾기\r\n </button>\r\n </div>\r\n\r\n <!-- 검색 영역 -->\r\n <div :class=\"preset.searchContainerClass\">\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=\"cn(\r\n 'pl-8',\r\n props.styletype === 'minimal' && 'h-8 text-xs'\r\n )\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <!-- 메뉴 목록 -->\r\n <div :class=\"preset.menuContainerClass\">\r\n <template v-if=\"activeTab === 'menu'\">\r\n <template v-if=\"filteredMenuItems.length > 0\">\r\n <div\r\n v-for=\"(item, index) in filteredMenuItems\"\r\n :key=\"item.menuKey || item.label || index\"\r\n class=\"flex items-center group\"\r\n >\r\n <JDynamicMenuItem\r\n :item=\"item\"\r\n :level=\"0\"\r\n :permissions=\"permissions\"\r\n :active-path=\"activePath\"\r\n :expanded-keys=\"expandedKeys\"\r\n :favorites=\"favorites\"\r\n :on-favorite-toggle=\"handleFavoriteToggle\"\r\n :is-favorite=\"isFavorite\"\r\n :styletype=\"styletype\"\r\n class=\"flex-1\"\r\n @menu-click=\"handleMenuClick\"\r\n @expand-change=\"handleExpandChange\"\r\n />\r\n </div>\r\n </template>\n <div v-else class=\"text-center py-8 text-muted-foreground\">\n <p class=\"text-xs\">검색 결과가 없습니다.</p>\n </div>\n </template>\n\n <template v-else>\n <template v-if=\"filteredFavoriteItems.length > 0\">\n <JDynamicMenuItem\n v-for=\"(item, index) in filteredFavoriteItems\"\n :key=\"item.menuKey || item.label || index\"\n :item=\"item\"\n :level=\"0\"\n :permissions=\"permissions\"\n :active-path=\"activePath\"\n :expanded-keys=\"expandedKeys\"\n :styletype=\"styletype\"\n @menu-click=\"handleMenuClick\"\n @expand-change=\"handleExpandChange\"\n />\n </template>\n <div v-else class=\"text-center py-8 text-muted-foreground\">\n <p class=\"text-xs\">즐겨찾기가 없습니다.</p>\n </div>\n </template>\r\n </div>\r\n </aside>\r\n </Transition>\r\n</template>\r\n\r\n<style scoped>\r\n.slide-enter-active,\r\n.slide-leave-active {\r\n transition: transform 0.3s ease, opacity 0.3s ease;\r\n}\r\n\r\n.slide-enter-from,\r\n.slide-leave-to {\r\n transform: translateX(-100%);\r\n opacity: 0;\r\n}\r\n</style>\r\n"],"names":["props","__props","emit","__emit","route","useRoute","activeTab","ref","searchQuery","activePath","computed","expandedKeys","favoriteMenuItems","flattenFavorites","items","result","item","key","childFavorites","filteredMenuItems","query","searchInMenu","matchesLabel","filteredChildren","watch","filtered","keysToExpand","traverse","menuItems","filteredFavoriteItems","handleTabChange","tab","handleExpandChange","menuKey","expanded","handleMenuClick","event","handleFavoriteToggle","isFavorite","findMenuItemByKey","targetKey","found","STYLE_PRESETS","preset","rootClasses","cn","_createBlock","_Transition","_createElementVNode","_normalizeClass","_unref","_hoisted_1","_createVNode","JIcon","JInput","$event","_createElementBlock","_Fragment","_openBlock","_renderList","index","JDynamicMenuItem","_hoisted_2","_cache","_hoisted_3","_vShow"],"mappings":"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"}
1
+ {"version":3,"file":"JSidebarAdvanced.vue2.cjs","sources":["../../../../src/components/organisms/JSidebarAdvanced.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, watch } from 'vue'\nimport { useRoute } from 'vue-router'\nimport type { SidebarMenuItem, MenuPermission, MenuClickEvent } from '@/types/sidebar-menu.types'\nimport JDynamicMenuItem from './JSidebarSimple/JDynamicMenuItem.vue'\nimport JInput from '@/components/atoms/JInput.vue'\nimport JIcon from '@/components/atoms/JIcon.vue'\nimport { cn } from '@/lib/utils'\n\n/**\n * JSidebarAdvanced - 고급 사이드바 컴포넌트\n * Advanced Sidebar Component\n * \n * @description\n * 검색, 즐겨찾기, 다단계 메뉴를 지원하는 고급 사이드바 컴포넌트입니다.\n * 기본 메뉴와 즐겨찾기 탭을 제공합니다.\n * \n * @example\n * ```vue\n * <JSidebarAdvanced\n * :menu-items=\"menuItems\"\n * :permissions=\"userPermissions\"\n * :favorites=\"favoriteMenuKeys\"\n * @menu-click=\"handleMenuClick\"\n * @favorite-change=\"handleFavoriteChange\"\n * />\n * ```\n * \n * @example JSON 메뉴 데이터 예시\n * ```json\n * [\n * {\n * \"label\": \"대시보드\",\n * \"icon\": \"house\",\n * \"menuType\": \"L\",\n * \"menuKey\": 1,\n * \"path\": \"/dashboard\"\n * },\n * {\n * \"label\": \"재고 관리\",\n * \"icon\": \"package\",\n * \"menuType\": \"F\",\n * \"menuKey\": 2,\n * \"children\": [\n * {\n * \"label\": \"재고 현황\",\n * \"menuType\": \"L\",\n * \"menuKey\": 21,\n * \"path\": \"/inventory/status\"\n * },\n * {\n * \"label\": \"입고 관리\",\n * \"menuType\": \"F\",\n * \"menuKey\": 22,\n * \"children\": [\n * {\n * \"label\": \"입고 등록\",\n * \"menuType\": \"L\",\n * \"menuKey\": 221,\n * \"path\": \"/inventory/receiving/register\"\n * }\n * ]\n * }\n * ]\n * }\n * ]\n * ```\n */\n\ntype TabType = 'menu' | 'favorites'\n\ntype StyleType = 'default' | 'minimal'\n\nconst props = withDefaults(\n defineProps<{\n /** 메뉴 아이템 목록 */\n menuItems: SidebarMenuItem[]\n /** 권한 목록 */\n permissions?: MenuPermission[]\n /** 즐겨찾기 메뉴 키 목록 */\n favorites?: (number | string)[]\n /** 스타일 타입 */\n styletype?: StyleType\n /** 추가 CSS 클래스 */\n class?: string\n /** 너비 */\n width?: string\n /** 표시 여부 */\n isVisible?: boolean\n }>(),\n {\n permissions: () => [],\n favorites: () => [],\n styletype: 'minimal',\n width: '280px',\n isVisible: true,\n },\n)\n\nconst emit = defineEmits<{\n /** 메뉴 클릭 이벤트 */\n menuClick: [event: MenuClickEvent]\n /** 즐겨찾기 변경 이벤트 */\n favoriteChange: [menuKey: number | string | undefined, isFavorite: boolean]\n}>()\n\n// vue-router가 설정되지 않은 경우를 대비 (Storybook에서 router가 제공됨)\nconst route = useRoute()\n\n/**\n * 현재 활성 탭\n */\nconst activeTab = ref<TabType>('menu')\n\n/**\n * 검색어\n */\nconst searchQuery = ref('')\n\n/**\n * 현재 활성화된 경로\n */\nconst activePath = computed(() => route.path)\n\n/**\n * 확장된 메뉴 키 목록\n */\nconst expandedKeys = ref<Set<number | string>>(new Set())\n\n/**\n * 즐겨찾기 메뉴 아이템 목록\n * 즐겨찾기는 dept 없이 1단계로만 평탄화하여 표시\n */\nconst favoriteMenuItems = computed(() => {\n if (!Array.isArray(props.favorites) || props.favorites.length === 0) {\n return []\n }\n\n if (!Array.isArray(props.menuItems) || props.menuItems.length === 0) {\n return []\n }\n\n /**\n * 메뉴 아이템을 재귀적으로 순회하며 즐겨찾기만 추출\n * 즐겨찾기에서는 dept 없이 1단계로만 평탄화\n * menuType L(Link)만 포함하고 F(Folder)는 제외\n */\n const flattenFavorites = (items: SidebarMenuItem[]): SidebarMenuItem[] => {\n const result: SidebarMenuItem[] = []\n\n if (!Array.isArray(items)) {\n return result\n }\n\n for (const item of items) {\n const key = item.menuKey || item.label\n const isFavorite = Array.isArray(props.favorites) && props.favorites.includes(key)\n\n // 즐겨찾기이고 menuType이 L(Link)인 경우만 추가 (F는 제외)\n if (isFavorite && item.menuType === 'L') {\n result.push({\n ...item,\n children: undefined, // children 제거하여 1단계로만 표시\n })\n }\n\n // 하위 메뉴도 재귀적으로 탐색\n if (item.children && Array.isArray(item.children) && item.children.length > 0) {\n const childFavorites = flattenFavorites(item.children)\n result.push(...childFavorites)\n }\n }\n\n return result\n }\n\n return flattenFavorites(props.menuItems)\n})\n\n/**\n * 검색어로 필터링된 메뉴 아이템\n * 재귀적으로 children까지 검색\n */\nconst filteredMenuItems = computed(() => {\n if (!Array.isArray(props.menuItems) || props.menuItems.length === 0) {\n return []\n }\n\n if (!searchQuery.value || searchQuery.value.trim() === '') {\n return props.menuItems\n }\n\n const query = searchQuery.value.toLowerCase().trim()\n\n /**\n * 메뉴 아이템을 재귀적으로 검색\n */\n const searchInMenu = (items: SidebarMenuItem[]): SidebarMenuItem[] => {\n const result: SidebarMenuItem[] = []\n\n if (!Array.isArray(items)) {\n return result\n }\n\n for (const item of items) {\n const matchesLabel = item.label?.toLowerCase().includes(query) ?? false\n\n // 하위 메뉴 검색\n let filteredChildren: SidebarMenuItem[] | undefined = undefined\n if (item.children && Array.isArray(item.children) && item.children.length > 0) {\n filteredChildren = searchInMenu(item.children)\n }\n\n // 현재 메뉴나 하위 메뉴 중 하나라도 매칭되면 포함\n if (matchesLabel || (Array.isArray(filteredChildren) && filteredChildren.length > 0)) {\n result.push({\n ...item,\n children: filteredChildren,\n })\n }\n }\n\n return result\n }\n\n return searchInMenu(props.menuItems)\n})\n\n/**\n * 검색 결과에 따라 부모 메뉴 자동 확장\n * computed 외부에서 watch를 통해 처리\n */\nwatch(\n () => filteredMenuItems.value,\n (filtered) => {\n if (!searchQuery.value || searchQuery.value.trim() === '') {\n return\n }\n\n // 검색 결과에서 매칭된 하위 메뉴가 있는 부모를 찾아 확장\n const findParentsWithMatches = (items: SidebarMenuItem[]): Set<number | string> => {\n const keysToExpand = new Set<number | string>()\n\n const traverse = (menuItems: SidebarMenuItem[]): void => {\n if (!Array.isArray(menuItems)) {\n return\n }\n\n for (const item of menuItems) {\n if (item.children && Array.isArray(item.children) && item.children.length > 0) {\n const key = item.menuKey || item.label\n keysToExpand.add(key)\n traverse(item.children)\n }\n }\n }\n\n traverse(items)\n return keysToExpand\n }\n\n const keysToExpand = findParentsWithMatches(filtered)\n keysToExpand.forEach(key => {\n expandedKeys.value.add(key)\n })\n },\n { immediate: false }\n)\n\n/**\n * 검색어로 필터링된 즐겨찾기 메뉴 아이템\n * 즐겨찾기는 이미 평탄화되어 있으므로 단순 필터링만 수행\n */\nconst filteredFavoriteItems = computed(() => {\n if (!Array.isArray(favoriteMenuItems.value) || favoriteMenuItems.value.length === 0) {\n return []\n }\n\n if (!searchQuery.value || searchQuery.value.trim() === '') {\n return favoriteMenuItems.value\n }\n\n const query = searchQuery.value.toLowerCase().trim()\n\n // 즐겨찾기는 이미 평탄화되어 1단계이므로 단순 필터링만 수행\n return favoriteMenuItems.value.filter((item) =>\n item.label?.toLowerCase().includes(query) ?? false\n )\n})\n\n\n/**\n * 탭 변경 핸들러\n * 탭 전환 시 검색 쿼리를 초기화하여 각 탭의 독립적인 검색 상태 유지\n */\nconst handleTabChange = (tab: TabType) => {\n if (activeTab.value !== tab) {\n activeTab.value = tab\n // 탭 전환 시 검색 쿼리 초기화 (선택적 - UX 고려)\n // 검색 쿼리를 유지하려면 아래 라인을 제거하세요\n searchQuery.value = ''\n }\n}\n\n/**\n * 확장 상태 변경 핸들러\n */\nconst handleExpandChange = (menuKey: number | string | undefined, expanded: boolean) => {\n if (!menuKey) return\n\n if (expanded) {\n expandedKeys.value.add(menuKey)\n } else {\n expandedKeys.value.delete(menuKey)\n }\n}\n\n/**\n * 메뉴 클릭 핸들러\n */\nconst handleMenuClick = (event: MenuClickEvent) => {\n emit('menuClick', event)\n}\n\n/**\n * 즐겨찾기 토글 핸들러\n */\nconst handleFavoriteToggle = (menuKey: number | string | undefined) => {\n if (!menuKey) return\n\n const isFavorite = props.favorites?.includes(menuKey) ?? false\n emit('favoriteChange', menuKey, !isFavorite)\n}\n\n/**\n * 메뉴 아이템에서 특정 menuKey를 찾는 헬퍼 함수\n */\nconst findMenuItemByKey = (items: SidebarMenuItem[], targetKey: number | string): SidebarMenuItem | null => {\n for (const item of items) {\n const key = item.menuKey || item.label\n if (key === targetKey) {\n return item\n }\n if (item.children && item.children.length > 0) {\n const found = findMenuItemByKey(item.children, targetKey)\n if (found) return found\n }\n }\n return null\n}\n\n/**\n * 메뉴가 즐겨찾기인지 확인 (L 타입만 즐겨찾기 가능)\n */\nconst isFavorite = (menuKey: number | string | undefined): boolean => {\n if (!menuKey) return false\n if (!props.favorites?.includes(menuKey)) return false\n \n // menuType이 L인 경우만 즐겨찾기로 인정\n const menuItem = findMenuItemByKey(props.menuItems, menuKey)\n return menuItem?.menuType === 'L'\n}\n\n/**\n * 스타일 프리셋\n */\nconst STYLE_PRESETS: Record<StyleType, {\n containerClass: string\n tabContainerClass: string\n tabButtonClass: string\n searchContainerClass: string\n menuContainerClass: string\n}> = {\n default: {\n containerClass: 'h-full bg-background border-r border-border flex flex-col',\n tabContainerClass: 'flex border-b border-border',\n tabButtonClass: 'flex-1 px-3 py-1.5 text-xs font-medium transition-colors border-b-2 hover:bg-accent/50',\n searchContainerClass: 'p-1.5 border-b border-border',\n menuContainerClass: 'flex-1 overflow-y-auto p-1.5 space-y-0.5',\n },\n minimal: {\n containerClass: 'h-full bg-background border-r border-border flex flex-col',\n tabContainerClass: 'flex border-b border-border',\n tabButtonClass: 'flex-1 px-2 py-1 text-xs font-medium transition-colors border-b-2',\n searchContainerClass: 'p-1 border-b border-border',\n menuContainerClass: 'flex-1 overflow-y-auto p-1 space-y-0.5',\n },\n}\n\nconst preset = computed(() => {\n return STYLE_PRESETS[props.styletype] ?? STYLE_PRESETS.default\n})\n\n/**\n * 루트 클래스\n */\nconst rootClasses = computed(() => {\n return cn(\n preset.value.containerClass,\n props.class\n )\n})\n</script>\n\n<template>\n <Transition name=\"slide\">\n <aside v-show=\"props.isVisible\" :class=\"rootClasses\" :style=\"{ width }\">\n <!-- 탭 헤더 -->\n <div :class=\"preset.tabContainerClass\">\n <button\n :class=\"cn(\n preset.tabButtonClass,\n activeTab === 'menu'\n ? 'border-primary text-primary'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n )\"\n @click=\"handleTabChange('menu')\"\n >\n 기본메뉴\n </button>\n <button\n :class=\"cn(\n preset.tabButtonClass,\n activeTab === 'favorites'\n ? 'border-primary text-primary'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n )\"\n @click=\"handleTabChange('favorites')\"\n >\n 즐겨찾기\n </button>\n </div>\n\n <!-- 검색 영역 -->\n <div :class=\"preset.searchContainerClass\">\n <div class=\"relative\">\n <JIcon\n name=\"search\"\n size=\"sm\"\n class=\"absolute left-2 top-1/2 -translate-y-1/2 text-muted-foreground\"\n />\n <JInput\n v-model=\"searchQuery\"\n placeholder=\"메뉴 검색...\"\n :class=\"cn(\n 'pl-8',\n props.styletype === 'minimal' && 'h-8 text-xs'\n )\"\n />\n </div>\n </div>\n\n <!-- 메뉴 목록 -->\n <div :class=\"preset.menuContainerClass\">\n <template v-if=\"activeTab === 'menu'\">\n <template v-if=\"filteredMenuItems.length > 0\">\n <div\n v-for=\"(item, index) in filteredMenuItems\"\n :key=\"item.menuKey || item.label || index\"\n class=\"flex items-center group\"\n >\n <JDynamicMenuItem\n :item=\"item\"\n :level=\"0\"\n :permissions=\"permissions\"\n :active-path=\"activePath\"\n :expanded-keys=\"expandedKeys\"\n :favorites=\"favorites\"\n :on-favorite-toggle=\"handleFavoriteToggle\"\n :is-favorite=\"isFavorite\"\n :styletype=\"styletype\"\n class=\"flex-1\"\n @menu-click=\"handleMenuClick\"\n @expand-change=\"handleExpandChange\"\n />\n </div>\n </template>\n <div v-else class=\"text-center py-8 text-muted-foreground\">\n <p class=\"text-xs\">검색 결과가 없습니다.</p>\n </div>\n </template>\n\n <template v-else>\n <template v-if=\"filteredFavoriteItems.length > 0\">\n <JDynamicMenuItem\n v-for=\"(item, index) in filteredFavoriteItems\"\n :key=\"item.menuKey || item.label || index\"\n :item=\"item\"\n :level=\"0\"\n :permissions=\"permissions\"\n :active-path=\"activePath\"\n :expanded-keys=\"expandedKeys\"\n :styletype=\"styletype\"\n @menu-click=\"handleMenuClick\"\n @expand-change=\"handleExpandChange\"\n />\n </template>\n <div v-else class=\"text-center py-8 text-muted-foreground\">\n <p class=\"text-xs\">즐겨찾기가 없습니다.</p>\n </div>\n </template>\n </div>\n </aside>\n </Transition>\n</template>\n\n<style scoped>\n.slide-enter-active,\n.slide-leave-active {\n transition: transform 0.3s ease, opacity 0.3s ease;\n}\n\n.slide-enter-from,\n.slide-leave-to {\n transform: translateX(-100%);\n opacity: 0;\n}\n</style>\n"],"names":["props","__props","emit","__emit","route","useRoute","activeTab","ref","searchQuery","activePath","computed","expandedKeys","favoriteMenuItems","flattenFavorites","items","result","item","key","childFavorites","filteredMenuItems","query","searchInMenu","matchesLabel","filteredChildren","watch","filtered","keysToExpand","traverse","menuItems","filteredFavoriteItems","handleTabChange","tab","handleExpandChange","menuKey","expanded","handleMenuClick","event","handleFavoriteToggle","isFavorite","findMenuItemByKey","targetKey","found","STYLE_PRESETS","preset","rootClasses","cn","_createBlock","_Transition","_createElementVNode","_normalizeClass","_unref","_hoisted_1","_createVNode","JIcon","JInput","$event","_createElementBlock","_Fragment","_openBlock","_renderList","index","JDynamicMenuItem","_hoisted_2","_cache","_hoisted_3","_vShow"],"mappings":"ytBAyEA,MAAMA,EAAQC,EA0BRC,EAAOC,EAQPC,EAAQC,EAAAA,SAAA,EAKRC,EAAYC,EAAAA,IAAa,MAAM,EAK/BC,EAAcD,EAAAA,IAAI,EAAE,EAKpBE,EAAaC,EAAAA,SAAS,IAAMN,EAAM,IAAI,EAKtCO,EAAeJ,EAAAA,IAA0B,IAAI,GAAK,EAMlDK,EAAoBF,EAAAA,SAAS,IAAM,CACvC,GAAI,CAAC,MAAM,QAAQV,EAAM,SAAS,GAAKA,EAAM,UAAU,SAAW,EAChE,MAAO,CAAA,EAGT,GAAI,CAAC,MAAM,QAAQA,EAAM,SAAS,GAAKA,EAAM,UAAU,SAAW,EAChE,MAAO,CAAA,EAQT,MAAMa,EAAoBC,GAAgD,CACxE,MAAMC,EAA4B,CAAA,EAElC,GAAI,CAAC,MAAM,QAAQD,CAAK,EACtB,OAAOC,EAGT,UAAWC,KAAQF,EAAO,CACxB,MAAMG,EAAMD,EAAK,SAAWA,EAAK,MAYjC,GAXmB,MAAM,QAAQhB,EAAM,SAAS,GAAKA,EAAM,UAAU,SAASiB,CAAG,GAG/DD,EAAK,WAAa,KAClCD,EAAO,KAAK,CACV,GAAGC,EACH,SAAU,MAAA,CACX,EAICA,EAAK,UAAY,MAAM,QAAQA,EAAK,QAAQ,GAAKA,EAAK,SAAS,OAAS,EAAG,CAC7E,MAAME,EAAiBL,EAAiBG,EAAK,QAAQ,EACrDD,EAAO,KAAK,GAAGG,CAAc,CAC/B,CACF,CAEA,OAAOH,CACT,EAEA,OAAOF,EAAiBb,EAAM,SAAS,CACzC,CAAC,EAMKmB,EAAoBT,EAAAA,SAAS,IAAM,CACvC,GAAI,CAAC,MAAM,QAAQV,EAAM,SAAS,GAAKA,EAAM,UAAU,SAAW,EAChE,MAAO,CAAA,EAGT,GAAI,CAACQ,EAAY,OAASA,EAAY,MAAM,KAAA,IAAW,GACrD,OAAOR,EAAM,UAGf,MAAMoB,EAAQZ,EAAY,MAAM,YAAA,EAAc,KAAA,EAKxCa,EAAgBP,GAAgD,CACpE,MAAMC,EAA4B,CAAA,EAElC,GAAI,CAAC,MAAM,QAAQD,CAAK,EACtB,OAAOC,EAGT,UAAWC,KAAQF,EAAO,CACxB,MAAMQ,EAAeN,EAAK,OAAO,cAAc,SAASI,CAAK,GAAK,GAGlE,IAAIG,EACAP,EAAK,UAAY,MAAM,QAAQA,EAAK,QAAQ,GAAKA,EAAK,SAAS,OAAS,IAC1EO,EAAmBF,EAAaL,EAAK,QAAQ,IAI3CM,GAAiB,MAAM,QAAQC,CAAgB,GAAKA,EAAiB,OAAS,IAChFR,EAAO,KAAK,CACV,GAAGC,EACH,SAAUO,CAAA,CACX,CAEL,CAEA,OAAOR,CACT,EAEA,OAAOM,EAAarB,EAAM,SAAS,CACrC,CAAC,EAMDwB,EAAAA,MACE,IAAML,EAAkB,MACvBM,GAAa,CACZ,GAAI,CAACjB,EAAY,OAASA,EAAY,MAAM,KAAA,IAAW,GACrD,QAI8BM,GAAmD,CACjF,MAAMY,MAAmB,IAEnBC,EAAYC,GAAuC,CACvD,GAAK,MAAM,QAAQA,CAAS,GAI5B,UAAWZ,KAAQY,EACjB,GAAIZ,EAAK,UAAY,MAAM,QAAQA,EAAK,QAAQ,GAAKA,EAAK,SAAS,OAAS,EAAG,CAC7E,MAAMC,EAAMD,EAAK,SAAWA,EAAK,MACjCU,EAAa,IAAIT,CAAG,EACpBU,EAASX,EAAK,QAAQ,CACxB,EAEJ,EAEA,OAAAW,EAASb,CAAK,EACPY,CACT,GAE4CD,CAAQ,EACvC,QAAQR,GAAO,CAC1BN,EAAa,MAAM,IAAIM,CAAG,CAC5B,CAAC,CACH,EACA,CAAE,UAAW,EAAA,CAAM,EAOrB,MAAMY,EAAwBnB,EAAAA,SAAS,IAAM,CAC3C,GAAI,CAAC,MAAM,QAAQE,EAAkB,KAAK,GAAKA,EAAkB,MAAM,SAAW,EAChF,MAAO,CAAA,EAGT,GAAI,CAACJ,EAAY,OAASA,EAAY,MAAM,KAAA,IAAW,GACrD,OAAOI,EAAkB,MAG3B,MAAMQ,EAAQZ,EAAY,MAAM,YAAA,EAAc,KAAA,EAG9C,OAAOI,EAAkB,MAAM,OAAQI,GACrCA,EAAK,OAAO,cAAc,SAASI,CAAK,GAAK,EAAA,CAEjD,CAAC,EAOKU,EAAmBC,GAAiB,CACpCzB,EAAU,QAAUyB,IACtBzB,EAAU,MAAQyB,EAGlBvB,EAAY,MAAQ,GAExB,EAKMwB,EAAqB,CAACC,EAAsCC,IAAsB,CACjFD,IAEDC,EACFvB,EAAa,MAAM,IAAIsB,CAAO,EAE9BtB,EAAa,MAAM,OAAOsB,CAAO,EAErC,EAKME,EAAmBC,GAA0B,CACjDlC,EAAK,YAAakC,CAAK,CACzB,EAKMC,EAAwBJ,GAAyC,CACrE,GAAI,CAACA,EAAS,OAEd,MAAMK,EAAatC,EAAM,WAAW,SAASiC,CAAO,GAAK,GACzD/B,EAAK,iBAAkB+B,EAAS,CAACK,CAAU,CAC7C,EAKMC,EAAoB,CAACzB,EAA0B0B,IAAuD,CAC1G,UAAWxB,KAAQF,EAAO,CAExB,IADYE,EAAK,SAAWA,EAAK,SACrBwB,EACV,OAAOxB,EAET,GAAIA,EAAK,UAAYA,EAAK,SAAS,OAAS,EAAG,CAC7C,MAAMyB,EAAQF,EAAkBvB,EAAK,SAAUwB,CAAS,EACxD,GAAIC,EAAO,OAAOA,CACpB,CACF,CACA,OAAO,IACT,EAKMH,EAAcL,GACd,CAACA,GACD,CAACjC,EAAM,WAAW,SAASiC,CAAO,EAAU,GAG/BM,EAAkBvC,EAAM,UAAWiC,CAAO,GAC1C,WAAa,IAM1BS,EAMD,CACH,QAAS,CACP,eAAgB,4DAChB,kBAAmB,8BACnB,eAAgB,yFAChB,qBAAsB,+BACtB,mBAAoB,0CAAA,EAEtB,QAAS,CACP,eAAgB,4DAChB,kBAAmB,8BACnB,eAAgB,oEAChB,qBAAsB,6BACtB,mBAAoB,wCAAA,CACtB,EAGIC,EAASjC,EAAAA,SAAS,IACfgC,EAAc1C,EAAM,SAAS,GAAK0C,EAAc,OACxD,EAKKE,EAAclC,EAAAA,SAAS,IACpBmC,EAAAA,GACLF,EAAO,MAAM,eACb3C,EAAM,KAAA,CAET,8BAIC8C,EAAAA,YAkGaC,EAAAA,WAAA,CAlGD,KAAK,SAAO,mBACtB,IAgGM,kBAhGNC,EAAAA,mBAgGM,QAAA,CAhG2B,uBAAOJ,EAAA,KAAW,EAAG,8BAAS3C,EAAA,MAAK,CAAA,GAEpE+C,EAAAA,mBAuBM,MAAA,CAvBA,MAAKC,EAAAA,eAAEN,EAAA,MAAO,iBAAiB,CAAA,GACnCK,EAAAA,mBAUS,SAAA,CATN,uBAAOE,EAAAA,MAAAL,IAAA,EAAcF,EAAA,MAAO,eAA0BrC,EAAA,QAAS,wGAM/D,uBAAOwB,EAAe,MAAA,EAAA,EACxB,SAED,CAAA,EACAkB,EAAAA,mBAUS,SAAA,CATN,uBAAOE,EAAAA,MAAAL,IAAA,EAAcF,EAAA,MAAO,eAA0BrC,EAAA,QAAS,6GAM/D,uBAAOwB,EAAe,WAAA,EAAA,EACxB,SAED,CAAA,CAAA,KAIFkB,EAAAA,mBAgBM,MAAA,CAhBA,MAAKC,EAAAA,eAAEN,EAAA,MAAO,oBAAoB,CAAA,GACtCK,EAAAA,mBAcM,MAdNG,EAcM,CAbJC,EAAAA,YAIEC,EAAAA,QAAA,CAHA,KAAK,SACL,KAAK,KACL,MAAM,gEAAA,GAERD,EAAAA,YAOEE,EAAAA,QAAA,YANS9C,EAAA,2CAAAA,EAAW,MAAA+C,GACpB,YAAY,WACX,uBAAOL,EAAAA,MAAAL,IAAA,SAAoC7C,EAAM,YAAS,WAAA,aAAA,yCASjEgD,EAAAA,mBAgDM,MAAA,CAhDA,MAAKC,EAAAA,eAAEN,EAAA,MAAO,kBAAkB,CAAA,GACpBrC,EAAA,QAAS,sBAAzBkD,EAAAA,mBA0BWC,WAAA,CAAA,IAAA,GAAA,CAzBOtC,EAAA,MAAkB,OAAM,GACtCuC,EAAAA,UAAA,EAAA,EAAAF,qBAmBMC,EAAAA,SAAA,CAAA,IAAA,GAAAE,EAAAA,WAlBoBxC,EAAA,MAAiB,CAAjCH,EAAM4C,mBADhBJ,EAAAA,mBAmBM,MAAA,CAjBH,IAAKxC,EAAK,SAAWA,EAAK,OAAS4C,EACpC,MAAM,yBAAA,GAENR,EAAAA,YAaES,EAAAA,QAAA,CAZC,KAAA7C,EACA,MAAO,EACP,YAAaf,EAAA,YACb,cAAaQ,EAAA,MACb,gBAAeE,EAAA,MACf,UAAWV,EAAA,UACX,qBAAoBoC,EACpB,cAAaC,EACb,UAAWrC,EAAA,UACZ,MAAM,SACL,YAAYkC,EACZ,eAAeH,CAAA,kGAItB0B,EAAAA,UAAA,EAAAF,EAAAA,mBAEM,MAFNM,EAEM,CAAA,GAAAC,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAA,CADJf,EAAAA,mBAAmC,IAAA,CAAhC,MAAM,SAAA,EAAU,eAAY,EAAA,CAAA,2BAInCQ,EAAAA,mBAkBWC,EAAAA,SAAA,CAAA,IAAA,GAAA,CAjBO5B,EAAA,MAAsB,OAAM,GAC1C6B,EAAAA,UAAA,EAAA,EAAAF,qBAWEC,EAAAA,SAAA,CAAA,IAAA,GAAAE,EAAAA,WAVwB9B,EAAA,MAAqB,CAArCb,EAAM4C,mBADhBd,EAAAA,YAWEe,UAAA,CATC,IAAK7C,EAAK,SAAWA,EAAK,OAAS4C,EACnC,KAAA5C,EACA,MAAO,EACP,YAAaf,EAAA,YACb,cAAaQ,EAAA,MACb,gBAAeE,EAAA,MACf,UAAWV,EAAA,UACX,YAAYkC,EACZ,eAAeH,CAAA,oFAGpB0B,EAAAA,UAAA,EAAAF,EAAAA,mBAEM,MAFNQ,EAEM,CAAA,GAAAD,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAA,CADJf,EAAAA,mBAAkC,IAAA,CAA/B,MAAM,SAAA,EAAU,cAAW,EAAA,CAAA,qBA5FrB,CAAAiB,EAAAA,MAAAjE,EAAM,SAAS,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"JSidebarAdvanced.vue2.js","sources":["../../../../src/components/organisms/JSidebarAdvanced.vue"],"sourcesContent":["<script setup lang=\"ts\">\r\nimport { ref, computed, watch } from 'vue'\r\nimport { useRoute } from 'vue-router'\r\nimport type { SidebarMenuItem, MenuPermission, MenuClickEvent } from '@/types/sidebar-menu.types'\r\nimport JDynamicMenuItem from './JSidebarSimple/JDynamicMenuItem.vue'\r\nimport JInput from '@/components/atoms/JInput.vue'\r\nimport JIcon from '@/components/atoms/JIcon.vue'\r\nimport { cn } from '@/lib/utils'\r\n\r\n/**\r\n * JSidebarAdvanced - 고급 사이드바 컴포넌트\r\n * Advanced Sidebar Component\r\n * \r\n * @description\r\n * 검색, 즐겨찾기, 다단계 메뉴를 지원하는 고급 사이드바 컴포넌트입니다.\r\n * 기본 메뉴와 즐겨찾기 탭을 제공합니다.\r\n * \r\n * @example\r\n * ```vue\r\n * <JSidebarAdvanced\r\n * :menu-items=\"menuItems\"\r\n * :permissions=\"userPermissions\"\r\n * :favorites=\"favoriteMenuKeys\"\r\n * @menu-click=\"handleMenuClick\"\r\n * @favorite-change=\"handleFavoriteChange\"\r\n * />\r\n * ```\r\n * \r\n * @example JSON 메뉴 데이터 예시\r\n * ```json\r\n * [\r\n * {\r\n * \"label\": \"대시보드\",\r\n * \"icon\": \"house\",\r\n * \"menuType\": \"L\",\r\n * \"menuKey\": 1,\r\n * \"path\": \"/dashboard\"\r\n * },\r\n * {\r\n * \"label\": \"재고 관리\",\r\n * \"icon\": \"package\",\r\n * \"menuType\": \"F\",\r\n * \"menuKey\": 2,\r\n * \"children\": [\r\n * {\r\n * \"label\": \"재고 현황\",\r\n * \"menuType\": \"L\",\r\n * \"menuKey\": 21,\r\n * \"path\": \"/inventory/status\"\r\n * },\r\n * {\r\n * \"label\": \"입고 관리\",\r\n * \"menuType\": \"F\",\r\n * \"menuKey\": 22,\r\n * \"children\": [\r\n * {\r\n * \"label\": \"입고 등록\",\r\n * \"menuType\": \"L\",\r\n * \"menuKey\": 221,\r\n * \"path\": \"/inventory/receiving/register\"\r\n * }\r\n * ]\r\n * }\r\n * ]\r\n * }\r\n * ]\r\n * ```\r\n */\r\n\r\ntype TabType = 'menu' | 'favorites'\r\n\r\ntype StyleType = 'default' | 'minimal'\r\n\r\nconst props = withDefaults(\r\n defineProps<{\r\n /** 메뉴 아이템 목록 */\r\n menuItems: SidebarMenuItem[]\r\n /** 권한 목록 */\r\n permissions?: MenuPermission[]\r\n /** 즐겨찾기 메뉴 키 목록 */\r\n favorites?: (number | string)[]\r\n /** 스타일 타입 */\r\n styletype?: StyleType\r\n /** 추가 CSS 클래스 */\r\n class?: string\r\n /** 너비 */\r\n width?: string\r\n /** 표시 여부 */\r\n isVisible?: boolean\r\n }>(),\r\n {\r\n permissions: () => [],\r\n favorites: () => [],\r\n styletype: 'minimal',\r\n width: '280px',\r\n isVisible: true,\r\n },\r\n)\r\n\r\nconst emit = defineEmits<{\r\n /** 메뉴 클릭 이벤트 */\r\n menuClick: [event: MenuClickEvent]\r\n /** 즐겨찾기 변경 이벤트 */\r\n favoriteChange: [menuKey: number | string | undefined, isFavorite: boolean]\r\n}>()\r\n\r\n// vue-router가 설정되지 않은 경우를 대비 (Storybook에서 router가 제공됨)\r\nconst route = useRoute()\r\n\r\n/**\r\n * 현재 활성 탭\r\n */\r\nconst activeTab = ref<TabType>('menu')\r\n\r\n/**\r\n * 검색어\r\n */\r\nconst searchQuery = ref('')\r\n\r\n/**\r\n * 현재 활성화된 경로\r\n */\r\nconst activePath = computed(() => route.path)\r\n\r\n/**\r\n * 확장된 메뉴 키 목록\r\n */\r\nconst expandedKeys = ref<Set<number | string>>(new Set())\r\n\r\n/**\r\n * 즐겨찾기 메뉴 아이템 목록\r\n * 즐겨찾기는 dept 없이 1단계로만 평탄화하여 표시\r\n */\r\nconst favoriteMenuItems = computed(() => {\r\n if (!Array.isArray(props.favorites) || props.favorites.length === 0) {\r\n return []\r\n }\r\n\r\n if (!Array.isArray(props.menuItems) || props.menuItems.length === 0) {\r\n return []\r\n }\r\n\r\n /**\r\n * 메뉴 아이템을 재귀적으로 순회하며 즐겨찾기만 추출\r\n * 즐겨찾기에서는 dept 없이 1단계로만 평탄화\r\n * menuType L(Link)만 포함하고 F(Folder)는 제외\r\n */\r\n const flattenFavorites = (items: SidebarMenuItem[]): SidebarMenuItem[] => {\r\n const result: SidebarMenuItem[] = []\r\n\r\n if (!Array.isArray(items)) {\r\n return result\r\n }\r\n\r\n for (const item of items) {\r\n const key = item.menuKey || item.label\r\n const isFavorite = Array.isArray(props.favorites) && props.favorites.includes(key)\r\n\r\n // 즐겨찾기이고 menuType이 L(Link)인 경우만 추가 (F는 제외)\r\n if (isFavorite && item.menuType === 'L') {\r\n result.push({\r\n ...item,\r\n children: undefined, // children 제거하여 1단계로만 표시\r\n })\r\n }\r\n\r\n // 하위 메뉴도 재귀적으로 탐색\r\n if (item.children && Array.isArray(item.children) && item.children.length > 0) {\r\n const childFavorites = flattenFavorites(item.children)\r\n result.push(...childFavorites)\r\n }\r\n }\r\n\r\n return result\r\n }\r\n\r\n return flattenFavorites(props.menuItems)\r\n})\r\n\r\n/**\r\n * 검색어로 필터링된 메뉴 아이템\r\n * 재귀적으로 children까지 검색\r\n */\r\nconst filteredMenuItems = computed(() => {\r\n if (!Array.isArray(props.menuItems) || props.menuItems.length === 0) {\r\n return []\r\n }\r\n\r\n if (!searchQuery.value || searchQuery.value.trim() === '') {\r\n return props.menuItems\r\n }\r\n\r\n const query = searchQuery.value.toLowerCase().trim()\r\n\r\n /**\r\n * 메뉴 아이템을 재귀적으로 검색\r\n */\r\n const searchInMenu = (items: SidebarMenuItem[]): SidebarMenuItem[] => {\r\n const result: SidebarMenuItem[] = []\r\n\r\n if (!Array.isArray(items)) {\r\n return result\r\n }\r\n\r\n for (const item of items) {\r\n const matchesLabel = item.label?.toLowerCase().includes(query) ?? false\r\n\r\n // 하위 메뉴 검색\r\n let filteredChildren: SidebarMenuItem[] | undefined = undefined\r\n if (item.children && Array.isArray(item.children) && item.children.length > 0) {\r\n filteredChildren = searchInMenu(item.children)\r\n }\r\n\r\n // 현재 메뉴나 하위 메뉴 중 하나라도 매칭되면 포함\r\n if (matchesLabel || (Array.isArray(filteredChildren) && filteredChildren.length > 0)) {\r\n result.push({\r\n ...item,\r\n children: filteredChildren,\r\n })\r\n }\r\n }\r\n\r\n return result\r\n }\r\n\r\n return searchInMenu(props.menuItems)\r\n})\r\n\r\n/**\r\n * 검색 결과에 따라 부모 메뉴 자동 확장\r\n * computed 외부에서 watch를 통해 처리\r\n */\r\nwatch(\r\n () => filteredMenuItems.value,\r\n (filtered) => {\r\n if (!searchQuery.value || searchQuery.value.trim() === '') {\r\n return\r\n }\r\n\r\n // 검색 결과에서 매칭된 하위 메뉴가 있는 부모를 찾아 확장\r\n const findParentsWithMatches = (items: SidebarMenuItem[]): Set<number | string> => {\r\n const keysToExpand = new Set<number | string>()\r\n\r\n const traverse = (menuItems: SidebarMenuItem[]): void => {\r\n if (!Array.isArray(menuItems)) {\r\n return\r\n }\r\n\r\n for (const item of menuItems) {\r\n if (item.children && Array.isArray(item.children) && item.children.length > 0) {\r\n const key = item.menuKey || item.label\r\n keysToExpand.add(key)\r\n traverse(item.children)\r\n }\r\n }\r\n }\r\n\r\n traverse(items)\r\n return keysToExpand\r\n }\r\n\r\n const keysToExpand = findParentsWithMatches(filtered)\r\n keysToExpand.forEach(key => {\r\n expandedKeys.value.add(key)\r\n })\r\n },\r\n { immediate: false }\r\n)\r\n\r\n/**\r\n * 검색어로 필터링된 즐겨찾기 메뉴 아이템\r\n * 즐겨찾기는 이미 평탄화되어 있으므로 단순 필터링만 수행\r\n */\r\nconst filteredFavoriteItems = computed(() => {\r\n if (!Array.isArray(favoriteMenuItems.value) || favoriteMenuItems.value.length === 0) {\r\n return []\r\n }\r\n\r\n if (!searchQuery.value || searchQuery.value.trim() === '') {\r\n return favoriteMenuItems.value\r\n }\r\n\r\n const query = searchQuery.value.toLowerCase().trim()\r\n\r\n // 즐겨찾기는 이미 평탄화되어 1단계이므로 단순 필터링만 수행\r\n return favoriteMenuItems.value.filter((item) =>\r\n item.label?.toLowerCase().includes(query) ?? false\r\n )\r\n})\r\n\r\n\r\n/**\r\n * 탭 변경 핸들러\r\n * 탭 전환 시 검색 쿼리를 초기화하여 각 탭의 독립적인 검색 상태 유지\r\n */\r\nconst handleTabChange = (tab: TabType) => {\r\n if (activeTab.value !== tab) {\r\n activeTab.value = tab\r\n // 탭 전환 시 검색 쿼리 초기화 (선택적 - UX 고려)\r\n // 검색 쿼리를 유지하려면 아래 라인을 제거하세요\r\n searchQuery.value = ''\r\n }\r\n}\r\n\r\n/**\r\n * 확장 상태 변경 핸들러\r\n */\r\nconst handleExpandChange = (menuKey: number | string | undefined, expanded: boolean) => {\r\n if (!menuKey) return\r\n\r\n if (expanded) {\r\n expandedKeys.value.add(menuKey)\r\n } else {\r\n expandedKeys.value.delete(menuKey)\r\n }\r\n}\r\n\r\n/**\r\n * 메뉴 클릭 핸들러\r\n */\r\nconst handleMenuClick = (event: MenuClickEvent) => {\r\n emit('menuClick', event)\r\n}\r\n\r\n/**\r\n * 즐겨찾기 토글 핸들러\r\n */\r\nconst handleFavoriteToggle = (menuKey: number | string | undefined) => {\r\n if (!menuKey) return\r\n\r\n const isFavorite = props.favorites?.includes(menuKey) ?? false\r\n emit('favoriteChange', menuKey, !isFavorite)\r\n}\r\n\r\n/**\r\n * 메뉴 아이템에서 특정 menuKey를 찾는 헬퍼 함수\r\n */\r\nconst findMenuItemByKey = (items: SidebarMenuItem[], targetKey: number | string): SidebarMenuItem | null => {\r\n for (const item of items) {\r\n const key = item.menuKey || item.label\r\n if (key === targetKey) {\r\n return item\r\n }\r\n if (item.children && item.children.length > 0) {\r\n const found = findMenuItemByKey(item.children, targetKey)\r\n if (found) return found\r\n }\r\n }\r\n return null\r\n}\r\n\r\n/**\r\n * 메뉴가 즐겨찾기인지 확인 (L 타입만 즐겨찾기 가능)\r\n */\r\nconst isFavorite = (menuKey: number | string | undefined): boolean => {\r\n if (!menuKey) return false\r\n if (!props.favorites?.includes(menuKey)) return false\r\n \r\n // menuType이 L인 경우만 즐겨찾기로 인정\r\n const menuItem = findMenuItemByKey(props.menuItems, menuKey)\r\n return menuItem?.menuType === 'L'\r\n}\r\n\r\n/**\n * 스타일 프리셋\n */\nconst STYLE_PRESETS: Record<StyleType, {\n containerClass: string\n tabContainerClass: string\n tabButtonClass: string\n searchContainerClass: string\n menuContainerClass: string\n}> = {\n default: {\n containerClass: 'h-full bg-background border-r border-border flex flex-col',\n tabContainerClass: 'flex border-b border-border',\n tabButtonClass: 'flex-1 px-3 py-1.5 text-xs font-medium transition-colors border-b-2 hover:bg-accent/50',\n searchContainerClass: 'p-1.5 border-b border-border',\n menuContainerClass: 'flex-1 overflow-y-auto p-1.5 space-y-0.5',\n },\n minimal: {\n containerClass: 'h-full bg-background border-r border-border flex flex-col',\n tabContainerClass: 'flex border-b border-border',\n tabButtonClass: 'flex-1 px-2 py-1 text-xs font-medium transition-colors border-b-2',\n searchContainerClass: 'p-1 border-b border-border',\n menuContainerClass: 'flex-1 overflow-y-auto p-1 space-y-0.5',\n },\n}\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 rootClasses = computed(() => {\r\n return cn(\r\n preset.value.containerClass,\r\n props.class\r\n )\r\n})\r\n</script>\r\n\r\n<template>\r\n <Transition name=\"slide\">\r\n <aside v-show=\"props.isVisible\" :class=\"rootClasses\" :style=\"{ width }\">\r\n <!-- 탭 헤더 -->\r\n <div :class=\"preset.tabContainerClass\">\r\n <button\r\n :class=\"cn(\r\n preset.tabButtonClass,\r\n activeTab === 'menu'\r\n ? 'border-primary text-primary'\r\n : 'border-transparent text-muted-foreground hover:text-foreground'\r\n )\"\r\n @click=\"handleTabChange('menu')\"\r\n >\r\n 기본메뉴\r\n </button>\r\n <button\r\n :class=\"cn(\r\n preset.tabButtonClass,\r\n activeTab === 'favorites'\r\n ? 'border-primary text-primary'\r\n : 'border-transparent text-muted-foreground hover:text-foreground'\r\n )\"\r\n @click=\"handleTabChange('favorites')\"\r\n >\r\n 즐겨찾기\r\n </button>\r\n </div>\r\n\r\n <!-- 검색 영역 -->\r\n <div :class=\"preset.searchContainerClass\">\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=\"cn(\r\n 'pl-8',\r\n props.styletype === 'minimal' && 'h-8 text-xs'\r\n )\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <!-- 메뉴 목록 -->\r\n <div :class=\"preset.menuContainerClass\">\r\n <template v-if=\"activeTab === 'menu'\">\r\n <template v-if=\"filteredMenuItems.length > 0\">\r\n <div\r\n v-for=\"(item, index) in filteredMenuItems\"\r\n :key=\"item.menuKey || item.label || index\"\r\n class=\"flex items-center group\"\r\n >\r\n <JDynamicMenuItem\r\n :item=\"item\"\r\n :level=\"0\"\r\n :permissions=\"permissions\"\r\n :active-path=\"activePath\"\r\n :expanded-keys=\"expandedKeys\"\r\n :favorites=\"favorites\"\r\n :on-favorite-toggle=\"handleFavoriteToggle\"\r\n :is-favorite=\"isFavorite\"\r\n :styletype=\"styletype\"\r\n class=\"flex-1\"\r\n @menu-click=\"handleMenuClick\"\r\n @expand-change=\"handleExpandChange\"\r\n />\r\n </div>\r\n </template>\n <div v-else class=\"text-center py-8 text-muted-foreground\">\n <p class=\"text-xs\">검색 결과가 없습니다.</p>\n </div>\n </template>\n\n <template v-else>\n <template v-if=\"filteredFavoriteItems.length > 0\">\n <JDynamicMenuItem\n v-for=\"(item, index) in filteredFavoriteItems\"\n :key=\"item.menuKey || item.label || index\"\n :item=\"item\"\n :level=\"0\"\n :permissions=\"permissions\"\n :active-path=\"activePath\"\n :expanded-keys=\"expandedKeys\"\n :styletype=\"styletype\"\n @menu-click=\"handleMenuClick\"\n @expand-change=\"handleExpandChange\"\n />\n </template>\n <div v-else class=\"text-center py-8 text-muted-foreground\">\n <p class=\"text-xs\">즐겨찾기가 없습니다.</p>\n </div>\n </template>\r\n </div>\r\n </aside>\r\n </Transition>\r\n</template>\r\n\r\n<style scoped>\r\n.slide-enter-active,\r\n.slide-leave-active {\r\n transition: transform 0.3s ease, opacity 0.3s ease;\r\n}\r\n\r\n.slide-enter-from,\r\n.slide-leave-to {\r\n transform: translateX(-100%);\r\n opacity: 0;\r\n}\r\n</style>\r\n"],"names":["props","__props","emit","__emit","route","useRoute","activeTab","ref","searchQuery","activePath","computed","expandedKeys","favoriteMenuItems","flattenFavorites","items","result","item","key","childFavorites","filteredMenuItems","query","searchInMenu","matchesLabel","filteredChildren","watch","filtered","keysToExpand","traverse","menuItems","filteredFavoriteItems","handleTabChange","tab","handleExpandChange","menuKey","expanded","handleMenuClick","event","handleFavoriteToggle","isFavorite","findMenuItemByKey","targetKey","found","STYLE_PRESETS","preset","rootClasses","cn","_createBlock","_Transition","_createElementVNode","_normalizeClass","_unref","_hoisted_1","_createVNode","JIcon","JInput","$event","_createElementBlock","_Fragment","_openBlock","_renderList","index","JDynamicMenuItem","_hoisted_2","_cache","_hoisted_3","_vShow"],"mappings":"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"}
1
+ {"version":3,"file":"JSidebarAdvanced.vue2.js","sources":["../../../../src/components/organisms/JSidebarAdvanced.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, watch } from 'vue'\nimport { useRoute } from 'vue-router'\nimport type { SidebarMenuItem, MenuPermission, MenuClickEvent } from '@/types/sidebar-menu.types'\nimport JDynamicMenuItem from './JSidebarSimple/JDynamicMenuItem.vue'\nimport JInput from '@/components/atoms/JInput.vue'\nimport JIcon from '@/components/atoms/JIcon.vue'\nimport { cn } from '@/lib/utils'\n\n/**\n * JSidebarAdvanced - 고급 사이드바 컴포넌트\n * Advanced Sidebar Component\n * \n * @description\n * 검색, 즐겨찾기, 다단계 메뉴를 지원하는 고급 사이드바 컴포넌트입니다.\n * 기본 메뉴와 즐겨찾기 탭을 제공합니다.\n * \n * @example\n * ```vue\n * <JSidebarAdvanced\n * :menu-items=\"menuItems\"\n * :permissions=\"userPermissions\"\n * :favorites=\"favoriteMenuKeys\"\n * @menu-click=\"handleMenuClick\"\n * @favorite-change=\"handleFavoriteChange\"\n * />\n * ```\n * \n * @example JSON 메뉴 데이터 예시\n * ```json\n * [\n * {\n * \"label\": \"대시보드\",\n * \"icon\": \"house\",\n * \"menuType\": \"L\",\n * \"menuKey\": 1,\n * \"path\": \"/dashboard\"\n * },\n * {\n * \"label\": \"재고 관리\",\n * \"icon\": \"package\",\n * \"menuType\": \"F\",\n * \"menuKey\": 2,\n * \"children\": [\n * {\n * \"label\": \"재고 현황\",\n * \"menuType\": \"L\",\n * \"menuKey\": 21,\n * \"path\": \"/inventory/status\"\n * },\n * {\n * \"label\": \"입고 관리\",\n * \"menuType\": \"F\",\n * \"menuKey\": 22,\n * \"children\": [\n * {\n * \"label\": \"입고 등록\",\n * \"menuType\": \"L\",\n * \"menuKey\": 221,\n * \"path\": \"/inventory/receiving/register\"\n * }\n * ]\n * }\n * ]\n * }\n * ]\n * ```\n */\n\ntype TabType = 'menu' | 'favorites'\n\ntype StyleType = 'default' | 'minimal'\n\nconst props = withDefaults(\n defineProps<{\n /** 메뉴 아이템 목록 */\n menuItems: SidebarMenuItem[]\n /** 권한 목록 */\n permissions?: MenuPermission[]\n /** 즐겨찾기 메뉴 키 목록 */\n favorites?: (number | string)[]\n /** 스타일 타입 */\n styletype?: StyleType\n /** 추가 CSS 클래스 */\n class?: string\n /** 너비 */\n width?: string\n /** 표시 여부 */\n isVisible?: boolean\n }>(),\n {\n permissions: () => [],\n favorites: () => [],\n styletype: 'minimal',\n width: '280px',\n isVisible: true,\n },\n)\n\nconst emit = defineEmits<{\n /** 메뉴 클릭 이벤트 */\n menuClick: [event: MenuClickEvent]\n /** 즐겨찾기 변경 이벤트 */\n favoriteChange: [menuKey: number | string | undefined, isFavorite: boolean]\n}>()\n\n// vue-router가 설정되지 않은 경우를 대비 (Storybook에서 router가 제공됨)\nconst route = useRoute()\n\n/**\n * 현재 활성 탭\n */\nconst activeTab = ref<TabType>('menu')\n\n/**\n * 검색어\n */\nconst searchQuery = ref('')\n\n/**\n * 현재 활성화된 경로\n */\nconst activePath = computed(() => route.path)\n\n/**\n * 확장된 메뉴 키 목록\n */\nconst expandedKeys = ref<Set<number | string>>(new Set())\n\n/**\n * 즐겨찾기 메뉴 아이템 목록\n * 즐겨찾기는 dept 없이 1단계로만 평탄화하여 표시\n */\nconst favoriteMenuItems = computed(() => {\n if (!Array.isArray(props.favorites) || props.favorites.length === 0) {\n return []\n }\n\n if (!Array.isArray(props.menuItems) || props.menuItems.length === 0) {\n return []\n }\n\n /**\n * 메뉴 아이템을 재귀적으로 순회하며 즐겨찾기만 추출\n * 즐겨찾기에서는 dept 없이 1단계로만 평탄화\n * menuType L(Link)만 포함하고 F(Folder)는 제외\n */\n const flattenFavorites = (items: SidebarMenuItem[]): SidebarMenuItem[] => {\n const result: SidebarMenuItem[] = []\n\n if (!Array.isArray(items)) {\n return result\n }\n\n for (const item of items) {\n const key = item.menuKey || item.label\n const isFavorite = Array.isArray(props.favorites) && props.favorites.includes(key)\n\n // 즐겨찾기이고 menuType이 L(Link)인 경우만 추가 (F는 제외)\n if (isFavorite && item.menuType === 'L') {\n result.push({\n ...item,\n children: undefined, // children 제거하여 1단계로만 표시\n })\n }\n\n // 하위 메뉴도 재귀적으로 탐색\n if (item.children && Array.isArray(item.children) && item.children.length > 0) {\n const childFavorites = flattenFavorites(item.children)\n result.push(...childFavorites)\n }\n }\n\n return result\n }\n\n return flattenFavorites(props.menuItems)\n})\n\n/**\n * 검색어로 필터링된 메뉴 아이템\n * 재귀적으로 children까지 검색\n */\nconst filteredMenuItems = computed(() => {\n if (!Array.isArray(props.menuItems) || props.menuItems.length === 0) {\n return []\n }\n\n if (!searchQuery.value || searchQuery.value.trim() === '') {\n return props.menuItems\n }\n\n const query = searchQuery.value.toLowerCase().trim()\n\n /**\n * 메뉴 아이템을 재귀적으로 검색\n */\n const searchInMenu = (items: SidebarMenuItem[]): SidebarMenuItem[] => {\n const result: SidebarMenuItem[] = []\n\n if (!Array.isArray(items)) {\n return result\n }\n\n for (const item of items) {\n const matchesLabel = item.label?.toLowerCase().includes(query) ?? false\n\n // 하위 메뉴 검색\n let filteredChildren: SidebarMenuItem[] | undefined = undefined\n if (item.children && Array.isArray(item.children) && item.children.length > 0) {\n filteredChildren = searchInMenu(item.children)\n }\n\n // 현재 메뉴나 하위 메뉴 중 하나라도 매칭되면 포함\n if (matchesLabel || (Array.isArray(filteredChildren) && filteredChildren.length > 0)) {\n result.push({\n ...item,\n children: filteredChildren,\n })\n }\n }\n\n return result\n }\n\n return searchInMenu(props.menuItems)\n})\n\n/**\n * 검색 결과에 따라 부모 메뉴 자동 확장\n * computed 외부에서 watch를 통해 처리\n */\nwatch(\n () => filteredMenuItems.value,\n (filtered) => {\n if (!searchQuery.value || searchQuery.value.trim() === '') {\n return\n }\n\n // 검색 결과에서 매칭된 하위 메뉴가 있는 부모를 찾아 확장\n const findParentsWithMatches = (items: SidebarMenuItem[]): Set<number | string> => {\n const keysToExpand = new Set<number | string>()\n\n const traverse = (menuItems: SidebarMenuItem[]): void => {\n if (!Array.isArray(menuItems)) {\n return\n }\n\n for (const item of menuItems) {\n if (item.children && Array.isArray(item.children) && item.children.length > 0) {\n const key = item.menuKey || item.label\n keysToExpand.add(key)\n traverse(item.children)\n }\n }\n }\n\n traverse(items)\n return keysToExpand\n }\n\n const keysToExpand = findParentsWithMatches(filtered)\n keysToExpand.forEach(key => {\n expandedKeys.value.add(key)\n })\n },\n { immediate: false }\n)\n\n/**\n * 검색어로 필터링된 즐겨찾기 메뉴 아이템\n * 즐겨찾기는 이미 평탄화되어 있으므로 단순 필터링만 수행\n */\nconst filteredFavoriteItems = computed(() => {\n if (!Array.isArray(favoriteMenuItems.value) || favoriteMenuItems.value.length === 0) {\n return []\n }\n\n if (!searchQuery.value || searchQuery.value.trim() === '') {\n return favoriteMenuItems.value\n }\n\n const query = searchQuery.value.toLowerCase().trim()\n\n // 즐겨찾기는 이미 평탄화되어 1단계이므로 단순 필터링만 수행\n return favoriteMenuItems.value.filter((item) =>\n item.label?.toLowerCase().includes(query) ?? false\n )\n})\n\n\n/**\n * 탭 변경 핸들러\n * 탭 전환 시 검색 쿼리를 초기화하여 각 탭의 독립적인 검색 상태 유지\n */\nconst handleTabChange = (tab: TabType) => {\n if (activeTab.value !== tab) {\n activeTab.value = tab\n // 탭 전환 시 검색 쿼리 초기화 (선택적 - UX 고려)\n // 검색 쿼리를 유지하려면 아래 라인을 제거하세요\n searchQuery.value = ''\n }\n}\n\n/**\n * 확장 상태 변경 핸들러\n */\nconst handleExpandChange = (menuKey: number | string | undefined, expanded: boolean) => {\n if (!menuKey) return\n\n if (expanded) {\n expandedKeys.value.add(menuKey)\n } else {\n expandedKeys.value.delete(menuKey)\n }\n}\n\n/**\n * 메뉴 클릭 핸들러\n */\nconst handleMenuClick = (event: MenuClickEvent) => {\n emit('menuClick', event)\n}\n\n/**\n * 즐겨찾기 토글 핸들러\n */\nconst handleFavoriteToggle = (menuKey: number | string | undefined) => {\n if (!menuKey) return\n\n const isFavorite = props.favorites?.includes(menuKey) ?? false\n emit('favoriteChange', menuKey, !isFavorite)\n}\n\n/**\n * 메뉴 아이템에서 특정 menuKey를 찾는 헬퍼 함수\n */\nconst findMenuItemByKey = (items: SidebarMenuItem[], targetKey: number | string): SidebarMenuItem | null => {\n for (const item of items) {\n const key = item.menuKey || item.label\n if (key === targetKey) {\n return item\n }\n if (item.children && item.children.length > 0) {\n const found = findMenuItemByKey(item.children, targetKey)\n if (found) return found\n }\n }\n return null\n}\n\n/**\n * 메뉴가 즐겨찾기인지 확인 (L 타입만 즐겨찾기 가능)\n */\nconst isFavorite = (menuKey: number | string | undefined): boolean => {\n if (!menuKey) return false\n if (!props.favorites?.includes(menuKey)) return false\n \n // menuType이 L인 경우만 즐겨찾기로 인정\n const menuItem = findMenuItemByKey(props.menuItems, menuKey)\n return menuItem?.menuType === 'L'\n}\n\n/**\n * 스타일 프리셋\n */\nconst STYLE_PRESETS: Record<StyleType, {\n containerClass: string\n tabContainerClass: string\n tabButtonClass: string\n searchContainerClass: string\n menuContainerClass: string\n}> = {\n default: {\n containerClass: 'h-full bg-background border-r border-border flex flex-col',\n tabContainerClass: 'flex border-b border-border',\n tabButtonClass: 'flex-1 px-3 py-1.5 text-xs font-medium transition-colors border-b-2 hover:bg-accent/50',\n searchContainerClass: 'p-1.5 border-b border-border',\n menuContainerClass: 'flex-1 overflow-y-auto p-1.5 space-y-0.5',\n },\n minimal: {\n containerClass: 'h-full bg-background border-r border-border flex flex-col',\n tabContainerClass: 'flex border-b border-border',\n tabButtonClass: 'flex-1 px-2 py-1 text-xs font-medium transition-colors border-b-2',\n searchContainerClass: 'p-1 border-b border-border',\n menuContainerClass: 'flex-1 overflow-y-auto p-1 space-y-0.5',\n },\n}\n\nconst preset = computed(() => {\n return STYLE_PRESETS[props.styletype] ?? STYLE_PRESETS.default\n})\n\n/**\n * 루트 클래스\n */\nconst rootClasses = computed(() => {\n return cn(\n preset.value.containerClass,\n props.class\n )\n})\n</script>\n\n<template>\n <Transition name=\"slide\">\n <aside v-show=\"props.isVisible\" :class=\"rootClasses\" :style=\"{ width }\">\n <!-- 탭 헤더 -->\n <div :class=\"preset.tabContainerClass\">\n <button\n :class=\"cn(\n preset.tabButtonClass,\n activeTab === 'menu'\n ? 'border-primary text-primary'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n )\"\n @click=\"handleTabChange('menu')\"\n >\n 기본메뉴\n </button>\n <button\n :class=\"cn(\n preset.tabButtonClass,\n activeTab === 'favorites'\n ? 'border-primary text-primary'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n )\"\n @click=\"handleTabChange('favorites')\"\n >\n 즐겨찾기\n </button>\n </div>\n\n <!-- 검색 영역 -->\n <div :class=\"preset.searchContainerClass\">\n <div class=\"relative\">\n <JIcon\n name=\"search\"\n size=\"sm\"\n class=\"absolute left-2 top-1/2 -translate-y-1/2 text-muted-foreground\"\n />\n <JInput\n v-model=\"searchQuery\"\n placeholder=\"메뉴 검색...\"\n :class=\"cn(\n 'pl-8',\n props.styletype === 'minimal' && 'h-8 text-xs'\n )\"\n />\n </div>\n </div>\n\n <!-- 메뉴 목록 -->\n <div :class=\"preset.menuContainerClass\">\n <template v-if=\"activeTab === 'menu'\">\n <template v-if=\"filteredMenuItems.length > 0\">\n <div\n v-for=\"(item, index) in filteredMenuItems\"\n :key=\"item.menuKey || item.label || index\"\n class=\"flex items-center group\"\n >\n <JDynamicMenuItem\n :item=\"item\"\n :level=\"0\"\n :permissions=\"permissions\"\n :active-path=\"activePath\"\n :expanded-keys=\"expandedKeys\"\n :favorites=\"favorites\"\n :on-favorite-toggle=\"handleFavoriteToggle\"\n :is-favorite=\"isFavorite\"\n :styletype=\"styletype\"\n class=\"flex-1\"\n @menu-click=\"handleMenuClick\"\n @expand-change=\"handleExpandChange\"\n />\n </div>\n </template>\n <div v-else class=\"text-center py-8 text-muted-foreground\">\n <p class=\"text-xs\">검색 결과가 없습니다.</p>\n </div>\n </template>\n\n <template v-else>\n <template v-if=\"filteredFavoriteItems.length > 0\">\n <JDynamicMenuItem\n v-for=\"(item, index) in filteredFavoriteItems\"\n :key=\"item.menuKey || item.label || index\"\n :item=\"item\"\n :level=\"0\"\n :permissions=\"permissions\"\n :active-path=\"activePath\"\n :expanded-keys=\"expandedKeys\"\n :styletype=\"styletype\"\n @menu-click=\"handleMenuClick\"\n @expand-change=\"handleExpandChange\"\n />\n </template>\n <div v-else class=\"text-center py-8 text-muted-foreground\">\n <p class=\"text-xs\">즐겨찾기가 없습니다.</p>\n </div>\n </template>\n </div>\n </aside>\n </Transition>\n</template>\n\n<style scoped>\n.slide-enter-active,\n.slide-leave-active {\n transition: transform 0.3s ease, opacity 0.3s ease;\n}\n\n.slide-enter-from,\n.slide-leave-to {\n transform: translateX(-100%);\n opacity: 0;\n}\n</style>\n"],"names":["props","__props","emit","__emit","route","useRoute","activeTab","ref","searchQuery","activePath","computed","expandedKeys","favoriteMenuItems","flattenFavorites","items","result","item","key","childFavorites","filteredMenuItems","query","searchInMenu","matchesLabel","filteredChildren","watch","filtered","keysToExpand","traverse","menuItems","filteredFavoriteItems","handleTabChange","tab","handleExpandChange","menuKey","expanded","handleMenuClick","event","handleFavoriteToggle","isFavorite","findMenuItemByKey","targetKey","found","STYLE_PRESETS","preset","rootClasses","cn","_createBlock","_Transition","_createElementVNode","_normalizeClass","_unref","_hoisted_1","_createVNode","JIcon","JInput","$event","_createElementBlock","_Fragment","_openBlock","_renderList","index","JDynamicMenuItem","_hoisted_2","_cache","_hoisted_3","_vShow"],"mappings":"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"}
@@ -3,5 +3,5 @@
3
3
  for (const [t_key, t_val] of t_opts)
4
4
  t_merged[t_key] = t_val;
5
5
  return t_merged;
6
- };,u=t(e.default,[["__scopeId","data-v-cf0579e4"]]);exports.default=u;
6
+ };,u=t(e.default,[["__scopeId","data-v-88c10bb4"]]);exports.default=u;
7
7
  //# sourceMappingURL=JSidebarSimple.vue.cjs.map
@@ -6,8 +6,8 @@ const r = (r_comp, r_opts) => {
6
6
  r_merged[r_key] = r_val;
7
7
  return r_merged;
8
8
  };
9
- const p = /* @__PURE__ */ r(o, [["__scopeId", "data-v-cf0579e4"]]);
9
+ const a = /* @__PURE__ */ r(o, [["__scopeId", "data-v-88c10bb4"]]);
10
10
  export {
11
- p as default
11
+ a as default
12
12
  };
13
13
  //# sourceMappingURL=JSidebarSimple.vue.js.map