@j-solution/components 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/README.md +8 -8
  2. package/assets/jwms-portal-frontend-BtHTA-UF.css +1 -0
  3. package/assets/styles/global-utilities.css +34 -0
  4. package/assets/styles/j-components.css +1 -1
  5. package/assets/styles/themes.css +21 -21
  6. package/components/atoms/JButton.vue.cjs +6 -1
  7. package/components/atoms/JButton.vue.cjs.map +1 -1
  8. package/components/atoms/JButton.vue.js +10 -85
  9. package/components/atoms/JButton.vue.js.map +1 -1
  10. package/components/atoms/JButton.vue2.cjs +1 -1
  11. package/components/atoms/JButton.vue2.cjs.map +1 -1
  12. package/components/atoms/JButton.vue2.js +85 -2
  13. package/components/atoms/JButton.vue2.js.map +1 -1
  14. package/components/atoms/JGrid.vue.cjs +1 -1
  15. package/components/atoms/JGrid.vue.js +1 -1
  16. package/components/atoms/JGrid.vue2.cjs +1 -1
  17. package/components/atoms/JGrid.vue2.cjs.map +1 -1
  18. package/components/atoms/JGrid.vue2.js +39 -35
  19. package/components/atoms/JGrid.vue2.js.map +1 -1
  20. package/components/atoms/JLabel.vue.cjs +1 -1
  21. package/components/atoms/JLabel.vue.cjs.map +1 -1
  22. package/components/atoms/JLabel.vue.js +26 -22
  23. package/components/atoms/JLabel.vue.js.map +1 -1
  24. package/components/atoms/JSectionTitle.vue.cjs +7 -0
  25. package/components/atoms/JSectionTitle.vue.cjs.map +1 -0
  26. package/components/atoms/JSectionTitle.vue.js +13 -0
  27. package/components/atoms/JSectionTitle.vue.js.map +1 -0
  28. package/components/atoms/JSectionTitle.vue2.cjs +2 -0
  29. package/components/atoms/JSectionTitle.vue2.cjs.map +1 -0
  30. package/components/atoms/JSectionTitle.vue2.js +67 -0
  31. package/components/atoms/JSectionTitle.vue2.js.map +1 -0
  32. package/components/atoms/JSplitter.vue.cjs +6 -1
  33. package/components/atoms/JSplitter.vue.cjs.map +1 -1
  34. package/components/atoms/JSplitter.vue.js +10 -59
  35. package/components/atoms/JSplitter.vue.js.map +1 -1
  36. package/components/atoms/JSplitter.vue2.cjs +1 -1
  37. package/components/atoms/JSplitter.vue2.cjs.map +1 -1
  38. package/components/atoms/JSplitter.vue2.js +59 -2
  39. package/components/atoms/JSplitter.vue2.js.map +1 -1
  40. package/components/examples/ExampleCrudPage.vue.cjs +1 -1
  41. package/components/examples/ExampleCrudPage.vue.cjs.map +1 -1
  42. package/components/examples/ExampleCrudPage.vue.js +228 -208
  43. package/components/examples/ExampleCrudPage.vue.js.map +1 -1
  44. package/components/examples/ExampleTabMappingPage.vue.cjs +1 -1
  45. package/components/examples/ExampleTabMappingPage.vue.cjs.map +1 -1
  46. package/components/examples/ExampleTabMappingPage.vue.js +341 -368
  47. package/components/examples/ExampleTabMappingPage.vue.js.map +1 -1
  48. package/components/molecules/JAlert.vue.cjs +1 -1
  49. package/components/molecules/JAlert.vue.cjs.map +1 -1
  50. package/components/molecules/JAlert.vue.js +18 -16
  51. package/components/molecules/JAlert.vue.js.map +1 -1
  52. package/components/molecules/JCard.vue.cjs +1 -1
  53. package/components/molecules/JCard.vue.cjs.map +1 -1
  54. package/components/molecules/JCard.vue.js +55 -39
  55. package/components/molecules/JCard.vue.js.map +1 -1
  56. package/components/molecules/JEmptyState.vue.cjs +7 -0
  57. package/components/molecules/JEmptyState.vue.cjs.map +1 -0
  58. package/components/molecules/JEmptyState.vue.js +13 -0
  59. package/components/molecules/JEmptyState.vue.js.map +1 -0
  60. package/components/molecules/JEmptyState.vue2.cjs +2 -0
  61. package/components/molecules/JEmptyState.vue2.cjs.map +1 -0
  62. package/components/molecules/JEmptyState.vue2.js +127 -0
  63. package/components/molecules/JEmptyState.vue2.js.map +1 -0
  64. package/components/molecules/JFormField.vue.cjs +6 -1
  65. package/components/molecules/JFormField.vue.cjs.map +1 -1
  66. package/components/molecules/JFormField.vue.js +10 -264
  67. package/components/molecules/JFormField.vue.js.map +1 -1
  68. package/components/molecules/JFormField.vue2.cjs +2 -0
  69. package/components/molecules/JFormField.vue2.cjs.map +1 -0
  70. package/components/molecules/JFormField.vue2.js +271 -0
  71. package/components/molecules/JFormField.vue2.js.map +1 -0
  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 +1 -1
  75. package/components/molecules/JTabs.vue2.cjs.map +1 -1
  76. package/components/molecules/JTabs.vue2.js +44 -50
  77. package/components/molecules/JTabs.vue2.js.map +1 -1
  78. package/components/molecules/JTitlebar.vue.cjs +1 -1
  79. package/components/molecules/JTitlebar.vue.cjs.map +1 -1
  80. package/components/molecules/JTitlebar.vue.js +23 -20
  81. package/components/molecules/JTitlebar.vue.js.map +1 -1
  82. package/components/organisms/JDynamicForm.vue2.cjs +1 -1
  83. package/components/organisms/JDynamicForm.vue2.cjs.map +1 -1
  84. package/components/organisms/JDynamicForm.vue2.js +35 -32
  85. package/components/organisms/JDynamicForm.vue2.js.map +1 -1
  86. package/components/organisms/JDynamicTabs.vue.cjs +1 -1
  87. package/components/organisms/JDynamicTabs.vue.cjs.map +1 -1
  88. package/components/organisms/JDynamicTabs.vue.js +47 -52
  89. package/components/organisms/JDynamicTabs.vue.js.map +1 -1
  90. package/components/organisms/JFilterBar.vue.cjs +6 -1
  91. package/components/organisms/JFilterBar.vue.cjs.map +1 -1
  92. package/components/organisms/JFilterBar.vue.js +10 -137
  93. package/components/organisms/JFilterBar.vue.js.map +1 -1
  94. package/components/organisms/JFilterBar.vue2.cjs +1 -1
  95. package/components/organisms/JFilterBar.vue2.cjs.map +1 -1
  96. package/components/organisms/JFilterBar.vue2.js +141 -2
  97. package/components/organisms/JFilterBar.vue2.js.map +1 -1
  98. package/components/organisms/JFormModal.vue.cjs +1 -1
  99. package/components/organisms/JFormModal.vue.cjs.map +1 -1
  100. package/components/organisms/JFormModal.vue.js +54 -49
  101. package/components/organisms/JFormModal.vue.js.map +1 -1
  102. package/components/organisms/JHeader.vue.cjs +1 -1
  103. package/components/organisms/JHeader.vue.cjs.map +1 -1
  104. package/components/organisms/JHeader.vue.js +191 -190
  105. package/components/organisms/JHeader.vue.js.map +1 -1
  106. package/components/organisms/JModal.vue.cjs +1 -1
  107. package/components/organisms/JModal.vue.cjs.map +1 -1
  108. package/components/organisms/JModal.vue.js +47 -45
  109. package/components/organisms/JModal.vue.js.map +1 -1
  110. package/components/organisms/JPageContainer.vue.cjs +1 -1
  111. package/components/organisms/JPageContainer.vue.cjs.map +1 -1
  112. package/components/organisms/JPageContainer.vue.js +22 -22
  113. package/components/organisms/JPageContainer.vue.js.map +1 -1
  114. package/components/organisms/JSearchPanel.vue2.cjs +1 -1
  115. package/components/organisms/JSearchPanel.vue2.cjs.map +1 -1
  116. package/components/organisms/JSearchPanel.vue2.js +34 -32
  117. package/components/organisms/JSearchPanel.vue2.js.map +1 -1
  118. package/components/organisms/JShuttle.vue.cjs +7 -0
  119. package/components/organisms/JShuttle.vue.cjs.map +1 -0
  120. package/components/organisms/JShuttle.vue.js +13 -0
  121. package/components/organisms/JShuttle.vue.js.map +1 -0
  122. package/components/organisms/JShuttle.vue2.cjs +2 -0
  123. package/components/organisms/JShuttle.vue2.cjs.map +1 -0
  124. package/components/organisms/JShuttle.vue2.js +216 -0
  125. package/components/organisms/JShuttle.vue2.js.map +1 -0
  126. package/components/organisms/JSidebarSimple/JDynamicMenuItem.vue.cjs +1 -1
  127. package/components/organisms/JSidebarSimple/JDynamicMenuItem.vue.cjs.map +1 -1
  128. package/components/organisms/JSidebarSimple/JDynamicMenuItem.vue.js +52 -52
  129. package/components/organisms/JSidebarSimple/JDynamicMenuItem.vue.js.map +1 -1
  130. package/components/shadcn/Card.vue.cjs +1 -1
  131. package/components/shadcn/Card.vue.cjs.map +1 -1
  132. package/components/shadcn/Card.vue.js +1 -1
  133. package/components/shadcn/Card.vue.js.map +1 -1
  134. package/components/shadcn/CardContent.vue.cjs +1 -1
  135. package/components/shadcn/CardContent.vue.cjs.map +1 -1
  136. package/components/shadcn/CardContent.vue.js +4 -4
  137. package/components/shadcn/CardContent.vue.js.map +1 -1
  138. package/components/shadcn/CardHeader.vue.cjs +1 -1
  139. package/components/shadcn/CardHeader.vue.cjs.map +1 -1
  140. package/components/shadcn/CardHeader.vue.js +5 -5
  141. package/components/shadcn/CardHeader.vue.js.map +1 -1
  142. package/components/shadcn/Input.vue.cjs +1 -1
  143. package/components/shadcn/Input.vue.cjs.map +1 -1
  144. package/components/shadcn/Input.vue.js +3 -3
  145. package/components/shadcn/Input.vue.js.map +1 -1
  146. package/components/shadcn/SelectTrigger.vue.cjs +1 -1
  147. package/components/shadcn/SelectTrigger.vue.cjs.map +1 -1
  148. package/components/shadcn/SelectTrigger.vue.js +1 -1
  149. package/components/shadcn/SelectTrigger.vue.js.map +1 -1
  150. package/components/shadcn/TabsContent.vue.cjs +1 -1
  151. package/components/shadcn/TabsContent.vue.cjs.map +1 -1
  152. package/components/shadcn/TabsContent.vue.js +1 -1
  153. package/components/shadcn/TabsContent.vue.js.map +1 -1
  154. package/components/shadcn/TabsList.vue.cjs +1 -1
  155. package/components/shadcn/TabsList.vue.cjs.map +1 -1
  156. package/components/shadcn/TabsList.vue.js +10 -10
  157. package/components/shadcn/TabsList.vue.js.map +1 -1
  158. package/components/shadcn/Textarea.vue.cjs +1 -1
  159. package/components/shadcn/Textarea.vue.cjs.map +1 -1
  160. package/components/shadcn/Textarea.vue.js +1 -1
  161. package/components/shadcn/Textarea.vue.js.map +1 -1
  162. package/components/shadcn/index.cjs +1 -1
  163. package/components/shadcn/index.cjs.map +1 -1
  164. package/components/shadcn/index.js +4 -4
  165. package/components/shadcn/index.js.map +1 -1
  166. package/components/templates/JLayout.vue.cjs.map +1 -1
  167. package/components/templates/JLayout.vue.js.map +1 -1
  168. package/index.cjs +1 -1
  169. package/index.js +73 -67
  170. package/package.json +1 -1
  171. package/types/index.d.ts +920 -777
  172. package/assets/jwms-portal-frontend-CwxPfHfa.css +0 -1
  173. package/components/molecules/JFormField.vue3.cjs +0 -2
  174. package/components/molecules/JFormField.vue3.cjs.map +0 -1
  175. package/components/molecules/JFormField.vue3.js +0 -6
  176. package/components/molecules/JFormField.vue3.js.map +0 -1
