@j-solution/components 1.7.0 → 1.9.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 (207) hide show
  1. package/README.md +413 -415
  2. package/assets/jwms-portal-frontend-Ct2Tc7yj.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 +443 -443
  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 +143 -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/JSidebar/JSidebar.vue.cjs +2 -0
  127. package/components/organisms/JSidebar/JSidebar.vue.cjs.map +1 -0
  128. package/components/organisms/JSidebar/JSidebar.vue.js +189 -0
  129. package/components/organisms/JSidebar/JSidebar.vue.js.map +1 -0
  130. package/components/{molecules/JFormField.vue3.cjs → organisms/JSidebar/JSidebar.vue2.cjs} +2 -2
  131. package/components/organisms/JSidebar/JSidebar.vue2.cjs.map +1 -0
  132. package/components/organisms/JSidebar/JSidebar.vue2.js +5 -0
  133. package/components/organisms/JSidebar/JSidebar.vue2.js.map +1 -0
  134. package/components/organisms/JSidebar/JSidebarGroup.vue.cjs +2 -0
  135. package/components/organisms/JSidebar/JSidebarGroup.vue.cjs.map +1 -0
  136. package/components/organisms/JSidebar/JSidebarGroup.vue.js +89 -0
  137. package/components/organisms/JSidebar/JSidebarGroup.vue.js.map +1 -0
  138. package/components/organisms/JSidebar/JSidebarGroup.vue2.cjs +2 -0
  139. package/components/organisms/JSidebar/JSidebarGroup.vue2.cjs.map +1 -0
  140. package/components/organisms/JSidebar/JSidebarGroup.vue2.js +5 -0
  141. package/components/organisms/JSidebar/JSidebarGroup.vue2.js.map +1 -0
  142. package/components/organisms/JSidebar/JSidebarItem.vue.cjs +2 -0
  143. package/components/organisms/JSidebar/JSidebarItem.vue.cjs.map +1 -0
  144. package/components/organisms/JSidebar/JSidebarItem.vue.js +79 -0
  145. package/components/organisms/JSidebar/JSidebarItem.vue.js.map +1 -0
  146. package/components/organisms/JSidebar/JSidebarItem.vue2.cjs +2 -0
  147. package/components/organisms/JSidebar/JSidebarItem.vue2.cjs.map +1 -0
  148. package/components/organisms/JSidebar/JSidebarItem.vue2.js +5 -0
  149. package/components/organisms/JSidebar/JSidebarItem.vue2.js.map +1 -0
  150. package/components/organisms/JSidebarSimple/JDynamicMenuItem.vue.cjs +1 -1
  151. package/components/organisms/JSidebarSimple/JDynamicMenuItem.vue.cjs.map +1 -1
  152. package/components/organisms/JSidebarSimple/JDynamicMenuItem.vue.js +52 -52
  153. package/components/organisms/JSidebarSimple/JDynamicMenuItem.vue.js.map +1 -1
  154. package/components/shadcn/Card.vue.cjs +1 -1
  155. package/components/shadcn/Card.vue.cjs.map +1 -1
  156. package/components/shadcn/Card.vue.js +1 -1
  157. package/components/shadcn/Card.vue.js.map +1 -1
  158. package/components/shadcn/CardContent.vue.cjs +1 -1
  159. package/components/shadcn/CardContent.vue.cjs.map +1 -1
  160. package/components/shadcn/CardContent.vue.js +4 -4
  161. package/components/shadcn/CardContent.vue.js.map +1 -1
  162. package/components/shadcn/CardHeader.vue.cjs +1 -1
  163. package/components/shadcn/CardHeader.vue.cjs.map +1 -1
  164. package/components/shadcn/CardHeader.vue.js +5 -5
  165. package/components/shadcn/CardHeader.vue.js.map +1 -1
  166. package/components/shadcn/Input.vue.cjs +1 -1
  167. package/components/shadcn/Input.vue.cjs.map +1 -1
  168. package/components/shadcn/Input.vue.js +3 -3
  169. package/components/shadcn/Input.vue.js.map +1 -1
  170. package/components/shadcn/SelectTrigger.vue.cjs +1 -1
  171. package/components/shadcn/SelectTrigger.vue.cjs.map +1 -1
  172. package/components/shadcn/SelectTrigger.vue.js +1 -1
  173. package/components/shadcn/SelectTrigger.vue.js.map +1 -1
  174. package/components/shadcn/TabsContent.vue.cjs +1 -1
  175. package/components/shadcn/TabsContent.vue.cjs.map +1 -1
  176. package/components/shadcn/TabsContent.vue.js +1 -1
  177. package/components/shadcn/TabsContent.vue.js.map +1 -1
  178. package/components/shadcn/TabsList.vue.cjs +1 -1
  179. package/components/shadcn/TabsList.vue.cjs.map +1 -1
  180. package/components/shadcn/TabsList.vue.js +10 -10
  181. package/components/shadcn/TabsList.vue.js.map +1 -1
  182. package/components/shadcn/Textarea.vue.cjs +1 -1
  183. package/components/shadcn/Textarea.vue.cjs.map +1 -1
  184. package/components/shadcn/Textarea.vue.js +1 -1
  185. package/components/shadcn/Textarea.vue.js.map +1 -1
  186. package/components/shadcn/index.cjs +1 -1
  187. package/components/shadcn/index.cjs.map +1 -1
  188. package/components/shadcn/index.js +4 -4
  189. package/components/shadcn/index.js.map +1 -1
  190. package/components/templates/JLayout.vue.cjs.map +1 -1
  191. package/components/templates/JLayout.vue.js.map +1 -1
  192. package/components/templates/JLayoutSimple.vue.cjs +1 -1
  193. package/components/templates/JLayoutSimple.vue.cjs.map +1 -1
  194. package/components/templates/JLayoutSimple.vue.js +36 -30
  195. package/components/templates/JLayoutSimple.vue.js.map +1 -1
  196. package/index.cjs +1 -1
  197. package/index.js +75 -67
  198. package/package.json +1 -1
  199. package/types/index.d.ts +662 -461
  200. package/types/sidebar.types.cjs +2 -0
  201. package/types/sidebar.types.cjs.map +1 -0
  202. package/types/sidebar.types.js +5 -0
  203. package/types/sidebar.types.js.map +1 -0
  204. package/assets/jwms-portal-frontend-CwxPfHfa.css +0 -1
  205. package/components/molecules/JFormField.vue3.cjs.map +0 -1
  206. package/components/molecules/JFormField.vue3.js +0 -6
  207. 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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"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue"),C=require("../../../types/sidebar.types.cjs"),u=require("../../atoms/JIcon.vue.cjs"),S=require("../../atoms/JInput.vue.cjs"),F=require("./JSidebarGroup.vue.cjs"),h=require("./JSidebarItem.vue.cjs"),x=require("../../../lib/utils.cjs"),L={key:0,class:"p-2 flex-shrink-0"},I={class:"relative"},q={class:"flex-1 overflow-y-auto overflow-x-hidden px-1 py-1"},z={class:"text-[10px] text-yellow-500/60"},J={class:"overflow-hidden"},K={class:"flex justify-center py-1"},M={key:1,class:"text-center py-6 text-muted-foreground text-xs"},P={class:"flex-shrink-0 border-t border-border p-1"},O={key:0},T=e.defineComponent({__name:"JSidebar",props:{items:{},collapsed:{type:Boolean,default:!1},activePath:{},width:{default:"220px"},collapsedWidth:{default:"56px"},storageKey:{},showSearch:{type:Boolean,default:!0},showFavorites:{type:Boolean,default:!0}},emits:["update:collapsed","menu-click"],setup(n,{emit:_}){const r=n,k=_,a=e.ref(new Set(B()));function B(){if(!r.storageKey)return[];try{const t=localStorage.getItem(r.storageKey);return t?JSON.parse(t):[]}catch{return[]}}function w(){r.storageKey&&localStorage.setItem(r.storageKey,JSON.stringify([...a.value]))}function b(t){a.value.has(t)?a.value.delete(t):a.value.add(t),a.value=new Set(a.value),w()}const i=e.reactive({collapsed:r.collapsed,activePath:r.activePath,favorites:a.value,toggleFavorite:b});e.watch(()=>r.collapsed,t=>{i.collapsed=t}),e.watch(()=>r.activePath,t=>{i.activePath=t}),e.watch(a,t=>{i.favorites=t}),e.provide(C.SIDEBAR_INJECTION_KEY,i);const c=e.ref(""),y=e.computed(()=>{if(!r.storageKey||a.value.size===0)return[];const t=[],l=o=>{for(const s of o)s.menuType==="L"&&a.value.has(s.id)&&t.push(s),s.children&&l(s.children)};return l(r.items),t}),g=e.computed(()=>{const t=c.value.trim().toLowerCase();if(!t)return r.items;const l=o=>{const s=[];for(const p of o){const V=p.label.toLowerCase().includes(t),v=p.children?l(p.children):void 0;(V||v&&v.length>0)&&s.push({...p,children:v})}return s};return l(r.items)}),d=e.computed(()=>{const t=c.value.trim().toLowerCase();return t?y.value.filter(l=>l.label.toLowerCase().includes(t)):y.value}),m=e.ref(!0),f=(t,l)=>{k("menu-click",t,l)},E=()=>{k("update:collapsed",!r.collapsed)},N=e.computed(()=>r.collapsed?r.collapsedWidth:r.width);return(t,l)=>(e.openBlock(),e.createElementBlock("aside",{class:"h-full bg-background border-r border-border flex flex-col flex-shrink-0 overflow-hidden transition-[width] duration-200 ease-out",style:e.normalizeStyle({width:N.value})},[!n.collapsed&&n.showSearch?(e.openBlock(),e.createElementBlock("div",L,[e.createElementVNode("div",I,[e.createVNode(u.default,{name:"search",size:"sm",class:"absolute left-2 top-1/2 -translate-y-1/2 text-muted-foreground"}),e.createVNode(S.default,{modelValue:c.value,"onUpdate:modelValue":l[0]||(l[0]=o=>c.value=o),placeholder:"메뉴 검색...",class:"pl-8 h-7 text-xs"},null,8,["modelValue"])])])):e.createCommentVNode("",!0),e.createElementVNode("nav",q,[n.showFavorites&&n.storageKey&&d.value.length>0?(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[n.collapsed?(e.openBlock(),e.createElementBlock(e.Fragment,{key:1},[e.createElementVNode("div",K,[e.createVNode(u.default,{name:"star",size:"sm",class:"text-yellow-500"})]),(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(d.value,o=>(e.openBlock(),e.createBlock(h.default,{key:"fav-c-"+o.id,item:o,onMenuClick:f},null,8,["item"]))),128))],64)):(e.openBlock(),e.createElementBlock(e.Fragment,{key:0},[e.createElementVNode("button",{class:"flex items-center gap-1.5 w-full px-2 py-1.5 text-xs font-semibold text-yellow-600 cursor-pointer select-none transition-colors hover:text-yellow-700",onClick:l[1]||(l[1]=o=>m.value=!m.value)},[e.createVNode(u.default,{name:"chevronRight",size:"sm",class:e.normalizeClass(e.unref(x.cn)("flex-shrink-0 transition-transform duration-200",m.value&&"rotate-90"))},null,8,["class"]),e.createVNode(u.default,{name:"star",size:"sm",class:"flex-shrink-0 text-yellow-500"}),l[2]||(l[2]=e.createElementVNode("span",{class:"flex-1 text-left"},"즐겨찾기",-1)),e.createElementVNode("span",z,e.toDisplayString(d.value.length),1)]),e.createElementVNode("div",{class:e.normalizeClass(e.unref(x.cn)("grid transition-[grid-template-rows] duration-200 ease-out",m.value?"grid-rows-[1fr]":"grid-rows-[0fr]"))},[e.createElementVNode("div",J,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(d.value,o=>(e.openBlock(),e.createBlock(h.default,{key:"fav-"+o.id,item:o,onMenuClick:f},null,8,["item"]))),128))])],2)],64)),l[3]||(l[3]=e.createElementVNode("div",{class:"h-px bg-border mx-2 my-1"},null,-1))],64)):e.createCommentVNode("",!0),(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(g.value,o=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:o.id},[o.menuType==="F"?(e.openBlock(),e.createBlock(F.default,{key:0,item:o,onMenuClick:f},null,8,["item"])):o.menuType==="L"?(e.openBlock(),e.createBlock(h.default,{key:1,item:o,onMenuClick:f},null,8,["item"])):e.createCommentVNode("",!0)],64))),128)),g.value.length===0&&c.value.trim()?(e.openBlock(),e.createElementBlock("div",M," 검색 결과가 없습니다. ")):e.createCommentVNode("",!0)]),e.createElementVNode("div",P,[e.createElementVNode("button",{class:"flex items-center gap-2 w-full px-2 py-1 rounded-md text-xs text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors",onClick:E},[e.createVNode(u.default,{name:n.collapsed?"panelLeftOpen":"panelLeftClose",size:"sm"},null,8,["name"]),n.collapsed?e.createCommentVNode("",!0):(e.openBlock(),e.createElementBlock("span",O,"접기"))])])],4))}});exports.default=T;