@@ -0,0 +1,216 @@
1
+ import { defineComponent as M, ref as m, computed as C, createElementBlock as u, openBlock as c, normalizeClass as z, unref as o, createElementVNode as t, createCommentVNode as L, toDisplayString as f, createVNode as i, withCtx as g, Fragment as _, createTextVNode as w } from "vue";
2
+ import V from "../atoms/JButton.vue.js";
3
+ import T from "../atoms/JInput.vue.js";
4
+ import "../shadcn/index.js";
5
+ import "lucide-vue-next";
6
+ import { cn as J } from "../../lib/utils.js";
7
+ import "@internationalized/date";
8
+ import "md-editor-v3";
9
+ /* empty css */
10
+ /* empty css */
11
+ import "../shadcn/badge-variants.js";
12
+ import "@vueuse/core";
13
+ import "reka-ui";
14
+ /* empty css */
15
+ import "../shadcn/avatar-variants.js";
16
+ import j from "../atoms/JIcon.vue.js";
17
+ import "dompurify";
18
+ /* empty css */
19
+ import S from "../atoms/JGrid.vue.js";
20
+ /* empty css */
21
+ /* empty css */
22
+ import "vue-sonner";
23
+ const E = { class: "j-shuttle-container" }, O = { class: "j-shuttle-panel" }, $ = { class: "j-shuttle-panel-card" }, F = { class: "j-shuttle-panel-header" }, q = { class: "j-shuttle-panel-title" }, A = { class: "j-shuttle-panel-count" }, H = {
24
+ key: 0,
25
+ class: "j-shuttle-search"
26
+ }, I = { class: "j-shuttle-grid-wrapper" }, K = { class: "j-shuttle-panel-footer" }, P = { class: "text-xs text-muted-foreground" }, Q = { class: "j-shuttle-actions" }, W = { class: "j-shuttle-actions-buttons" }, X = { class: "j-shuttle-panel" }, Y = { class: "j-shuttle-panel-card" }, Z = { class: "j-shuttle-panel-header j-shuttle-panel-header--primary" }, ee = { class: "j-shuttle-panel-title" }, te = { class: "j-shuttle-panel-count j-shuttle-panel-count--primary" }, se = {
27
+ key: 0,
28
+ class: "j-shuttle-search"
29
+ }, le = { class: "j-shuttle-grid-wrapper" }, ae = { class: "j-shuttle-panel-footer" }, oe = { class: "text-xs text-muted-foreground" }, Le = /* @__PURE__ */ M({
30
+ __name: "JShuttle",
31
+ props: {
32
+ leftTitle: { default: "Unmapped" },
33
+ rightTitle: { default: "Mapped" },
34
+ leftData: {},
35
+ rightData: {},
36
+ columnDefs: {},
37
+ searchable: { type: Boolean, default: !1 },
38
+ class: {}
39
+ },
40
+ emits: ["update:leftData", "update:rightData", "move"],
41
+ setup(d, { expose: N, emit: U }) {
42
+ const a = d, h = U, n = m([]), r = m([]), p = m(""), v = m(""), b = m(), y = m(), k = C(() => {
43
+ if (!a.searchable || !p.value)
44
+ return a.leftData;
45
+ const l = p.value.toLowerCase();
46
+ return a.leftData.filter((e) => Object.values(e).some(
47
+ (s) => String(s).toLowerCase().includes(l)
48
+ ));
49
+ }), R = C(() => {
50
+ if (!a.searchable || !v.value)
51
+ return a.rightData;
52
+ const l = v.value.toLowerCase();
53
+ return a.rightData.filter((e) => Object.values(e).some(
54
+ (s) => String(s).toLowerCase().includes(l)
55
+ ));
56
+ });
57
+ function G() {
58
+ if (n.value.length === 0) return;
59
+ const l = [...n.value], e = a.leftData.filter(
60
+ (D) => !l.some((x) => x.id === D.id)
61
+ ), s = [...a.rightData, ...l];
62
+ h("update:leftData", e), h("update:rightData", s), h("move", { items: l, direction: "toRight" }), n.value = [];
63
+ }
64
+ function B() {
65
+ if (r.value.length === 0) return;
66
+ const l = [...r.value], e = a.rightData.filter(
67
+ (D) => !l.some((x) => x.id === D.id)
68
+ ), s = [...a.leftData, ...l];
69
+ h("update:leftData", s), h("update:rightData", e), h("move", { items: l, direction: "toLeft" }), r.value = [];
70
+ }
71
+ return N({
72
+ leftGridRef: b,
73
+ rightGridRef: y
74
+ }), (l, e) => (c(), u("div", {
75
+ class: z(o(J)("j-shuttle", a.class))
76
+ }, [
77
+ t("div", E, [
78
+ t("div", O, [
79
+ t("div", $, [
80
+ t("div", F, [
81
+ t("h4", q, f(d.leftTitle || "Unmapped"), 1),
82
+ t("span", A, f(k.value.length) + " items ", 1)
83
+ ]),
84
+ d.searchable ? (c(), u("div", H, [
85
+ i(o(T), {
86
+ modelValue: p.value,
87
+ "onUpdate:modelValue": e[0] || (e[0] = (s) => p.value = s),
88
+ placeholder: "검색..."
89
+ }, {
90
+ prefix: g(() => [
91
+ i(o(j), {
92
+ name: "search",
93
+ class: "w-4 h-4 text-muted-foreground"
94
+ })
95
+ ]),
96
+ _: 1
97
+ }, 8, ["modelValue"])
98
+ ])) : L("", !0),
99
+ t("div", I, [
100
+ i(o(S), {
101
+ ref_key: "leftGridRef",
102
+ ref: b,
103
+ "row-data": k.value,
104
+ "column-defs": d.columnDefs,
105
+ checkbox: !0,
106
+ "row-numbers": !1,
107
+ pagination: !1,
108
+ "selected-rows": n.value,
109
+ "onUpdate:selectedRows": e[1] || (e[1] = (s) => n.value = s),
110
+ class: "h-full"
111
+ }, null, 8, ["row-data", "column-defs", "selected-rows"])
112
+ ]),
113
+ t("div", K, [
114
+ t("span", P, [
115
+ n.value.length > 0 ? (c(), u(_, { key: 0 }, [
116
+ t("strong", null, f(n.value.length), 1),
117
+ e[4] || (e[4] = w(" selected ", -1))
118
+ ], 64)) : (c(), u(_, { key: 1 }, [
119
+ w(" No items selected ")
120
+ ], 64))
121
+ ])
122
+ ])
123
+ ])
124
+ ]),
125
+ t("div", Q, [
126
+ e[5] || (e[5] = t("div", { class: "j-shuttle-actions-divider" }, null, -1)),
127
+ t("div", W, [
128
+ i(o(V), {
129
+ styletype: "primary",
130
+ size: "xs",
131
+ disabled: n.value.length === 0,
132
+ onClick: G,
133
+ class: "j-shuttle-action-btn",
134
+ title: "선택한 항목을 오른쪽으로 이동"
135
+ }, {
136
+ default: g(() => [
137
+ i(o(j), {
138
+ name: "chevronRight",
139
+ class: "w-3.5 h-3.5"
140
+ })
141
+ ]),
142
+ _: 1
143
+ }, 8, ["disabled"]),
144
+ i(o(V), {
145
+ variant: "outline",
146
+ size: "xs",
147
+ disabled: r.value.length === 0,
148
+ onClick: B,
149
+ class: "j-shuttle-action-btn",
150
+ title: "선택한 항목을 왼쪽으로 이동"
151
+ }, {
152
+ default: g(() => [
153
+ i(o(j), {
154
+ name: "chevronLeft",
155
+ class: "w-3.5 h-3.5"
156
+ })
157
+ ]),
158
+ _: 1
159
+ }, 8, ["disabled"])
160
+ ])
161
+ ]),
162
+ t("div", X, [
163
+ t("div", Y, [
164
+ t("div", Z, [
165
+ t("h4", ee, f(d.rightTitle || "Mapped"), 1),
166
+ t("span", te, f(R.value.length) + " items ", 1)
167
+ ]),
168
+ d.searchable ? (c(), u("div", se, [
169
+ i(o(T), {
170
+ modelValue: v.value,
171
+ "onUpdate:modelValue": e[2] || (e[2] = (s) => v.value = s),
172
+ placeholder: "검색..."
173
+ }, {
174
+ prefix: g(() => [
175
+ i(o(j), {
176
+ name: "search",
177
+ class: "w-4 h-4 text-muted-foreground"
178
+ })
179
+ ]),
180
+ _: 1
181
+ }, 8, ["modelValue"])
182
+ ])) : L("", !0),
183
+ t("div", le, [
184
+ i(o(S), {
185
+ ref_key: "rightGridRef",
186
+ ref: y,
187
+ "row-data": R.value,
188
+ "column-defs": d.columnDefs,
189
+ checkbox: !0,
190
+ "row-numbers": !1,
191
+ pagination: !1,
192
+ "selected-rows": r.value,
193
+ "onUpdate:selectedRows": e[3] || (e[3] = (s) => r.value = s),
194
+ class: "h-full"
195
+ }, null, 8, ["row-data", "column-defs", "selected-rows"])
196
+ ]),
197
+ t("div", ae, [
198
+ t("span", oe, [
199
+ r.value.length > 0 ? (c(), u(_, { key: 0 }, [
200
+ t("strong", null, f(r.value.length), 1),
201
+ e[6] || (e[6] = w(" selected ", -1))
202
+ ], 64)) : (c(), u(_, { key: 1 }, [
203
+ w(" No items selected ")
204
+ ], 64))
205
+ ])
206
+ ])
207
+ ])
208
+ ])
209
+ ])
210
+ ], 2));
211
+ }
212
+ });
213
+ export {
214
+ Le as default
215
+ };
216
+ //# sourceMappingURL=JShuttle.vue2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JShuttle.vue2.js","sources":["../../../../src/components/organisms/JShuttle.vue"],"sourcesContent":["<template>\n <div :class=\"cn('j-shuttle', props.class)\">\n <div class=\"j-shuttle-container\">\n <!-- 왼쪽 패널 -->\n <div class=\"j-shuttle-panel\">\n <div class=\"j-shuttle-panel-card\">\n <!-- 헤더 -->\n <div class=\"j-shuttle-panel-header\">\n <h4 class=\"j-shuttle-panel-title\">\n {{ leftTitle || 'Unmapped' }}\n </h4>\n <span class=\"j-shuttle-panel-count\">\n {{ filteredLeftData.length }} items\n </span>\n </div>\n\n <!-- 검색 -->\n <div v-if=\"searchable\" class=\"j-shuttle-search\">\n <JInput\n v-model=\"leftSearch\"\n placeholder=\"검색...\"\n >\n <template #prefix>\n <JIcon name=\"search\" class=\"w-4 h-4 text-muted-foreground\" />\n </template>\n </JInput>\n </div>\n\n <!-- 그리드 -->\n <div class=\"j-shuttle-grid-wrapper\">\n <JGrid\n ref=\"leftGridRef\"\n :row-data=\"filteredLeftData\"\n :column-defs=\"columnDefs\"\n :checkbox=\"true\"\n :row-numbers=\"false\"\n :pagination=\"false\"\n v-model:selected-rows=\"leftSelectedRows\"\n class=\"h-full\"\n />\n </div>\n\n <!-- 푸터 (선택 개수) -->\n <div class=\"j-shuttle-panel-footer\">\n <span class=\"text-xs text-muted-foreground\">\n <template v-if=\"leftSelectedRows.length > 0\">\n <strong>{{ leftSelectedRows.length }}</strong> selected\n </template>\n <template v-else>\n No items selected\n </template>\n </span>\n </div>\n </div>\n </div>\n\n <!-- 중앙 버튼 영역 -->\n <div class=\"j-shuttle-actions\">\n <div class=\"j-shuttle-actions-divider\" />\n <div class=\"j-shuttle-actions-buttons\">\n <JButton\n styletype=\"primary\"\n size=\"xs\"\n :disabled=\"leftSelectedRows.length === 0\"\n @click=\"moveToRight\"\n class=\"j-shuttle-action-btn\"\n title=\"선택한 항목을 오른쪽으로 이동\"\n >\n <JIcon name=\"chevronRight\" class=\"w-3.5 h-3.5\" />\n </JButton>\n <JButton\n variant=\"outline\"\n size=\"xs\"\n :disabled=\"rightSelectedRows.length === 0\"\n @click=\"moveToLeft\"\n class=\"j-shuttle-action-btn\"\n title=\"선택한 항목을 왼쪽으로 이동\"\n >\n <JIcon name=\"chevronLeft\" class=\"w-3.5 h-3.5\" />\n </JButton>\n </div>\n </div>\n\n <!-- 오른쪽 패널 -->\n <div class=\"j-shuttle-panel\">\n <div class=\"j-shuttle-panel-card\">\n <!-- 헤더 -->\n <div class=\"j-shuttle-panel-header j-shuttle-panel-header--primary\">\n <h4 class=\"j-shuttle-panel-title\">\n {{ rightTitle || 'Mapped' }}\n </h4>\n <span class=\"j-shuttle-panel-count j-shuttle-panel-count--primary\">\n {{ filteredRightData.length }} items\n </span>\n </div>\n\n <!-- 검색 -->\n <div v-if=\"searchable\" class=\"j-shuttle-search\">\n <JInput\n v-model=\"rightSearch\"\n placeholder=\"검색...\"\n >\n <template #prefix>\n <JIcon name=\"search\" class=\"w-4 h-4 text-muted-foreground\" />\n </template>\n </JInput>\n </div>\n\n <!-- 그리드 -->\n <div class=\"j-shuttle-grid-wrapper\">\n <JGrid\n ref=\"rightGridRef\"\n :row-data=\"filteredRightData\"\n :column-defs=\"columnDefs\"\n :checkbox=\"true\"\n :row-numbers=\"false\"\n :pagination=\"false\"\n v-model:selected-rows=\"rightSelectedRows\"\n class=\"h-full\"\n />\n </div>\n\n <!-- 푸터 (선택 개수) -->\n <div class=\"j-shuttle-panel-footer\">\n <span class=\"text-xs text-muted-foreground\">\n <template v-if=\"rightSelectedRows.length > 0\">\n <strong>{{ rightSelectedRows.length }}</strong> selected\n </template>\n <template v-else>\n No items selected\n </template>\n </span>\n </div>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed } from 'vue'\nimport { JGrid, JButton, JIcon, JInput } from '@/components/atoms'\nimport { cn } from '@/lib/utils'\nimport type { JShuttleProps, JShuttleEmits, ShuttleItem } from '@/types/shuttle.types'\n\nconst props = withDefaults(defineProps<JShuttleProps>(), {\n searchable: false,\n leftTitle: 'Unmapped',\n rightTitle: 'Mapped',\n})\n\nconst emit = defineEmits<JShuttleEmits>()\n\n// 선택된 행\nconst leftSelectedRows = ref<ShuttleItem[]>([])\nconst rightSelectedRows = ref<ShuttleItem[]>([])\n\n// 검색어\nconst leftSearch = ref('')\nconst rightSearch = ref('')\n\n// 그리드 참조\nconst leftGridRef = ref()\nconst rightGridRef = ref()\n\n// 검색 필터링된 데이터\nconst filteredLeftData = computed(() => {\n if (!props.searchable || !leftSearch.value) {\n return props.leftData\n }\n const search = leftSearch.value.toLowerCase()\n return props.leftData.filter((item) => {\n return Object.values(item).some((val) =>\n String(val).toLowerCase().includes(search)\n )\n })\n})\n\nconst filteredRightData = computed(() => {\n if (!props.searchable || !rightSearch.value) {\n return props.rightData\n }\n const search = rightSearch.value.toLowerCase()\n return props.rightData.filter((item) => {\n return Object.values(item).some((val) =>\n String(val).toLowerCase().includes(search)\n )\n })\n})\n\n// 왼쪽 → 오른쪽 이동\nfunction moveToRight() {\n if (leftSelectedRows.value.length === 0) return\n\n const itemsToMove = [...leftSelectedRows.value]\n const newLeftData = props.leftData.filter(\n (item) => !itemsToMove.some((selected) => selected.id === item.id)\n )\n const newRightData = [...props.rightData, ...itemsToMove]\n\n emit('update:leftData', newLeftData)\n emit('update:rightData', newRightData)\n emit('move', { items: itemsToMove, direction: 'toRight' })\n\n leftSelectedRows.value = []\n}\n\n// 오른쪽 → 왼쪽 이동\nfunction moveToLeft() {\n if (rightSelectedRows.value.length === 0) return\n\n const itemsToMove = [...rightSelectedRows.value]\n const newRightData = props.rightData.filter(\n (item) => !itemsToMove.some((selected) => selected.id === item.id)\n )\n const newLeftData = [...props.leftData, ...itemsToMove]\n\n emit('update:leftData', newLeftData)\n emit('update:rightData', newRightData)\n emit('move', { items: itemsToMove, direction: 'toLeft' })\n\n rightSelectedRows.value = []\n}\n\ndefineExpose({\n leftGridRef,\n rightGridRef,\n})\n</script>\n\n<style scoped>\n.j-shuttle {\n width: 100%;\n height: 100%;\n min-height: 500px;\n}\n\n.j-shuttle-container {\n display: flex;\n gap: 0.5rem;\n height: 100%;\n padding: 0;\n border-radius: 0;\n background: transparent;\n}\n\n.j-shuttle-panel {\n flex: 1;\n display: flex;\n flex-direction: column;\n}\n\n.j-shuttle-panel-card {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: hsl(var(--card));\n border: 1px solid hsl(var(--border));\n border-radius: 0.125rem; /* rounded-sm */\n box-shadow: none;\n overflow: hidden;\n}\n\n.j-shuttle-panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.4rem 1rem;\n border-bottom: 1px solid hsl(var(--border));\n background: hsl(var(--muted) / 0.3);\n transition: background-color 0.2s ease;\n}\n\n.j-shuttle-panel-header--primary {\n background: linear-gradient(135deg,\n hsl(var(--primary) / 0.08) 0%,\n hsl(var(--primary) / 0.02) 100%);\n}\n\n.j-shuttle-panel-title {\n font-size: 0.8125rem;\n font-weight: 600;\n color: hsl(var(--foreground));\n letter-spacing: -0.01em;\n}\n\n.j-shuttle-panel-count {\n font-size: 0.75rem;\n font-weight: 500;\n color: hsl(var(--muted-foreground));\n padding: 0.25rem 0.625rem;\n border-radius: 0.125rem; /* rounded-sm */\n background: hsl(var(--muted));\n border: 1px solid hsl(var(--border) / 0.5);\n}\n\n.j-shuttle-panel-count--primary {\n color: hsl(var(--primary));\n background: hsl(var(--primary) / 0.1);\n border-color: hsl(var(--primary) / 0.2);\n}\n\n.j-shuttle-search {\n padding: 0.75rem 1rem;\n border-bottom: 1px solid hsl(var(--border) / 0.5);\n background: hsl(var(--card));\n}\n\n.j-shuttle-grid-wrapper {\n flex: 1;\n overflow: hidden;\n padding: 0;\n background: hsl(var(--background));\n}\n\n.j-shuttle-panel-footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n padding: 0.625rem 1rem;\n border-top: 1px solid hsl(var(--border) / 0.5);\n background: hsl(var(--muted) / 0.2);\n min-height: 2.5rem;\n}\n\n.j-shuttle-actions {\n position: relative;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 0;\n min-width: 2.5rem;\n}\n\n.j-shuttle-actions-divider {\n position: absolute;\n width: 1px;\n height: 100%;\n background: linear-gradient(to bottom,\n transparent 0%,\n hsl(var(--border)) 10%,\n hsl(var(--border)) 90%,\n transparent 100%);\n}\n\n.j-shuttle-actions-buttons {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: 0.375rem;\n padding: 0.375rem;\n background: transparent;\n border: none;\n box-shadow: none;\n}\n\n.j-shuttle-action-btn {\n min-width: 2rem;\n min-height: 2rem;\n transition: all 0.2s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.j-shuttle-action-btn:not(:disabled):hover {\n transform: scale(1.08);\n box-shadow: 0 2px 6px 0 rgb(0 0 0 / 0.12);\n}\n\n.j-shuttle-action-btn:not(:disabled):active {\n transform: scale(1.02);\n}\n\n.j-shuttle-action-btn:disabled {\n opacity: 1;\n background: hsl(var(--muted));\n color: hsl(var(--muted-foreground) / 0.4);\n border-color: hsl(var(--border));\n cursor: not-allowed;\n transform: scale(1);\n}\n\n/* 다크모드 조정 */\n.dark .j-shuttle-container {\n background: linear-gradient(135deg, \n hsl(var(--muted) / 0.2) 0%, \n hsl(var(--muted) / 0.05) 100%);\n}\n\n.dark .j-shuttle-panel-card {\n box-shadow: 0 2px 4px 0 rgb(0 0 0 / 0.2);\n}\n\n.dark .j-shuttle-panel-card:hover {\n box-shadow: 0 4px 6px 0 rgb(0 0 0 / 0.3);\n}\n</style>\n"],"names":["props","__props","emit","__emit","leftSelectedRows","ref","rightSelectedRows","leftSearch","rightSearch","leftGridRef","rightGridRef","filteredLeftData","computed","search","item","val","filteredRightData","moveToRight","itemsToMove","newLeftData","selected","newRightData","moveToLeft","__expose","_createElementBlock","_normalizeClass","_unref","cn","_createElementVNode","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_toDisplayString","_hoisted_6","_openBlock","_hoisted_7","_createVNode","JInput","$event","JIcon","_hoisted_8","JGrid","_hoisted_9","_hoisted_10","_Fragment","_hoisted_11","_hoisted_12","JButton","_hoisted_13","_hoisted_14","_hoisted_15","_hoisted_16","_hoisted_17","_hoisted_18","_hoisted_19","_hoisted_20","_hoisted_21"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue"),F=require("vue-router"),f=require("../../atoms/JIcon.vue.cjs"),u=require("../../../lib/utils.cjs"),S={key:1,class:"w-4 flex-shrink-0"},E={key:0,class:"w-full"},P=e.defineComponent({__name:"JDynamicMenuItem",props:{item:{},level:{default:0},permissions:{default:()=>[]},activePath:{},expandedKeys:{default:()=>new Set},favorites:{default:()=>[]},onFavoriteToggle:{},isFavorite:{},styletype:{default:"default"},className:{},maxDepth:{default:10},disableNavigation:{type:Boolean,default:!1},activeKey:{default:null}},emits:["menuClick","expandChange"],setup(t,{emit:k}){const n=t,r=k,x=F.useRouter(),C=e.computed(()=>u.hasMenuPermission(n.item.menuKey,n.permissions)),m=e.computed(()=>n.activeKey!==void 0&&n.activeKey!==null?n.item.menuKey===n.activeKey:!n.item.path||!n.activePath?!1:n.activePath===n.item.path),o=e.computed(()=>n.item.menuType==="F"||Array.isArray(n.item.children)&&n.item.children.length>0),d=e.computed(()=>{if(!o.value)return!1;const i=n.item.menuKey||n.item.label;return n.expandedKeys?.has(i)??!1}),v=e.computed(()=>n.item.disabled||!C.value),p=e.computed(()=>({paddingLeft:`${8+(n.level||0)*12}px`})),b=i=>{if(v.value){i.preventDefault();return}if(o.value){const a=n.item.menuKey||n.item.label,s=!d.value;r("expandChange",a,s),r("menuClick",{menuItem:n.item,path:[n.item],event:i})}else!n.disableNavigation&&n.item.path&&x.push(n.item.path),r("menuClick",{menuItem:n.item,path:[n.item],event:i})},h={default:{itemClass:"flex items-center gap-1.5 py-1 rounded-md cursor-pointer transition-colors group",labelClass:"flex-1 truncate text-xs",iconSize:"sm"},minimal:{itemClass:"flex items-center gap-1 py-1 rounded-md cursor-pointer transition-colors group",labelClass:"flex-1 truncate text-xs",iconSize:"sm"}},c=e.computed(()=>h[n.styletype]??h.default),B=e.computed(()=>d.value?"chevronDown":"chevronRight"),g=e.computed(()=>{if(!o.value||!Array.isArray(n.item.children))return 0;const i=a=>{let s=0;for(const l of a)s++,Array.isArray(l.children)&&l.children.length>0&&(s+=i(l.children));return s};return i(n.item.children)});return(i,a)=>{const s=e.resolveComponent("JDynamicMenuItem",!0);return e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(e.unref(u.cn)("w-full",t.className))},[e.createElementVNode("div",{class:e.normalizeClass(e.unref(u.cn)(c.value.itemClass,{"bg-accent text-accent-foreground":m.value,"hover:bg-accent/50":!v.value&&!m.value,"opacity-50 cursor-not-allowed":v.value,"font-medium":m.value,"font-semibold":o.value})),style:e.normalizeStyle(p.value),onClick:b},[o.value?(e.openBlock(),e.createBlock(f.default,{key:0,name:B.value,size:c.value.iconSize,class:"flex-shrink-0 opacity-60",style:{"stroke-width":"1.5"}},null,8,["name","size"])):(e.openBlock(),e.createElementBlock("span",S)),t.item.icon&&!o.value?(e.openBlock(),e.createBlock(f.default,{key:2,name:t.item.icon,size:c.value.iconSize,class:"flex-shrink-0"},null,8,["name","size"])):e.createCommentVNode("",!0),e.createElementVNode("span",{class:e.normalizeClass(c.value.labelClass)},e.toDisplayString(t.item.label),3),o.value&&g.value>0?(e.openBlock(),e.createElementBlock("span",{key:3,class:e.normalizeClass(e.unref(u.cn)("text-muted-foreground ml-1 flex-shrink-0",n.styletype==="minimal"?"text-[10px]":"text-xs"))}," ("+e.toDisplayString(g.value)+") ",3)):e.createCommentVNode("",!0),t.item.menuKey&&t.item.menuType==="L"&&t.onFavoriteToggle?(e.openBlock(),e.createElementBlock("button",{key:4,class:e.normalizeClass(e.unref(u.cn)("opacity-0 group-hover:opacity-100 transition-opacity hover:bg-accent rounded flex-shrink-0",n.styletype==="minimal"?"p-0.5":"p-1",t.isFavorite&&t.isFavorite(t.item.menuKey)&&"opacity-100")),onClick:a[0]||(a[0]=e.withModifiers(l=>t.onFavoriteToggle(t.item.menuKey),["stop"]))},[e.createVNode(f.default,{name:(t.isFavorite&&t.isFavorite(t.item.menuKey),"star"),size:c.value.iconSize,class:e.normalizeClass(t.isFavorite&&t.isFavorite(t.item.menuKey)?"text-yellow-500 fill-yellow-500":"text-muted-foreground")},null,8,["name","size","class"])],2)):e.createCommentVNode("",!0)],6),o.value&&d.value&&t.item.children&&Array.isArray(t.item.children)&&t.item.children.length>0&&t.level+1<t.maxDepth?(e.openBlock(),e.createElementBlock("div",E,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(t.item.children,(l,z)=>(e.openBlock(),e.createBlock(s,{key:l.menuKey||l.label||z,item:l,level:t.level+1,"max-depth":t.maxDepth,permissions:t.permissions,"active-path":t.activePath,"expanded-keys":t.expandedKeys,favorites:t.favorites,"on-favorite-toggle":t.onFavoriteToggle,"is-favorite":t.isFavorite,styletype:t.styletype,"disable-navigation":t.disableNavigation,"active-key":t.activeKey,onMenuClick:a[1]||(a[1]=y=>r("menuClick",y)),onExpandChange:a[2]||(a[2]=(y,K)=>r("expandChange",y,K))},null,8,["item","level","max-depth","permissions","active-path","expanded-keys","favorites","on-favorite-toggle","is-favorite","styletype","disable-navigation","active-key"]))),128))])):e.createCommentVNode("",!0)],2)}}});exports.default=P;
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue"),F=require("vue-router"),f=require("../../atoms/JIcon.vue.cjs"),c=require("../../../lib/utils.cjs"),S={key:1,class:"w-4 flex-shrink-0"},E={key:0,class:"border-l border-border/60 ml-[14px] pl-[6px]"},N=e.defineComponent({__name:"JDynamicMenuItem",props:{item:{},level:{default:0},permissions:{default:()=>[]},activePath:{},expandedKeys:{default:()=>new Set},favorites:{default:()=>[]},onFavoriteToggle:{},isFavorite:{},styletype:{default:"default"},className:{},maxDepth:{default:10},disableNavigation:{type:Boolean,default:!1},activeKey:{default:null}},emits:["menuClick","expandChange"],setup(t,{emit:g}){const n=t,s=g,x=F.useRouter(),p=e.computed(()=>c.hasMenuPermission(n.item.menuKey,n.permissions)),m=e.computed(()=>n.activeKey!==void 0&&n.activeKey!==null?n.item.menuKey===n.activeKey:!n.item.path||!n.activePath?!1:n.activePath===n.item.path),l=e.computed(()=>n.item.menuType==="F"||Array.isArray(n.item.children)&&n.item.children.length>0),d=e.computed(()=>{if(!l.value)return!1;const a=n.item.menuKey||n.item.label;return n.expandedKeys?.has(a)??!1}),v=e.computed(()=>n.item.disabled||!p.value),C=e.computed(()=>({paddingLeft:"8px",paddingRight:"8px"})),b=a=>{if(v.value){a.preventDefault();return}if(l.value){const i=n.item.menuKey||n.item.label,r=!d.value;s("expandChange",i,r),s("menuClick",{menuItem:n.item,path:[n.item],event:a})}else!n.disableNavigation&&n.item.path&&x.push(n.item.path),s("menuClick",{menuItem:n.item,path:[n.item],event:a})},h={default:{itemClass:"flex items-center gap-1.5 py-1 rounded-md cursor-pointer transition-colors group",labelClass:"flex-1 truncate text-xs",iconSize:"sm"},minimal:{itemClass:"flex items-center gap-1 py-1 rounded-md cursor-pointer transition-colors group",labelClass:"flex-1 truncate text-xs",iconSize:"sm"}},u=e.computed(()=>h[n.styletype]??h.default),B=e.computed(()=>d.value?"chevronDown":"chevronRight"),k=e.computed(()=>{if(!l.value||!Array.isArray(n.item.children))return 0;const a=i=>{let r=0;for(const o of i)r++,Array.isArray(o.children)&&o.children.length>0&&(r+=a(o.children));return r};return a(n.item.children)});return(a,i)=>{const r=e.resolveComponent("JDynamicMenuItem",!0);return e.openBlock(),e.createElementBlock("div",{class:e.normalizeClass(e.unref(c.cn)("w-full",t.className))},[e.createElementVNode("div",{class:e.normalizeClass(e.unref(c.cn)(u.value.itemClass,{"bg-primary/5 text-primary border-l-2 border-primary shadow-sm":m.value,"hover:bg-accent/50":!v.value&&!m.value,"opacity-50 cursor-not-allowed":v.value,"font-medium":m.value,"font-semibold":l.value})),style:e.normalizeStyle(C.value),onClick:b},[l.value?(e.openBlock(),e.createBlock(f.default,{key:0,name:B.value,size:u.value.iconSize,class:"flex-shrink-0 opacity-60",style:{"stroke-width":"1.5"}},null,8,["name","size"])):(e.openBlock(),e.createElementBlock("span",S)),t.item.icon&&!l.value?(e.openBlock(),e.createBlock(f.default,{key:2,name:t.item.icon,size:u.value.iconSize,class:"flex-shrink-0"},null,8,["name","size"])):e.createCommentVNode("",!0),e.createElementVNode("span",{class:e.normalizeClass(u.value.labelClass)},e.toDisplayString(t.item.label),3),l.value&&k.value>0?(e.openBlock(),e.createElementBlock("span",{key:3,class:e.normalizeClass(e.unref(c.cn)("text-muted-foreground ml-1 flex-shrink-0",n.styletype==="minimal"?"text-[10px]":"text-xs"))}," ("+e.toDisplayString(k.value)+") ",3)):e.createCommentVNode("",!0),t.item.menuKey&&t.item.menuType==="L"&&t.onFavoriteToggle?(e.openBlock(),e.createElementBlock("button",{key:4,class:e.normalizeClass(e.unref(c.cn)("opacity-0 group-hover:opacity-100 transition-opacity hover:bg-accent rounded flex-shrink-0",n.styletype==="minimal"?"p-0.5":"p-1",t.isFavorite&&t.isFavorite(t.item.menuKey)&&"opacity-100")),onClick:i[0]||(i[0]=e.withModifiers(o=>t.onFavoriteToggle(t.item.menuKey),["stop"]))},[e.createVNode(f.default,{name:(t.isFavorite&&t.isFavorite(t.item.menuKey),"star"),size:u.value.iconSize,class:e.normalizeClass(t.isFavorite&&t.isFavorite(t.item.menuKey)?"text-yellow-500 fill-yellow-500":"text-muted-foreground")},null,8,["name","size","class"])],2)):e.createCommentVNode("",!0)],6),l.value&&d.value&&t.item.children&&Array.isArray(t.item.children)&&t.item.children.length>0&&t.level+1<t.maxDepth?(e.openBlock(),e.createElementBlock("div",E,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(t.item.children,(o,z)=>(e.openBlock(),e.createBlock(r,{key:o.menuKey||o.label||z,item:o,level:t.level+1,"max-depth":t.maxDepth,permissions:t.permissions,"active-path":t.activePath,"expanded-keys":t.expandedKeys,favorites:t.favorites,"on-favorite-toggle":t.onFavoriteToggle,"is-favorite":t.isFavorite,styletype:t.styletype,"disable-navigation":t.disableNavigation,"active-key":t.activeKey,onMenuClick:i[1]||(i[1]=y=>s("menuClick",y)),onExpandChange:i[2]||(i[2]=(y,K)=>s("expandChange",y,K))},null,8,["item","level","max-depth","permissions","active-path","expanded-keys","favorites","on-favorite-toggle","is-favorite","styletype","disable-navigation","active-key"]))),128))])):e.createCommentVNode("",!0)],2)}}});exports.default=N;
2
2
  //# sourceMappingURL=JDynamicMenuItem.vue.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"JDynamicMenuItem.vue.cjs","sources":["../../../../../src/components/organisms/JSidebarSimple/JDynamicMenuItem.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { useRouter } from 'vue-router'\nimport type { SidebarMenuItem, MenuPermission, MenuClickEvent } from '@/types/sidebar-menu.types'\nimport JIcon from '@/components/atoms/JIcon.vue'\nimport { cn, hasMenuPermission } from '@/lib/utils'\n\n/**\n * JDynamicMenuItem - 재귀적 메뉴 아이템 컴포넌트\n * Recursive Menu Item Component\n * \n * @description\n * 다단계 메뉴 구조를 재귀적으로 렌더링하는 컴포넌트입니다.\n * 폴더 타입 메뉴는 확장/축소가 가능하고, 링크 타입 메뉴는 클릭 시 라우팅합니다.\n */\n\ntype StyleType = 'default' | 'minimal'\n\nconst props = withDefaults(\n defineProps<{\n /** 메뉴 아이템 */\n item: SidebarMenuItem\n /** 메뉴 레벨 (들여쓰기용, 0부터 시작) */\n level?: number\n /** 권한 목록 */\n permissions?: MenuPermission[]\n /** 활성화된 메뉴 경로 */\n activePath?: string\n /** 확장된 메뉴 키 목록 */\n expandedKeys?: Set<number | string>\n /** 즐겨찾기 메뉴 키 목록 */\n favorites?: (number | string)[]\n /** 즐겨찾기 변경 핸들러 */\n onFavoriteToggle?: (menuKey: number | string | undefined) => void\n /** 즐겨찾기 확인 함수 */\n isFavorite?: (menuKey: number | string | undefined) => boolean\n /** 스타일 타입 */\n styletype?: StyleType\n /** 추가 CSS 클래스 */\n className?: string\n /** 최대 깊이 제한 (무한 루프 방지, 기본값: 10) */\n maxDepth?: number\n /** 네비게이션 비활성화 (true일 때 router.push 건너뛰고 emit만 수행) */\n disableNavigation?: boolean\n /** 활성화된 메뉴 키 (menuKey 기반 활성화, activePath보다 우선) */\n activeKey?: number | string | null\n }>(),\n {\n level: 0,\n permissions: () => [],\n expandedKeys: () => new Set(),\n favorites: () => [],\n styletype: 'default',\n maxDepth: 10,\n disableNavigation: false,\n activeKey: null,\n },\n)\n\nconst emit = defineEmits<{\n /** 메뉴 클릭 이벤트 */\n menuClick: [event: MenuClickEvent]\n /** 확장 상태 변경 이벤트 */\n expandChange: [menuKey: number | string | undefined, expanded: boolean]\n}>()\n\nconst router = useRouter()\n\n/**\n * 권한 체크 함수\n * Permission check function\n * hasMenuPermission 유틸리티 함수를 사용하여 일관성 유지\n */\nconst checkPermission = computed(() => {\n return hasMenuPermission(props.item.menuKey, props.permissions)\n})\n\n/**\n * 메뉴가 활성화되어 있는지 여부\n * activeKey가 제공되면 menuKey 매칭, 아니면 경로 매칭\n */\nconst isActive = computed(() => {\n // activeKey가 제공되면 menuKey 기반 매칭 (우선순위 높음)\n if (props.activeKey !== undefined && props.activeKey !== null) {\n return props.item.menuKey === props.activeKey\n }\n // 경로 기반 매칭 (기본 동작)\n if (!props.item.path || !props.activePath) return false\n return props.activePath === props.item.path\n})\n\n/**\n * 메뉴 타입이 폴더인지 여부\n * 순환 참조 방지: children이 유효한 배열인지 확인\n */\nconst isFolder = computed(() => {\n return props.item.menuType === 'F' || (Array.isArray(props.item.children) && props.item.children.length > 0)\n})\n\n/**\n * 메뉴가 확장되어 있는지 여부\n */\nconst isExpanded = computed(() => {\n if (!isFolder.value) return false\n const key = props.item.menuKey || props.item.label\n return props.expandedKeys?.has(key) ?? false\n})\n\n/**\n * 메뉴가 비활성화되어 있는지 여부\n */\nconst isDisabled = computed(() => {\n return props.item.disabled || !checkPermission.value\n})\n\n/**\n * 레벨별 들여쓰기 스타일\n * Tailwind의 표준 클래스는 제한적이므로 인라인 스타일 사용\n */\nconst indentStyle = computed(() => {\n const basePadding = 8 // 기본 패딩 (px) - 12에서 8로 축소\n const level = props.level || 0\n const levelPadding = level * 12 // 레벨당 12px (16에서 12로 축소)\n const totalPadding = basePadding + levelPadding\n return { paddingLeft: `${totalPadding}px` }\n})\n\n/**\n * 메뉴 클릭 핸들러\n */\nconst handleMenuClick = (event: MouseEvent) => {\n if (isDisabled.value) {\n event.preventDefault()\n return\n }\n\n if (isFolder.value) {\n // 폴더 타입: 확장/축소 토글\n const key = props.item.menuKey || props.item.label\n const newExpanded = !isExpanded.value\n emit('expandChange', key, newExpanded)\n // 폴더도 메뉴 클릭 이벤트 발생\n emit('menuClick', {\n menuItem: props.item,\n path: [props.item],\n event,\n })\n } else {\n // 링크 타입: 라우팅 (disableNavigation이 false일 때만)\n if (!props.disableNavigation && props.item.path) {\n router.push(props.item.path)\n }\n \n // 메뉴 클릭 이벤트 발생\n emit('menuClick', {\n menuItem: props.item,\n path: [props.item], // 단순화된 경로 (필요시 부모 경로 포함하도록 확장 가능)\n event,\n })\n }\n}\n\n/**\n * 스타일 프리셋\n */\nconst STYLE_PRESETS: Record<StyleType, {\n itemClass: string\n labelClass: string\n iconSize: 'sm' | 'md'\n}> = {\n default: {\n itemClass: 'flex items-center gap-1.5 py-1 rounded-md cursor-pointer transition-colors group',\n labelClass: 'flex-1 truncate text-xs',\n iconSize: 'sm',\n },\n minimal: {\n itemClass: 'flex items-center gap-1 py-1 rounded-md cursor-pointer transition-colors group',\n labelClass: 'flex-1 truncate text-xs',\n iconSize: 'sm', // JIcon은 'xs'를 지원하지 않으므로 'sm' 사용\n },\n}\n\nconst preset = computed(() => {\n return STYLE_PRESETS[props.styletype] ?? STYLE_PRESETS.default\n})\n\n/**\n * Chevron 아이콘 컴포넌트\n */\nconst ChevronIcon = computed(() => {\n return isExpanded.value ? 'chevronDown' : 'chevronRight'\n})\n\n/**\n * 하위 메뉴 갯수 계산 (재귀적으로 모든 하위 메뉴 포함)\n * 폴더 타입인 경우에만 표시\n */\nconst childrenCount = computed(() => {\n if (!isFolder.value || !Array.isArray(props.item.children)) {\n return 0\n }\n \n const countChildren = (items: SidebarMenuItem[]): number => {\n let count = 0\n for (const item of items) {\n count++ // 현재 아이템 카운트\n if (Array.isArray(item.children) && item.children.length > 0) {\n count += countChildren(item.children) // 재귀적으로 하위 메뉴 카운트\n }\n }\n return count\n }\n \n return countChildren(props.item.children)\n})\n</script>\n\n<template>\n <div :class=\"cn('w-full', className)\">\n <!-- 메뉴 아이템 -->\n <div\n :class=\"cn(\n preset.itemClass,\n {\n 'bg-accent text-accent-foreground': isActive,\n 'hover:bg-accent/50': !isDisabled && !isActive,\n 'opacity-50 cursor-not-allowed': isDisabled,\n 'font-medium': isActive,\n 'font-semibold': isFolder, // 폴더인 경우 볼드체\n }\n )\"\n :style=\"indentStyle\"\n @click=\"handleMenuClick\"\n >\n <!-- Chevron 아이콘 (폴더 타입만, 덜 굵게) -->\n <JIcon\n v-if=\"isFolder\"\n :name=\"ChevronIcon\"\n :size=\"preset.iconSize\"\n class=\"flex-shrink-0 opacity-60\"\n style=\"stroke-width: 1.5;\"\n />\n <span v-else class=\"w-4 flex-shrink-0\" /> <!-- 폴더가 아닐 때 공간 확보 -->\n\n <!-- 메뉴 아이콘 (폴더가 아닌 경우만 표시) -->\n <JIcon\n v-if=\"item.icon && !isFolder\"\n :name=\"item.icon\"\n :size=\"preset.iconSize\"\n class=\"flex-shrink-0\"\n />\n\n <!-- 메뉴 라벨 -->\n <span :class=\"preset.labelClass\">{{ item.label }}</span>\n \n <!-- 하위 메뉴 갯수 (폴더인 경우만) -->\n <span\n v-if=\"isFolder && childrenCount > 0\"\n :class=\"cn(\n 'text-muted-foreground ml-1 flex-shrink-0',\n props.styletype === 'minimal' ? 'text-[10px]' : 'text-xs'\n )\"\n >\n ({{ childrenCount }})\n </span>\n \n <!-- 즐겨찾기 버튼 (menuType이 L인 경우만) -->\n <button\n v-if=\"item.menuKey && item.menuType === 'L' && onFavoriteToggle\"\n :class=\"cn(\n 'opacity-0 group-hover:opacity-100 transition-opacity hover:bg-accent rounded flex-shrink-0',\n props.styletype === 'minimal' ? 'p-0.5' : 'p-1',\n isFavorite && isFavorite(item.menuKey) && 'opacity-100'\n )\"\n @click.stop=\"onFavoriteToggle(item.menuKey)\"\n >\n <JIcon\n :name=\"isFavorite && isFavorite(item.menuKey) ? 'star' : 'star'\"\n :size=\"preset.iconSize\"\n :class=\"isFavorite && isFavorite(item.menuKey) ? 'text-yellow-500 fill-yellow-500' : 'text-muted-foreground'\"\n />\n </button>\n </div>\n\n <!-- 하위 메뉴 (폴더 타입이고 확장된 경우) -->\n <!-- 깊이 제한 체크: maxDepth를 초과하지 않는 경우에만 렌더링 -->\n <div\n v-if=\"isFolder && isExpanded && item.children && Array.isArray(item.children) && item.children.length > 0 && (level + 1) < maxDepth\"\n class=\"w-full\"\n >\n <JDynamicMenuItem\n v-for=\"(child, index) in item.children\"\n :key=\"child.menuKey || child.label || index\"\n :item=\"child\"\n :level=\"level + 1\"\n :max-depth=\"maxDepth\"\n :permissions=\"permissions\"\n :active-path=\"activePath\"\n :expanded-keys=\"expandedKeys\"\n :favorites=\"favorites\"\n :on-favorite-toggle=\"onFavoriteToggle\"\n :is-favorite=\"isFavorite\"\n :styletype=\"styletype\"\n :disable-navigation=\"disableNavigation\"\n :active-key=\"activeKey\"\n @menu-click=\"emit('menuClick', $event)\"\n @expand-change=\"(menuKey, expanded) => emit('expandChange', menuKey, expanded)\"\n />\n </div>\n </div>\n</template>\n"],"names":["props","__props","emit","__emit","router","useRouter","checkPermission","computed","hasMenuPermission","isActive","isFolder","isExpanded","key","isDisabled","indentStyle","handleMenuClick","event","newExpanded","STYLE_PRESETS","preset","ChevronIcon","childrenCount","countChildren","items","count","item","_createElementBlock","_normalizeClass","_unref","cn","_createElementVNode","_createBlock","JIcon","_openBlock","_hoisted_1","_toDisplayString","_cache","_withModifiers","$event","_createVNode","_hoisted_2","_Fragment","child","index","_component_JDynamicMenuItem","menuKey","expanded"],"mappings":"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"}
1
+ {"version":3,"file":"JDynamicMenuItem.vue.cjs","sources":["../../../../../src/components/organisms/JSidebarSimple/JDynamicMenuItem.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { useRouter } from 'vue-router'\nimport type { SidebarMenuItem, MenuPermission, MenuClickEvent } from '@/types/sidebar-menu.types'\nimport JIcon from '@/components/atoms/JIcon.vue'\nimport { cn, hasMenuPermission } from '@/lib/utils'\n\n/**\n * JDynamicMenuItem - 재귀적 메뉴 아이템 컴포넌트\n * Recursive Menu Item Component\n * \n * @description\n * 다단계 메뉴 구조를 재귀적으로 렌더링하는 컴포넌트입니다.\n * 폴더 타입 메뉴는 확장/축소가 가능하고, 링크 타입 메뉴는 클릭 시 라우팅합니다.\n */\n\ntype StyleType = 'default' | 'minimal'\n\nconst props = withDefaults(\n defineProps<{\n /** 메뉴 아이템 */\n item: SidebarMenuItem\n /** 메뉴 레벨 (들여쓰기용, 0부터 시작) */\n level?: number\n /** 권한 목록 */\n permissions?: MenuPermission[]\n /** 활성화된 메뉴 경로 */\n activePath?: string\n /** 확장된 메뉴 키 목록 */\n expandedKeys?: Set<number | string>\n /** 즐겨찾기 메뉴 키 목록 */\n favorites?: (number | string)[]\n /** 즐겨찾기 변경 핸들러 */\n onFavoriteToggle?: (menuKey: number | string | undefined) => void\n /** 즐겨찾기 확인 함수 */\n isFavorite?: (menuKey: number | string | undefined) => boolean\n /** 스타일 타입 */\n styletype?: StyleType\n /** 추가 CSS 클래스 */\n className?: string\n /** 최대 깊이 제한 (무한 루프 방지, 기본값: 10) */\n maxDepth?: number\n /** 네비게이션 비활성화 (true일 때 router.push 건너뛰고 emit만 수행) */\n disableNavigation?: boolean\n /** 활성화된 메뉴 키 (menuKey 기반 활성화, activePath보다 우선) */\n activeKey?: number | string | null\n }>(),\n {\n level: 0,\n permissions: () => [],\n expandedKeys: () => new Set(),\n favorites: () => [],\n styletype: 'default',\n maxDepth: 10,\n disableNavigation: false,\n activeKey: null,\n },\n)\n\nconst emit = defineEmits<{\n /** 메뉴 클릭 이벤트 */\n menuClick: [event: MenuClickEvent]\n /** 확장 상태 변경 이벤트 */\n expandChange: [menuKey: number | string | undefined, expanded: boolean]\n}>()\n\nconst router = useRouter()\n\n/**\n * 권한 체크 함수\n * Permission check function\n * hasMenuPermission 유틸리티 함수를 사용하여 일관성 유지\n */\nconst checkPermission = computed(() => {\n return hasMenuPermission(props.item.menuKey, props.permissions)\n})\n\n/**\n * 메뉴가 활성화되어 있는지 여부\n * activeKey가 제공되면 menuKey 매칭, 아니면 경로 매칭\n */\nconst isActive = computed(() => {\n // activeKey가 제공되면 menuKey 기반 매칭 (우선순위 높음)\n if (props.activeKey !== undefined && props.activeKey !== null) {\n return props.item.menuKey === props.activeKey\n }\n // 경로 기반 매칭 (기본 동작)\n if (!props.item.path || !props.activePath) return false\n return props.activePath === props.item.path\n})\n\n/**\n * 메뉴 타입이 폴더인지 여부\n * 순환 참조 방지: children이 유효한 배열인지 확인\n */\nconst isFolder = computed(() => {\n return props.item.menuType === 'F' || (Array.isArray(props.item.children) && props.item.children.length > 0)\n})\n\n/**\n * 메뉴가 확장되어 있는지 여부\n */\nconst isExpanded = computed(() => {\n if (!isFolder.value) return false\n const key = props.item.menuKey || props.item.label\n return props.expandedKeys?.has(key) ?? false\n})\n\n/**\n * 메뉴가 비활성화되어 있는지 여부\n */\nconst isDisabled = computed(() => {\n return props.item.disabled || !checkPermission.value\n})\n\n/**\n * 레벨별 들여쓰기 스타일\n * 모든 레벨에서 동일한 padding 사용 (들여쓰기는 컨테이너의 ml로 조절)\n */\nconst indentStyle = computed(() => {\n return { paddingLeft: '8px', paddingRight: '8px' }\n})\n\n\n\n/**\n * 메뉴 클릭 핸들러\n */\nconst handleMenuClick = (event: MouseEvent) => {\n if (isDisabled.value) {\n event.preventDefault()\n return\n }\n\n if (isFolder.value) {\n // 폴더 타입: 확장/축소 토글\n const key = props.item.menuKey || props.item.label\n const newExpanded = !isExpanded.value\n emit('expandChange', key, newExpanded)\n // 폴더도 메뉴 클릭 이벤트 발생\n emit('menuClick', {\n menuItem: props.item,\n path: [props.item],\n event,\n })\n } else {\n // 링크 타입: 라우팅 (disableNavigation이 false일 때만)\n if (!props.disableNavigation && props.item.path) {\n router.push(props.item.path)\n }\n \n // 메뉴 클릭 이벤트 발생\n emit('menuClick', {\n menuItem: props.item,\n path: [props.item], // 단순화된 경로 (필요시 부모 경로 포함하도록 확장 가능)\n event,\n })\n }\n}\n\n/**\n * 스타일 프리셋\n */\nconst STYLE_PRESETS: Record<StyleType, {\n itemClass: string\n labelClass: string\n iconSize: 'sm' | 'md'\n}> = {\n default: {\n itemClass: 'flex items-center gap-1.5 py-1 rounded-md cursor-pointer transition-colors group',\n labelClass: 'flex-1 truncate text-xs',\n iconSize: 'sm',\n },\n minimal: {\n itemClass: 'flex items-center gap-1 py-1 rounded-md cursor-pointer transition-colors group',\n labelClass: 'flex-1 truncate text-xs',\n iconSize: 'sm', // JIcon은 'xs'를 지원하지 않으므로 'sm' 사용\n },\n}\n\nconst preset = computed(() => {\n return STYLE_PRESETS[props.styletype] ?? STYLE_PRESETS.default\n})\n\n/**\n * Chevron 아이콘 컴포넌트\n */\nconst ChevronIcon = computed(() => {\n return isExpanded.value ? 'chevronDown' : 'chevronRight'\n})\n\n/**\n * 하위 메뉴 갯수 계산 (재귀적으로 모든 하위 메뉴 포함)\n * 폴더 타입인 경우에만 표시\n */\nconst childrenCount = computed(() => {\n if (!isFolder.value || !Array.isArray(props.item.children)) {\n return 0\n }\n \n const countChildren = (items: SidebarMenuItem[]): number => {\n let count = 0\n for (const item of items) {\n count++ // 현재 아이템 카운트\n if (Array.isArray(item.children) && item.children.length > 0) {\n count += countChildren(item.children) // 재귀적으로 하위 메뉴 카운트\n }\n }\n return count\n }\n \n return countChildren(props.item.children)\n})\n</script>\n\n<template>\n <div :class=\"cn('w-full', className)\">\n <!-- 메뉴 아이템 -->\n <div\n :class=\"cn(\n preset.itemClass,\n {\n 'bg-primary/5 text-primary border-l-2 border-primary shadow-sm': isActive,\n 'hover:bg-accent/50': !isDisabled && !isActive,\n 'opacity-50 cursor-not-allowed': isDisabled,\n 'font-medium': isActive,\n 'font-semibold': isFolder, // 폴더인 경우 볼드체\n }\n )\"\n :style=\"indentStyle\"\n @click=\"handleMenuClick\"\n >\n <!-- Chevron 아이콘 (폴더 타입만, 덜 굵게) -->\n <JIcon\n v-if=\"isFolder\"\n :name=\"ChevronIcon\"\n :size=\"preset.iconSize\"\n class=\"flex-shrink-0 opacity-60\"\n style=\"stroke-width: 1.5;\"\n />\n <span v-else class=\"w-4 flex-shrink-0\" /> <!-- 폴더가 아닐 때 공간 확보 -->\n\n <!-- 메뉴 아이콘 (폴더가 아닌 경우만 표시) -->\n <JIcon\n v-if=\"item.icon && !isFolder\"\n :name=\"item.icon\"\n :size=\"preset.iconSize\"\n class=\"flex-shrink-0\"\n />\n\n <!-- 메뉴 라벨 -->\n <span :class=\"preset.labelClass\">{{ item.label }}</span>\n \n <!-- 하위 메뉴 갯수 (폴더인 경우만) -->\n <span\n v-if=\"isFolder && childrenCount > 0\"\n :class=\"cn(\n 'text-muted-foreground ml-1 flex-shrink-0',\n props.styletype === 'minimal' ? 'text-[10px]' : 'text-xs'\n )\"\n >\n ({{ childrenCount }})\n </span>\n \n <!-- 즐겨찾기 버튼 (menuType이 L인 경우만) -->\n <button\n v-if=\"item.menuKey && item.menuType === 'L' && onFavoriteToggle\"\n :class=\"cn(\n 'opacity-0 group-hover:opacity-100 transition-opacity hover:bg-accent rounded flex-shrink-0',\n props.styletype === 'minimal' ? 'p-0.5' : 'p-1',\n isFavorite && isFavorite(item.menuKey) && 'opacity-100'\n )\"\n @click.stop=\"onFavoriteToggle(item.menuKey)\"\n >\n <JIcon\n :name=\"isFavorite && isFavorite(item.menuKey) ? 'star' : 'star'\"\n :size=\"preset.iconSize\"\n :class=\"isFavorite && isFavorite(item.menuKey) ? 'text-yellow-500 fill-yellow-500' : 'text-muted-foreground'\"\n />\n </button>\n </div>\n\n <!-- 하위 메뉴 (폴더 타입이고 확장된 경우) -->\n <!-- 깊이 제한 체크: maxDepth를 초과하지 않는 경우에만 렌더링 -->\n <div\n v-if=\"isFolder && isExpanded && item.children && Array.isArray(item.children) && item.children.length > 0 && (level + 1) < maxDepth\"\n class=\"border-l border-border/60 ml-[14px] pl-[6px]\"\n >\n <JDynamicMenuItem\n v-for=\"(child, index) in item.children\"\n :key=\"child.menuKey || child.label || index\"\n :item=\"child\"\n :level=\"level + 1\"\n :max-depth=\"maxDepth\"\n :permissions=\"permissions\"\n :active-path=\"activePath\"\n :expanded-keys=\"expandedKeys\"\n :favorites=\"favorites\"\n :on-favorite-toggle=\"onFavoriteToggle\"\n :is-favorite=\"isFavorite\"\n :styletype=\"styletype\"\n :disable-navigation=\"disableNavigation\"\n :active-key=\"activeKey\"\n @menu-click=\"emit('menuClick', $event)\"\n @expand-change=\"(menuKey, expanded) => emit('expandChange', menuKey, expanded)\"\n />\n </div>\n </div>\n</template>\n"],"names":["props","__props","emit","__emit","router","useRouter","checkPermission","computed","hasMenuPermission","isActive","isFolder","isExpanded","key","isDisabled","indentStyle","handleMenuClick","event","newExpanded","STYLE_PRESETS","preset","ChevronIcon","childrenCount","countChildren","items","count","item","_createElementBlock","_normalizeClass","_unref","cn","_createElementVNode","_createBlock","JIcon","_openBlock","_hoisted_1","_toDisplayString","_cache","_withModifiers","$event","_createVNode","_hoisted_2","_Fragment","child","index","_component_JDynamicMenuItem","menuKey","expanded"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { defineComponent as M, computed as l, resolveComponent as I, createElementBlock as m, openBlock as s, normalizeClass as u, unref as v, createElementVNode as z, createCommentVNode as y, normalizeStyle as B, createBlock as C, toDisplayString as P, withModifiers as L, createVNode as J, Fragment as R, renderList as V } from "vue";
1
+ import { defineComponent as P, computed as n, resolveComponent as I, createElementBlock as m, openBlock as l, normalizeClass as u, unref as v, createElementVNode as F, createCommentVNode as y, normalizeStyle as B, createBlock as C, toDisplayString as z, withModifiers as L, createVNode as R, Fragment as J, renderList as V } from "vue";
2
2
  import { useRouter as $ } from "vue-router";
3
3
  import b from "../../atoms/JIcon.vue.js";
4
4
  import { hasMenuPermission as Y, cn as f } from "../../../lib/utils.js";
@@ -7,8 +7,8 @@ const j = {
7
7
  class: "w-4 flex-shrink-0"
8
8
  }, q = {
9
9
  key: 0,
10
- class: "w-full"
11
- }, U = /* @__PURE__ */ M({
10
+ class: "border-l border-border/60 ml-[14px] pl-[6px]"
11
+ }, U = /* @__PURE__ */ P({
12
12
  __name: "JDynamicMenuItem",
13
13
  props: {
14
14
  item: {},
@@ -26,31 +26,31 @@ const j = {
26
26
  activeKey: { default: null }
27
27
  },
28
28
  emits: ["menuClick", "expandChange"],
29
- setup(e, { emit: p }) {
30
- const t = e, c = p, S = $(), w = l(() => Y(t.item.menuKey, t.permissions)), h = l(() => t.activeKey !== void 0 && t.activeKey !== null ? t.item.menuKey === t.activeKey : !t.item.path || !t.activePath ? !1 : t.activePath === t.item.path), o = l(() => t.item.menuType === "F" || Array.isArray(t.item.children) && t.item.children.length > 0), g = l(() => {
31
- if (!o.value) return !1;
32
- const n = t.item.menuKey || t.item.label;
33
- return t.expandedKeys?.has(n) ?? !1;
34
- }), x = l(() => t.item.disabled || !w.value), D = l(() => ({ paddingLeft: `${8 + (t.level || 0) * 12}px` })), A = (n) => {
35
- if (x.value) {
36
- n.preventDefault();
29
+ setup(e, { emit: S }) {
30
+ const t = e, c = S, w = $(), D = n(() => Y(t.item.menuKey, t.permissions)), h = n(() => t.activeKey !== void 0 && t.activeKey !== null ? t.item.menuKey === t.activeKey : !t.item.path || !t.activePath ? !1 : t.activePath === t.item.path), r = n(() => t.item.menuType === "F" || Array.isArray(t.item.children) && t.item.children.length > 0), x = n(() => {
31
+ if (!r.value) return !1;
32
+ const a = t.item.menuKey || t.item.label;
33
+ return t.expandedKeys?.has(a) ?? !1;
34
+ }), g = n(() => t.item.disabled || !D.value), A = n(() => ({ paddingLeft: "8px", paddingRight: "8px" })), E = (a) => {
35
+ if (g.value) {
36
+ a.preventDefault();
37
37
  return;
38
38
  }
39
- if (o.value) {
40
- const i = t.item.menuKey || t.item.label, r = !g.value;
41
- c("expandChange", i, r), c("menuClick", {
39
+ if (r.value) {
40
+ const i = t.item.menuKey || t.item.label, o = !x.value;
41
+ c("expandChange", i, o), c("menuClick", {
42
42
  menuItem: t.item,
43
43
  path: [t.item],
44
- event: n
44
+ event: a
45
45
  });
46
46
  } else
47
- !t.disableNavigation && t.item.path && S.push(t.item.path), c("menuClick", {
47
+ !t.disableNavigation && t.item.path && w.push(t.item.path), c("menuClick", {
48
48
  menuItem: t.item,
49
49
  path: [t.item],
50
50
  // 단순화된 경로 (필요시 부모 경로 포함하도록 확장 가능)
51
- event: n
51
+ event: a
52
52
  });
53
- }, K = {
53
+ }, p = {
54
54
  default: {
55
55
  itemClass: "flex items-center gap-1.5 py-1 rounded-md cursor-pointer transition-colors group",
56
56
  labelClass: "flex-1 truncate text-xs",
@@ -62,80 +62,80 @@ const j = {
62
62
  iconSize: "sm"
63
63
  // JIcon은 'xs'를 지원하지 않으므로 'sm' 사용
64
64
  }
65
- }, d = l(() => K[t.styletype] ?? K.default), E = l(() => g.value ? "chevronDown" : "chevronRight"), F = l(() => {
66
- if (!o.value || !Array.isArray(t.item.children))
65
+ }, d = n(() => p[t.styletype] ?? p.default), N = n(() => x.value ? "chevronDown" : "chevronRight"), K = n(() => {
66
+ if (!r.value || !Array.isArray(t.item.children))
67
67
  return 0;
68
- const n = (i) => {
69
- let r = 0;
70
- for (const a of i)
71
- r++, Array.isArray(a.children) && a.children.length > 0 && (r += n(a.children));
72
- return r;
68
+ const a = (i) => {
69
+ let o = 0;
70
+ for (const s of i)
71
+ o++, Array.isArray(s.children) && s.children.length > 0 && (o += a(s.children));
72
+ return o;
73
73
  };
74
- return n(t.item.children);
74
+ return a(t.item.children);
75
75
  });
76
- return (n, i) => {
77
- const r = I("JDynamicMenuItem", !0);
78
- return s(), m("div", {
76
+ return (a, i) => {
77
+ const o = I("JDynamicMenuItem", !0);
78
+ return l(), m("div", {
79
79
  class: u(v(f)("w-full", e.className))
80
80
  }, [
81
- z("div", {
81
+ F("div", {
82
82
  class: u(v(f)(
83
83
  d.value.itemClass,
84
84
  {
85
- "bg-accent text-accent-foreground": h.value,
86
- "hover:bg-accent/50": !x.value && !h.value,
87
- "opacity-50 cursor-not-allowed": x.value,
85
+ "bg-primary/5 text-primary border-l-2 border-primary shadow-sm": h.value,
86
+ "hover:bg-accent/50": !g.value && !h.value,
87
+ "opacity-50 cursor-not-allowed": g.value,
88
88
  "font-medium": h.value,
89
- "font-semibold": o.value
89
+ "font-semibold": r.value
90
90
  // 폴더인 경우 볼드체
91
91
  }
92
92
  )),
93
- style: B(D.value),
94
- onClick: A
93
+ style: B(A.value),
94
+ onClick: E
95
95
  }, [
96
- o.value ? (s(), C(b, {
96
+ r.value ? (l(), C(b, {
97
97
  key: 0,
98
- name: E.value,
98
+ name: N.value,
99
99
  size: d.value.iconSize,
100
100
  class: "flex-shrink-0 opacity-60",
101
101
  style: { "stroke-width": "1.5" }
102
- }, null, 8, ["name", "size"])) : (s(), m("span", j)),
103
- e.item.icon && !o.value ? (s(), C(b, {
102
+ }, null, 8, ["name", "size"])) : (l(), m("span", j)),
103
+ e.item.icon && !r.value ? (l(), C(b, {
104
104
  key: 2,
105
105
  name: e.item.icon,
106
106
  size: d.value.iconSize,
107
107
  class: "flex-shrink-0"
108
108
  }, null, 8, ["name", "size"])) : y("", !0),
109
- z("span", {
109
+ F("span", {
110
110
  class: u(d.value.labelClass)
111
- }, P(e.item.label), 3),
112
- o.value && F.value > 0 ? (s(), m("span", {
111
+ }, z(e.item.label), 3),
112
+ r.value && K.value > 0 ? (l(), m("span", {
113
113
  key: 3,
114
114
  class: u(v(f)(
115
115
  "text-muted-foreground ml-1 flex-shrink-0",
116
116
  t.styletype === "minimal" ? "text-[10px]" : "text-xs"
117
117
  ))
118
- }, " (" + P(F.value) + ") ", 3)) : y("", !0),
119
- e.item.menuKey && e.item.menuType === "L" && e.onFavoriteToggle ? (s(), m("button", {
118
+ }, " (" + z(K.value) + ") ", 3)) : y("", !0),
119
+ e.item.menuKey && e.item.menuType === "L" && e.onFavoriteToggle ? (l(), m("button", {
120
120
  key: 4,
121
121
  class: u(v(f)(
122
122
  "opacity-0 group-hover:opacity-100 transition-opacity hover:bg-accent rounded flex-shrink-0",
123
123
  t.styletype === "minimal" ? "p-0.5" : "p-1",
124
124
  e.isFavorite && e.isFavorite(e.item.menuKey) && "opacity-100"
125
125
  )),
126
- onClick: i[0] || (i[0] = L((a) => e.onFavoriteToggle(e.item.menuKey), ["stop"]))
126
+ onClick: i[0] || (i[0] = L((s) => e.onFavoriteToggle(e.item.menuKey), ["stop"]))
127
127
  }, [
128
- J(b, {
128
+ R(b, {
129
129
  name: (e.isFavorite && e.isFavorite(e.item.menuKey), "star"),
130
130
  size: d.value.iconSize,
131
131
  class: u(e.isFavorite && e.isFavorite(e.item.menuKey) ? "text-yellow-500 fill-yellow-500" : "text-muted-foreground")
132
132
  }, null, 8, ["name", "size", "class"])
133
133
  ], 2)) : y("", !0)
134
134
  ], 6),
135
- o.value && g.value && e.item.children && Array.isArray(e.item.children) && e.item.children.length > 0 && e.level + 1 < e.maxDepth ? (s(), m("div", q, [
136
- (s(!0), m(R, null, V(e.item.children, (a, N) => (s(), C(r, {
137
- key: a.menuKey || a.label || N,
138
- item: a,
135
+ r.value && x.value && e.item.children && Array.isArray(e.item.children) && e.item.children.length > 0 && e.level + 1 < e.maxDepth ? (l(), m("div", q, [
136
+ (l(!0), m(J, null, V(e.item.children, (s, T) => (l(), C(o, {
137
+ key: s.menuKey || s.label || T,
138
+ item: s,
139
139
  level: e.level + 1,
140
140
  "max-depth": e.maxDepth,
141
141
  permissions: e.permissions,
@@ -148,7 +148,7 @@ const j = {
148
148
  "disable-navigation": e.disableNavigation,
149
149
  "active-key": e.activeKey,
150
150
  onMenuClick: i[1] || (i[1] = (k) => c("menuClick", k)),
151
- onExpandChange: i[2] || (i[2] = (k, T) => c("expandChange", k, T))
151
+ onExpandChange: i[2] || (i[2] = (k, M) => c("expandChange", k, M))
152
152
  }, null, 8, ["item", "level", "max-depth", "permissions", "active-path", "expanded-keys", "favorites", "on-favorite-toggle", "is-favorite", "styletype", "disable-navigation", "active-key"]))), 128))
153
153
  ])) : y("", !0)
154
154
  ], 2);