2
+ //# sourceMappingURL=JSidebar.vue.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JSidebar.vue.cjs","sources":["../../../../../src/components/organisms/JSidebar/JSidebar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, provide, watch, reactive } from 'vue'\nimport type { SidebarMenuItem, SidebarState } from '@/types/sidebar.types'\nimport { SIDEBAR_INJECTION_KEY } from '@/types/sidebar.types'\nimport JIcon from '@/components/atoms/JIcon.vue'\nimport JInput from '@/components/atoms/JInput.vue'\nimport JSidebarGroup from './JSidebarGroup.vue'\nimport JSidebarItem from './JSidebarItem.vue'\nimport { cn } from '@/lib/utils'\n\n/**\n * JSidebar - 통합 사이드바 컴포넌트\n *\n * DB 트리 데이터 기반, provide/inject로 상태 공유.\n * collapsed 모드, 검색, 즐겨찾기(localStorage) 지원.\n * 라우터를 모름 — @menu-click으로 아이템을 올려보냄.\n */\n\nconst props = withDefaults(\n defineProps<{\n /** 메뉴 트리 데이터 */\n items: SidebarMenuItem[]\n /** 접힘 상태 (v-model:collapsed) */\n collapsed?: boolean\n /** 현재 활성 경로 */\n activePath?: string\n /** 펼침 너비 */\n width?: string\n /** 접힘 너비 */\n collapsedWidth?: string\n /** 즐겨찾기 localStorage 키 (없으면 즐겨찾기 비활성) */\n storageKey?: string\n /** 검색 표시 여부 */\n showSearch?: boolean\n /** 즐겨찾기 섹션 표시 여부 */\n showFavorites?: boolean\n }>(),\n {\n collapsed: false,\n width: '220px',\n collapsedWidth: '56px',\n showSearch: true,\n showFavorites: true,\n },\n)\n\nconst emit = defineEmits<{\n 'update:collapsed': [value: boolean]\n 'menu-click': [item: SidebarMenuItem, event: MouseEvent]\n}>()\n\n// ── 즐겨찾기 (localStorage) ──\nconst favorites = ref<Set<string>>(new Set(loadFavorites()))\n\nfunction loadFavorites(): string[] {\n if (!props.storageKey) return []\n try {\n const raw = localStorage.getItem(props.storageKey)\n return raw ? JSON.parse(raw) : []\n } catch {\n return []\n }\n}\n\nfunction saveFavorites() {\n if (!props.storageKey) return\n localStorage.setItem(props.storageKey, JSON.stringify([...favorites.value]))\n}\n\nfunction toggleFavorite(id: string) {\n if (favorites.value.has(id)) {\n favorites.value.delete(id)\n } else {\n favorites.value.add(id)\n }\n favorites.value = new Set(favorites.value) // trigger reactivity\n saveFavorites()\n}\n\n// ── provide/inject 상태 ──\nconst sidebarState = reactive<SidebarState>({\n collapsed: props.collapsed,\n activePath: props.activePath,\n favorites: favorites.value,\n toggleFavorite,\n})\n\n// props 변경 시 state 동기화\nwatch(() => props.collapsed, (v) => { sidebarState.collapsed = v })\nwatch(() => props.activePath, (v) => { sidebarState.activePath = v })\nwatch(favorites, (v) => { sidebarState.favorites = v })\n\nprovide(SIDEBAR_INJECTION_KEY, sidebarState)\n\n// ── 검색 ──\nconst searchQuery = ref('')\n\n/** 즐겨찾기 아이템 (L 타입만, 트리 평탄화) */\nconst favoriteItems = computed(() => {\n if (!props.storageKey || favorites.value.size === 0) return []\n const result: SidebarMenuItem[] = []\n const flatten = (items: SidebarMenuItem[]) => {\n for (const item of items) {\n if (item.menuType === 'L' && favorites.value.has(item.id)) {\n result.push(item)\n }\n if (item.children) flatten(item.children)\n }\n }\n flatten(props.items)\n return result\n})\n\n/** 검색 필터링 (재귀) */\nconst filteredItems = computed(() => {\n const q = searchQuery.value.trim().toLowerCase()\n if (!q) return props.items\n\n const filter = (items: SidebarMenuItem[]): SidebarMenuItem[] => {\n const result: SidebarMenuItem[] = []\n for (const item of items) {\n const matchLabel = item.label.toLowerCase().includes(q)\n const filteredChildren = item.children ? filter(item.children) : undefined\n if (matchLabel || (filteredChildren && filteredChildren.length > 0)) {\n result.push({ ...item, children: filteredChildren })\n }\n }\n return result\n }\n return filter(props.items)\n})\n\n/** 즐겨찾기 검색 필터링 */\nconst filteredFavorites = computed(() => {\n const q = searchQuery.value.trim().toLowerCase()\n if (!q) return favoriteItems.value\n return favoriteItems.value.filter(item => item.label.toLowerCase().includes(q))\n})\n\n// ── 즐겨찾기 그룹 펼침 ──\nconst favoritesExpanded = ref(true)\n\n// ── 이벤트 핸들러 ──\nconst handleMenuClick = (item: SidebarMenuItem, event: MouseEvent) => {\n emit('menu-click', item, event)\n}\n\nconst toggleCollapsed = () => {\n emit('update:collapsed', !props.collapsed)\n}\n\n// ── 사이드바 너비 ──\nconst sidebarWidth = computed(() => props.collapsed ? props.collapsedWidth : props.width)\n</script>\n\n<template>\n <aside\n class=\"h-full bg-background border-r border-border flex flex-col flex-shrink-0 overflow-hidden transition-[width] duration-200 ease-out\"\n :style=\"{ width: sidebarWidth }\"\n >\n <!-- 검색바 (펼침 + showSearch) -->\n <div v-if=\"!collapsed && showSearch\" class=\"p-2 flex-shrink-0\">\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=\"pl-8 h-7 text-xs\"\n />\n </div>\n </div>\n\n <!-- 메뉴 영역 -->\n <nav class=\"flex-1 overflow-y-auto overflow-x-hidden px-1 py-1\">\n <!-- 즐겨찾기 섹션 -->\n <template v-if=\"showFavorites && storageKey && filteredFavorites.length > 0\">\n <!-- 펼침: 즐겨찾기 그룹 -->\n <template v-if=\"!collapsed\">\n <button\n class=\"flex items-center gap-1.5 w-full px-2 py-1.5 text-xs font-semibold text-yellow-600 cursor-pointer select-none transition-colors hover:text-yellow-700\"\n @click=\"favoritesExpanded = !favoritesExpanded\"\n >\n <JIcon\n name=\"chevronRight\"\n size=\"sm\"\n :class=\"cn(\n 'flex-shrink-0 transition-transform duration-200',\n favoritesExpanded && 'rotate-90'\n )\"\n />\n <JIcon name=\"star\" size=\"sm\" class=\"flex-shrink-0 text-yellow-500\" />\n <span class=\"flex-1 text-left\">즐겨찾기</span>\n <span class=\"text-[10px] text-yellow-500/60\">{{ filteredFavorites.length }}</span>\n </button>\n <div\n :class=\"cn(\n 'grid transition-[grid-template-rows] duration-200 ease-out',\n favoritesExpanded ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'\n )\"\n >\n <div class=\"overflow-hidden\">\n <JSidebarItem\n v-for=\"fav in filteredFavorites\"\n :key=\"'fav-' + fav.id\"\n :item=\"fav\"\n @menu-click=\"handleMenuClick\"\n />\n </div>\n </div>\n </template>\n <!-- 접힘: 별 아이콘 + 구분선 -->\n <template v-else>\n <div class=\"flex justify-center py-1\">\n <JIcon name=\"star\" size=\"sm\" class=\"text-yellow-500\" />\n </div>\n <JSidebarItem\n v-for=\"fav in filteredFavorites\"\n :key=\"'fav-c-' + fav.id\"\n :item=\"fav\"\n @menu-click=\"handleMenuClick\"\n />\n </template>\n <div class=\"h-px bg-border mx-2 my-1\" />\n </template>\n\n <!-- 메인 메뉴 -->\n <template v-for=\"item in filteredItems\" :key=\"item.id\">\n <JSidebarGroup\n v-if=\"item.menuType === 'F'\"\n :item=\"item\"\n @menu-click=\"handleMenuClick\"\n />\n <JSidebarItem\n v-else-if=\"item.menuType === 'L'\"\n :item=\"item\"\n @menu-click=\"handleMenuClick\"\n />\n </template>\n\n <!-- 검색 결과 없음 -->\n <div\n v-if=\"filteredItems.length === 0 && searchQuery.trim()\"\n class=\"text-center py-6 text-muted-foreground text-xs\"\n >\n 검색 결과가 없습니다.\n </div>\n </nav>\n\n <!-- 하단: collapse 토글 -->\n <div class=\"flex-shrink-0 border-t border-border p-1\">\n <button\n class=\"flex items-center gap-2 w-full px-2 py-1 rounded-md text-xs text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors\"\n @click=\"toggleCollapsed\"\n >\n <JIcon\n :name=\"collapsed ? 'panelLeftOpen' : 'panelLeftClose'\"\n size=\"sm\"\n />\n <span v-if=\"!collapsed\">접기</span>\n </button>\n </div>\n </aside>\n</template>\n"],"names":["props","__props","emit","__emit","favorites","ref","loadFavorites","raw","saveFavorites","toggleFavorite","id","sidebarState","reactive","watch","v","provide","SIDEBAR_INJECTION_KEY","searchQuery","favoriteItems","computed","result","flatten","items","item","filteredItems","q","filter","matchLabel","filteredChildren","filteredFavorites","favoritesExpanded","handleMenuClick","event","toggleCollapsed","sidebarWidth","_createElementBlock","_openBlock","_hoisted_1","_createElementVNode","_hoisted_2","_createVNode","JIcon","JInput","$event","_hoisted_3","_Fragment","_hoisted_6","_renderList","fav","_createBlock","JSidebarItem","_cache","_unref","cn","_hoisted_4","_toDisplayString","_hoisted_5","JSidebarGroup","_hoisted_7","_hoisted_8"],"mappings":"wgCAkBA,MAAMA,EAAQC,EA4BRC,EAAOC,EAMPC,EAAYC,EAAAA,IAAiB,IAAI,IAAIC,EAAA,CAAe,CAAC,EAE3D,SAASA,GAA0B,CACjC,GAAI,CAACN,EAAM,WAAY,MAAO,CAAA,EAC9B,GAAI,CACF,MAAMO,EAAM,aAAa,QAAQP,EAAM,UAAU,EACjD,OAAOO,EAAM,KAAK,MAAMA,CAAG,EAAI,CAAA,CACjC,MAAQ,CACN,MAAO,CAAA,CACT,CACF,CAEA,SAASC,GAAgB,CAClBR,EAAM,YACX,aAAa,QAAQA,EAAM,WAAY,KAAK,UAAU,CAAC,GAAGI,EAAU,KAAK,CAAC,CAAC,CAC7E,CAEA,SAASK,EAAeC,EAAY,CAC9BN,EAAU,MAAM,IAAIM,CAAE,EACxBN,EAAU,MAAM,OAAOM,CAAE,EAEzBN,EAAU,MAAM,IAAIM,CAAE,EAExBN,EAAU,MAAQ,IAAI,IAAIA,EAAU,KAAK,EACzCI,EAAA,CACF,CAGA,MAAMG,EAAeC,EAAAA,SAAuB,CAC1C,UAAWZ,EAAM,UACjB,WAAYA,EAAM,WAClB,UAAWI,EAAU,MACrB,eAAAK,CAAA,CACD,EAGDI,EAAAA,MAAM,IAAMb,EAAM,UAAYc,GAAM,CAAEH,EAAa,UAAYG,CAAE,CAAC,EAClED,EAAAA,MAAM,IAAMb,EAAM,WAAac,GAAM,CAAEH,EAAa,WAAaG,CAAE,CAAC,EACpED,QAAMT,EAAYU,GAAM,CAAEH,EAAa,UAAYG,CAAE,CAAC,EAEtDC,EAAAA,QAAQC,EAAAA,sBAAuBL,CAAY,EAG3C,MAAMM,EAAcZ,EAAAA,IAAI,EAAE,EAGpBa,EAAgBC,EAAAA,SAAS,IAAM,CACnC,GAAI,CAACnB,EAAM,YAAcI,EAAU,MAAM,OAAS,QAAU,CAAA,EAC5D,MAAMgB,EAA4B,CAAA,EAC5BC,EAAWC,GAA6B,CAC5C,UAAWC,KAAQD,EACbC,EAAK,WAAa,KAAOnB,EAAU,MAAM,IAAImB,EAAK,EAAE,GACtDH,EAAO,KAAKG,CAAI,EAEdA,EAAK,UAAUF,EAAQE,EAAK,QAAQ,CAE5C,EACA,OAAAF,EAAQrB,EAAM,KAAK,EACZoB,CACT,CAAC,EAGKI,EAAgBL,EAAAA,SAAS,IAAM,CACnC,MAAMM,EAAIR,EAAY,MAAM,KAAA,EAAO,YAAA,EACnC,GAAI,CAACQ,EAAG,OAAOzB,EAAM,MAErB,MAAM0B,EAAUJ,GAAgD,CAC9D,MAAMF,EAA4B,CAAA,EAClC,UAAWG,KAAQD,EAAO,CACxB,MAAMK,EAAaJ,EAAK,MAAM,YAAA,EAAc,SAASE,CAAC,EAChDG,EAAmBL,EAAK,SAAWG,EAAOH,EAAK,QAAQ,EAAI,QAC7DI,GAAeC,GAAoBA,EAAiB,OAAS,IAC/DR,EAAO,KAAK,CAAE,GAAGG,EAAM,SAAUK,EAAkB,CAEvD,CACA,OAAOR,CACT,EACA,OAAOM,EAAO1B,EAAM,KAAK,CAC3B,CAAC,EAGK6B,EAAoBV,EAAAA,SAAS,IAAM,CACvC,MAAMM,EAAIR,EAAY,MAAM,KAAA,EAAO,YAAA,EACnC,OAAKQ,EACEP,EAAc,MAAM,OAAOK,GAAQA,EAAK,MAAM,YAAA,EAAc,SAASE,CAAC,CAAC,EAD/DP,EAAc,KAE/B,CAAC,EAGKY,EAAoBzB,EAAAA,IAAI,EAAI,EAG5B0B,EAAkB,CAACR,EAAuBS,IAAsB,CACpE9B,EAAK,aAAcqB,EAAMS,CAAK,CAChC,EAEMC,EAAkB,IAAM,CAC5B/B,EAAK,mBAAoB,CAACF,EAAM,SAAS,CAC3C,EAGMkC,EAAef,EAAAA,SAAS,IAAMnB,EAAM,UAAYA,EAAM,eAAiBA,EAAM,KAAK,8BAItFmC,EAAAA,mBA6GQ,QAAA,CA5GN,MAAM,mIACL,8BAAgBD,EAAA,MAAY,CAAA,GAGjB,CAAAjC,EAAA,WAAaA,EAAA,YAAzBmC,EAAAA,YAAAD,EAAAA,mBAaM,MAbNE,EAaM,CAZJC,EAAAA,mBAWM,MAXNC,EAWM,CAVJC,EAAAA,YAIEC,EAAAA,QAAA,CAHA,KAAK,SACL,KAAK,KACL,MAAM,gEAAA,GAERD,EAAAA,YAIEE,EAAAA,QAAA,YAHSzB,EAAA,2CAAAA,EAAW,MAAA0B,GACpB,YAAY,WACZ,MAAM,kBAAA,0DAMZL,EAAAA,mBAyEM,MAzENM,EAyEM,CAvEY3C,EAAA,eAAiBA,EAAA,YAAc4B,EAAA,MAAkB,OAAM,iBAAvEM,EAAAA,mBAgDWU,EAAAA,SAAA,CAAA,IAAA,GAAA,CA9CQ5C,EAAA,yBAkCjBkC,EAAAA,mBAUWU,EAAAA,SAAA,CAAA,IAAA,GAAA,CATTP,EAAAA,mBAEM,MAFNQ,EAEM,CADJN,EAAAA,YAAuDC,EAAAA,QAAA,CAAhD,KAAK,OAAO,KAAK,KAAK,MAAM,iBAAA,sBAErCN,EAAAA,mBAKEU,EAAAA,SAAA,KAAAE,EAAAA,WAJclB,EAAA,MAAPmB,kBADTC,EAAAA,YAKEC,UAAA,CAHC,IAAG,SAAaF,EAAI,GACpB,KAAMA,EACN,YAAYjB,CAAA,gDA1CjBI,EAAAA,mBAgCWU,WAAA,CAAA,IAAA,GAAA,CA/BTP,EAAAA,mBAeS,SAAA,CAdP,MAAM,wJACL,QAAKa,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAAR,GAAEb,EAAA,MAAiB,CAAIA,EAAA,MAAA,GAE7BU,EAAAA,YAOEC,EAAAA,QAAA,CANA,KAAK,eACL,KAAK,KACJ,uBAAOW,EAAAA,MAAAC,IAAA,oDAAuFvB,EAAA,OAAiB,WAAA,sBAKlHU,EAAAA,YAAqEC,EAAAA,QAAA,CAA9D,KAAK,OAAO,KAAK,KAAK,MAAM,+BAAA,GACnCU,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAAb,EAAAA,mBAA0C,OAAA,CAApC,MAAM,kBAAA,EAAmB,OAAI,EAAA,GACnCA,EAAAA,mBAAkF,OAAlFgB,EAAkFC,EAAAA,gBAAlC1B,EAAA,MAAkB,MAAM,EAAA,CAAA,CAAA,GAE1ES,EAAAA,mBAcM,MAAA,CAbH,uBAAOc,EAAAA,MAAAC,IAAA,+DAA8FvB,EAAA,MAAiB,kBAAA,iBAAA,KAKvHQ,EAAAA,mBAOM,MAPNkB,EAOM,kBANJrB,EAAAA,mBAKEU,EAAAA,SAAA,KAAAE,EAAAA,WAJclB,EAAA,MAAPmB,kBADTC,EAAAA,YAKEC,UAAA,CAHC,IAAG,OAAWF,EAAI,GAClB,KAAMA,EACN,YAAYjB,CAAA,mDAiBrBO,EAAAA,mBAAwC,MAAA,CAAnC,MAAM,4BAA0B,KAAA,EAAA,EAAA,oDAIvCH,EAAAA,mBAWWU,EAAAA,SAAA,KAAAE,EAAAA,WAXcvB,EAAA,MAARD,mDAA6B,IAAAA,EAAK,EAAA,GAEzCA,EAAK,WAAQ,mBADrB0B,EAAAA,YAIEQ,EAAAA,QAAA,OAFC,KAAAlC,EACA,YAAYQ,CAAA,oBAGFR,EAAK,WAAQ,mBAD1B0B,EAAAA,YAIEC,EAAAA,QAAA,OAFC,KAAA3B,EACA,YAAYQ,CAAA,6DAMTP,EAAA,MAAc,SAAM,GAAUP,EAAA,MAAY,KAAA,iBADlDkB,EAAAA,mBAKM,MALNuB,EAGC,gBAED,iCAIFpB,EAAAA,mBAWM,MAXNqB,EAWM,CAVJrB,EAAAA,mBASS,SAAA,CARP,MAAM,+IACL,QAAOL,CAAA,GAERO,EAAAA,YAGEC,EAAAA,QAAA,CAFC,KAAMxC,EAAA,UAAS,gBAAA,iBAChB,KAAK,IAAA,mBAEMA,EAAA,uCAAbmC,EAAAA,UAAA,EAAAD,EAAAA,mBAAiC,SAAT,IAAE"}
@@ -0,0 +1,189 @@
1
+ import { defineComponent as T, ref as C, reactive as W, watch as S, provide as q, computed as w, createElementBlock as r, openBlock as s, normalizeStyle as D, createCommentVNode as m, createElementVNode as a, createVNode as d, Fragment as u, normalizeClass as B, unref as I, toDisplayString as R, renderList as _, createBlock as k } from "vue";
2
+ import { SIDEBAR_INJECTION_KEY as j } from "../../../types/sidebar.types.js";
3
+ import v from "../../atoms/JIcon.vue.js";
4
+ import A from "../../atoms/JInput.vue.js";
5
+ import Q from "./JSidebarGroup.vue.js";
6
+ import L from "./JSidebarItem.vue.js";
7
+ import { cn as N } from "../../../lib/utils.js";
8
+ const U = {
9
+ key: 0,
10
+ class: "p-2 flex-shrink-0"
11
+ }, Y = { class: "relative" }, G = { class: "flex-1 overflow-y-auto overflow-x-hidden px-1 py-1" }, H = { class: "text-[10px] text-yellow-500/60" }, X = { class: "overflow-hidden" }, Z = { class: "flex justify-center py-1" }, ee = {
12
+ key: 1,
13
+ class: "text-center py-6 text-muted-foreground text-xs"
14
+ }, te = { class: "flex-shrink-0 border-t border-border p-1" }, le = { key: 0 }, ue = /* @__PURE__ */ T({
15
+ __name: "JSidebar",
16
+ props: {
17
+ items: {},
18
+ collapsed: { type: Boolean, default: !1 },
19
+ activePath: {},
20
+ width: { default: "220px" },
21
+ collapsedWidth: { default: "56px" },
22
+ storageKey: {},
23
+ showSearch: { type: Boolean, default: !0 },
24
+ showFavorites: { type: Boolean, default: !0 }
25
+ },
26
+ emits: ["update:collapsed", "menu-click"],
27
+ setup(i, { emit: E }) {
28
+ const o = i, z = E, n = C(new Set(V()));
29
+ function V() {
30
+ if (!o.storageKey) return [];
31
+ try {
32
+ const e = localStorage.getItem(o.storageKey);
33
+ return e ? JSON.parse(e) : [];
34
+ } catch {
35
+ return [];
36
+ }
37
+ }
38
+ function M() {
39
+ o.storageKey && localStorage.setItem(o.storageKey, JSON.stringify([...n.value]));
40
+ }
41
+ function P(e) {
42
+ n.value.has(e) ? n.value.delete(e) : n.value.add(e), n.value = new Set(n.value), M();
43
+ }
44
+ const p = W({
45
+ collapsed: o.collapsed,
46
+ activePath: o.activePath,
47
+ favorites: n.value,
48
+ toggleFavorite: P
49
+ });
50
+ S(() => o.collapsed, (e) => {
51
+ p.collapsed = e;
52
+ }), S(() => o.activePath, (e) => {
53
+ p.activePath = e;
54
+ }), S(n, (e) => {
55
+ p.favorites = e;
56
+ }), q(j, p);
57
+ const f = C(""), F = w(() => {
58
+ if (!o.storageKey || n.value.size === 0) return [];
59
+ const e = [], t = (l) => {
60
+ for (const c of l)
61
+ c.menuType === "L" && n.value.has(c.id) && e.push(c), c.children && t(c.children);
62
+ };
63
+ return t(o.items), e;
64
+ }), K = w(() => {
65
+ const e = f.value.trim().toLowerCase();
66
+ if (!e) return o.items;
67
+ const t = (l) => {
68
+ const c = [];
69
+ for (const g of l) {
70
+ const O = g.label.toLowerCase().includes(e), b = g.children ? t(g.children) : void 0;
71
+ (O || b && b.length > 0) && c.push({ ...g, children: b });
72
+ }
73
+ return c;
74
+ };
75
+ return t(o.items);
76
+ }), h = w(() => {
77
+ const e = f.value.trim().toLowerCase();
78
+ return e ? F.value.filter((t) => t.label.toLowerCase().includes(e)) : F.value;
79
+ }), x = C(!0), y = (e, t) => {
80
+ z("menu-click", e, t);
81
+ }, $ = () => {
82
+ z("update:collapsed", !o.collapsed);
83
+ }, J = w(() => o.collapsed ? o.collapsedWidth : o.width);
84
+ return (e, t) => (s(), r("aside", {
85
+ class: "h-full bg-background border-r border-border flex flex-col flex-shrink-0 overflow-hidden transition-[width] duration-200 ease-out",
86
+ style: D({ width: J.value })
87
+ }, [
88
+ !i.collapsed && i.showSearch ? (s(), r("div", U, [
89
+ a("div", Y, [
90
+ d(v, {
91
+ name: "search",
92
+ size: "sm",
93
+ class: "absolute left-2 top-1/2 -translate-y-1/2 text-muted-foreground"
94
+ }),
95
+ d(A, {
96
+ modelValue: f.value,
97
+ "onUpdate:modelValue": t[0] || (t[0] = (l) => f.value = l),
98
+ placeholder: "메뉴 검색...",
99
+ class: "pl-8 h-7 text-xs"
100
+ }, null, 8, ["modelValue"])
101
+ ])
102
+ ])) : m("", !0),
103
+ a("nav", G, [
104
+ i.showFavorites && i.storageKey && h.value.length > 0 ? (s(), r(u, { key: 0 }, [
105
+ i.collapsed ? (s(), r(u, { key: 1 }, [
106
+ a("div", Z, [
107
+ d(v, {
108
+ name: "star",
109
+ size: "sm",
110
+ class: "text-yellow-500"
111
+ })
112
+ ]),
113
+ (s(!0), r(u, null, _(h.value, (l) => (s(), k(L, {
114
+ key: "fav-c-" + l.id,
115
+ item: l,
116
+ onMenuClick: y
117
+ }, null, 8, ["item"]))), 128))
118
+ ], 64)) : (s(), r(u, { key: 0 }, [
119
+ a("button", {
120
+ class: "flex items-center gap-1.5 w-full px-2 py-1.5 text-xs font-semibold text-yellow-600 cursor-pointer select-none transition-colors hover:text-yellow-700",
121
+ onClick: t[1] || (t[1] = (l) => x.value = !x.value)
122
+ }, [
123
+ d(v, {
124
+ name: "chevronRight",
125
+ size: "sm",
126
+ class: B(I(N)(
127
+ "flex-shrink-0 transition-transform duration-200",
128
+ x.value && "rotate-90"
129
+ ))
130
+ }, null, 8, ["class"]),
131
+ d(v, {
132
+ name: "star",
133
+ size: "sm",
134
+ class: "flex-shrink-0 text-yellow-500"
135
+ }),
136
+ t[2] || (t[2] = a("span", { class: "flex-1 text-left" }, "즐겨찾기", -1)),
137
+ a("span", H, R(h.value.length), 1)
138
+ ]),
139
+ a("div", {
140
+ class: B(I(N)(
141
+ "grid transition-[grid-template-rows] duration-200 ease-out",
142
+ x.value ? "grid-rows-[1fr]" : "grid-rows-[0fr]"
143
+ ))
144
+ }, [
145
+ a("div", X, [
146
+ (s(!0), r(u, null, _(h.value, (l) => (s(), k(L, {
147
+ key: "fav-" + l.id,
148
+ item: l,
149
+ onMenuClick: y
150
+ }, null, 8, ["item"]))), 128))
151
+ ])
152
+ ], 2)
153
+ ], 64)),
154
+ t[3] || (t[3] = a("div", { class: "h-px bg-border mx-2 my-1" }, null, -1))
155
+ ], 64)) : m("", !0),
156
+ (s(!0), r(u, null, _(K.value, (l) => (s(), r(u, {
157
+ key: l.id
158
+ }, [
159
+ l.menuType === "F" ? (s(), k(Q, {
160
+ key: 0,
161
+ item: l,
162
+ onMenuClick: y
163
+ }, null, 8, ["item"])) : l.menuType === "L" ? (s(), k(L, {
164
+ key: 1,
165
+ item: l,
166
+ onMenuClick: y
167
+ }, null, 8, ["item"])) : m("", !0)
168
+ ], 64))), 128)),
169
+ K.value.length === 0 && f.value.trim() ? (s(), r("div", ee, " 검색 결과가 없습니다. ")) : m("", !0)
170
+ ]),
171
+ a("div", te, [
172
+ a("button", {
173
+ class: "flex items-center gap-2 w-full px-2 py-1 rounded-md text-xs text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors",
174
+ onClick: $
175
+ }, [
176
+ d(v, {
177
+ name: i.collapsed ? "panelLeftOpen" : "panelLeftClose",
178
+ size: "sm"
179
+ }, null, 8, ["name"]),
180
+ i.collapsed ? m("", !0) : (s(), r("span", le, "접기"))
181
+ ])
182
+ ])
183
+ ], 4));
184
+ }
185
+ });
186
+ export {
187
+ ue as default
188
+ };
189
+ //# sourceMappingURL=JSidebar.vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JSidebar.vue.js","sources":["../../../../../src/components/organisms/JSidebar/JSidebar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, computed, provide, watch, reactive } from 'vue'\nimport type { SidebarMenuItem, SidebarState } from '@/types/sidebar.types'\nimport { SIDEBAR_INJECTION_KEY } from '@/types/sidebar.types'\nimport JIcon from '@/components/atoms/JIcon.vue'\nimport JInput from '@/components/atoms/JInput.vue'\nimport JSidebarGroup from './JSidebarGroup.vue'\nimport JSidebarItem from './JSidebarItem.vue'\nimport { cn } from '@/lib/utils'\n\n/**\n * JSidebar - 통합 사이드바 컴포넌트\n *\n * DB 트리 데이터 기반, provide/inject로 상태 공유.\n * collapsed 모드, 검색, 즐겨찾기(localStorage) 지원.\n * 라우터를 모름 — @menu-click으로 아이템을 올려보냄.\n */\n\nconst props = withDefaults(\n defineProps<{\n /** 메뉴 트리 데이터 */\n items: SidebarMenuItem[]\n /** 접힘 상태 (v-model:collapsed) */\n collapsed?: boolean\n /** 현재 활성 경로 */\n activePath?: string\n /** 펼침 너비 */\n width?: string\n /** 접힘 너비 */\n collapsedWidth?: string\n /** 즐겨찾기 localStorage 키 (없으면 즐겨찾기 비활성) */\n storageKey?: string\n /** 검색 표시 여부 */\n showSearch?: boolean\n /** 즐겨찾기 섹션 표시 여부 */\n showFavorites?: boolean\n }>(),\n {\n collapsed: false,\n width: '220px',\n collapsedWidth: '56px',\n showSearch: true,\n showFavorites: true,\n },\n)\n\nconst emit = defineEmits<{\n 'update:collapsed': [value: boolean]\n 'menu-click': [item: SidebarMenuItem, event: MouseEvent]\n}>()\n\n// ── 즐겨찾기 (localStorage) ──\nconst favorites = ref<Set<string>>(new Set(loadFavorites()))\n\nfunction loadFavorites(): string[] {\n if (!props.storageKey) return []\n try {\n const raw = localStorage.getItem(props.storageKey)\n return raw ? JSON.parse(raw) : []\n } catch {\n return []\n }\n}\n\nfunction saveFavorites() {\n if (!props.storageKey) return\n localStorage.setItem(props.storageKey, JSON.stringify([...favorites.value]))\n}\n\nfunction toggleFavorite(id: string) {\n if (favorites.value.has(id)) {\n favorites.value.delete(id)\n } else {\n favorites.value.add(id)\n }\n favorites.value = new Set(favorites.value) // trigger reactivity\n saveFavorites()\n}\n\n// ── provide/inject 상태 ──\nconst sidebarState = reactive<SidebarState>({\n collapsed: props.collapsed,\n activePath: props.activePath,\n favorites: favorites.value,\n toggleFavorite,\n})\n\n// props 변경 시 state 동기화\nwatch(() => props.collapsed, (v) => { sidebarState.collapsed = v })\nwatch(() => props.activePath, (v) => { sidebarState.activePath = v })\nwatch(favorites, (v) => { sidebarState.favorites = v })\n\nprovide(SIDEBAR_INJECTION_KEY, sidebarState)\n\n// ── 검색 ──\nconst searchQuery = ref('')\n\n/** 즐겨찾기 아이템 (L 타입만, 트리 평탄화) */\nconst favoriteItems = computed(() => {\n if (!props.storageKey || favorites.value.size === 0) return []\n const result: SidebarMenuItem[] = []\n const flatten = (items: SidebarMenuItem[]) => {\n for (const item of items) {\n if (item.menuType === 'L' && favorites.value.has(item.id)) {\n result.push(item)\n }\n if (item.children) flatten(item.children)\n }\n }\n flatten(props.items)\n return result\n})\n\n/** 검색 필터링 (재귀) */\nconst filteredItems = computed(() => {\n const q = searchQuery.value.trim().toLowerCase()\n if (!q) return props.items\n\n const filter = (items: SidebarMenuItem[]): SidebarMenuItem[] => {\n const result: SidebarMenuItem[] = []\n for (const item of items) {\n const matchLabel = item.label.toLowerCase().includes(q)\n const filteredChildren = item.children ? filter(item.children) : undefined\n if (matchLabel || (filteredChildren && filteredChildren.length > 0)) {\n result.push({ ...item, children: filteredChildren })\n }\n }\n return result\n }\n return filter(props.items)\n})\n\n/** 즐겨찾기 검색 필터링 */\nconst filteredFavorites = computed(() => {\n const q = searchQuery.value.trim().toLowerCase()\n if (!q) return favoriteItems.value\n return favoriteItems.value.filter(item => item.label.toLowerCase().includes(q))\n})\n\n// ── 즐겨찾기 그룹 펼침 ──\nconst favoritesExpanded = ref(true)\n\n// ── 이벤트 핸들러 ──\nconst handleMenuClick = (item: SidebarMenuItem, event: MouseEvent) => {\n emit('menu-click', item, event)\n}\n\nconst toggleCollapsed = () => {\n emit('update:collapsed', !props.collapsed)\n}\n\n// ── 사이드바 너비 ──\nconst sidebarWidth = computed(() => props.collapsed ? props.collapsedWidth : props.width)\n</script>\n\n<template>\n <aside\n class=\"h-full bg-background border-r border-border flex flex-col flex-shrink-0 overflow-hidden transition-[width] duration-200 ease-out\"\n :style=\"{ width: sidebarWidth }\"\n >\n <!-- 검색바 (펼침 + showSearch) -->\n <div v-if=\"!collapsed && showSearch\" class=\"p-2 flex-shrink-0\">\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=\"pl-8 h-7 text-xs\"\n />\n </div>\n </div>\n\n <!-- 메뉴 영역 -->\n <nav class=\"flex-1 overflow-y-auto overflow-x-hidden px-1 py-1\">\n <!-- 즐겨찾기 섹션 -->\n <template v-if=\"showFavorites && storageKey && filteredFavorites.length > 0\">\n <!-- 펼침: 즐겨찾기 그룹 -->\n <template v-if=\"!collapsed\">\n <button\n class=\"flex items-center gap-1.5 w-full px-2 py-1.5 text-xs font-semibold text-yellow-600 cursor-pointer select-none transition-colors hover:text-yellow-700\"\n @click=\"favoritesExpanded = !favoritesExpanded\"\n >\n <JIcon\n name=\"chevronRight\"\n size=\"sm\"\n :class=\"cn(\n 'flex-shrink-0 transition-transform duration-200',\n favoritesExpanded && 'rotate-90'\n )\"\n />\n <JIcon name=\"star\" size=\"sm\" class=\"flex-shrink-0 text-yellow-500\" />\n <span class=\"flex-1 text-left\">즐겨찾기</span>\n <span class=\"text-[10px] text-yellow-500/60\">{{ filteredFavorites.length }}</span>\n </button>\n <div\n :class=\"cn(\n 'grid transition-[grid-template-rows] duration-200 ease-out',\n favoritesExpanded ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'\n )\"\n >\n <div class=\"overflow-hidden\">\n <JSidebarItem\n v-for=\"fav in filteredFavorites\"\n :key=\"'fav-' + fav.id\"\n :item=\"fav\"\n @menu-click=\"handleMenuClick\"\n />\n </div>\n </div>\n </template>\n <!-- 접힘: 별 아이콘 + 구분선 -->\n <template v-else>\n <div class=\"flex justify-center py-1\">\n <JIcon name=\"star\" size=\"sm\" class=\"text-yellow-500\" />\n </div>\n <JSidebarItem\n v-for=\"fav in filteredFavorites\"\n :key=\"'fav-c-' + fav.id\"\n :item=\"fav\"\n @menu-click=\"handleMenuClick\"\n />\n </template>\n <div class=\"h-px bg-border mx-2 my-1\" />\n </template>\n\n <!-- 메인 메뉴 -->\n <template v-for=\"item in filteredItems\" :key=\"item.id\">\n <JSidebarGroup\n v-if=\"item.menuType === 'F'\"\n :item=\"item\"\n @menu-click=\"handleMenuClick\"\n />\n <JSidebarItem\n v-else-if=\"item.menuType === 'L'\"\n :item=\"item\"\n @menu-click=\"handleMenuClick\"\n />\n </template>\n\n <!-- 검색 결과 없음 -->\n <div\n v-if=\"filteredItems.length === 0 && searchQuery.trim()\"\n class=\"text-center py-6 text-muted-foreground text-xs\"\n >\n 검색 결과가 없습니다.\n </div>\n </nav>\n\n <!-- 하단: collapse 토글 -->\n <div class=\"flex-shrink-0 border-t border-border p-1\">\n <button\n class=\"flex items-center gap-2 w-full px-2 py-1 rounded-md text-xs text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors\"\n @click=\"toggleCollapsed\"\n >\n <JIcon\n :name=\"collapsed ? 'panelLeftOpen' : 'panelLeftClose'\"\n size=\"sm\"\n />\n <span v-if=\"!collapsed\">접기</span>\n </button>\n </div>\n </aside>\n</template>\n"],"names":["props","__props","emit","__emit","favorites","ref","loadFavorites","raw","saveFavorites","toggleFavorite","id","sidebarState","reactive","watch","v","provide","SIDEBAR_INJECTION_KEY","searchQuery","favoriteItems","computed","result","flatten","items","item","filteredItems","q","filter","matchLabel","filteredChildren","filteredFavorites","favoritesExpanded","handleMenuClick","event","toggleCollapsed","sidebarWidth","_createElementBlock","_openBlock","_hoisted_1","_createElementVNode","_hoisted_2","_createVNode","JIcon","JInput","$event","_hoisted_3","_Fragment","_hoisted_6","_renderList","fav","_createBlock","JSidebarItem","_cache","_unref","cn","_hoisted_4","_toDisplayString","_hoisted_5","JSidebarGroup","_hoisted_7","_hoisted_8"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("./JFormField.vue.cjs");;/* empty css */exports.default=e.default;
2
- //# sourceMappingURL=JFormField.vue3.cjs.map
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("./JSidebar.vue.cjs");exports.default=e.default;
2
+ //# sourceMappingURL=JSidebar.vue2.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JSidebar.vue2.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -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\">\nimport { ref, computed, inject, watch } from 'vue'\nimport type { SidebarMenuItem } from '@/types/sidebar.types'\nimport { SIDEBAR_INJECTION_KEY } from '@/types/sidebar.types'\nimport JIcon from '@/components/atoms/JIcon.vue'\nimport JSidebarItem from './JSidebarItem.vue'\nimport { cn } from '@/lib/utils'\n\n/**\n * JSidebarGroup - 그룹 헤더 (menuType='F' 전용)\n * 폴더 타입 메뉴를 렌더링. 펼침/접힘 토글 + 하위 메뉴 재귀 렌더링.\n * 링크(L)는 JSidebarItem에서 처리.\n */\n\nconst props = defineProps<{\n item: SidebarMenuItem\n}>()\n\nconst emit = defineEmits<{\n 'menu-click': [item: SidebarMenuItem, event: MouseEvent]\n}>()\n\nconst state = inject(SIDEBAR_INJECTION_KEY)!\n\nconst isExpanded = ref(false)\n\n/** 직계 자식 중 L 타입 개수 (재귀하지 않음) */\nconst childCount = computed(() => {\n if (!props.item.children) return 0\n return props.item.children.filter(c => c.menuType === 'L').length\n})\n\nconst toggleExpand = () => {\n isExpanded.value = !isExpanded.value\n}\n\n/** 자식의 menu-click을 상위로 전파 */\nconst handleChildClick = (item: SidebarMenuItem, event: MouseEvent) => {\n emit('menu-click', item, event)\n}\n\n/** 현재 경로가 이 그룹의 자식에 포함되면 자동 펼침 */\nconst hasActiveChild = computed(() => {\n if (!state.activePath || !props.item.children) return false\n const checkActive = (items: SidebarMenuItem[]): boolean => {\n for (const child of items) {\n if (child.menuType === 'L' && child.path === state.activePath) return true\n if (child.children && checkActive(child.children)) return true\n }\n return false\n }\n return checkActive(props.item.children)\n})\n\nwatch(hasActiveChild, (active) => {\n if (active) isExpanded.value = true\n}, { immediate: true })\n</script>\n\n<template>\n <!-- Collapsed: 구분선만 -->\n <template v-if=\"state.collapsed\">\n <div class=\"h-px bg-border mx-2 my-1\" />\n <!-- collapsed 상태에서 자식 아이템 직접 렌더링 -->\n <template v-for=\"child in item.children\" :key=\"child.id\">\n <JSidebarItem\n v-if=\"child.menuType === 'L'\"\n :item=\"child\"\n @menu-click=\"handleChildClick\"\n />\n <!-- 중첩 폴더는 collapsed에서 무시 (1단계만) -->\n </template>\n </template>\n\n <!-- Expanded: 그룹 헤더 + 하위 메뉴 -->\n <div v-else>\n <!-- 그룹 헤더 -->\n <button\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\"\n @click=\"toggleExpand\"\n >\n <JIcon\n name=\"chevronRight\"\n size=\"sm\"\n :class=\"cn(\n 'flex-shrink-0 transition-transform duration-200',\n isExpanded && 'rotate-90'\n )\"\n />\n <span class=\"flex-1 truncate text-left\">{{ item.label }}</span>\n <span\n v-if=\"childCount > 0\"\n class=\"text-[10px] text-muted-foreground/60 flex-shrink-0\"\n >\n {{ childCount }}\n </span>\n </button>\n\n <!-- 하위 메뉴 (grid-template-rows 애니메이션) -->\n <div\n :class=\"cn(\n 'grid transition-[grid-template-rows] duration-200 ease-out',\n isExpanded ? 'grid-rows-[1fr]' : 'grid-rows-[0fr]'\n )\"\n >\n <div class=\"overflow-hidden ml-3 pl-2 border-l border-border/50\">\n <template v-for=\"child in item.children\" :key=\"child.id\">\n <JSidebarItem\n v-if=\"child.menuType === 'L'\"\n :item=\"child\"\n @menu-click=\"handleChildClick\"\n />\n <!-- 중첩 폴더: 재귀 -->\n <JSidebarGroup\n v-else-if=\"child.menuType === 'F'\"\n :item=\"child\"\n @menu-click=\"handleChildClick\"\n />\n </template>\n </div>\n </div>\n </div>\n</template>\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,oDAA2E1B,EAAA,OAAU,WAAA,sBAK/FkB,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,+DAAkF1B,EAAA,MAAU,kBAAA,iBAAA,KAKpGkB,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"}