@j-solution/components 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/assets/jwms-portal-frontend-BtHTA-UF.css +1 -0
- package/assets/styles/global-utilities.css +34 -0
- package/assets/styles/j-components.css +1 -1
- package/assets/styles/themes.css +21 -21
- package/components/atoms/JButton.vue.cjs +6 -1
- package/components/atoms/JButton.vue.cjs.map +1 -1
- package/components/atoms/JButton.vue.js +10 -85
- package/components/atoms/JButton.vue.js.map +1 -1
- package/components/atoms/JButton.vue2.cjs +1 -1
- package/components/atoms/JButton.vue2.cjs.map +1 -1
- package/components/atoms/JButton.vue2.js +85 -2
- package/components/atoms/JButton.vue2.js.map +1 -1
- package/components/atoms/JGrid.vue.cjs +1 -1
- package/components/atoms/JGrid.vue.js +1 -1
- package/components/atoms/JGrid.vue2.cjs +1 -1
- package/components/atoms/JGrid.vue2.cjs.map +1 -1
- package/components/atoms/JGrid.vue2.js +39 -35
- package/components/atoms/JGrid.vue2.js.map +1 -1
- package/components/atoms/JLabel.vue.cjs +1 -1
- package/components/atoms/JLabel.vue.cjs.map +1 -1
- package/components/atoms/JLabel.vue.js +26 -22
- package/components/atoms/JLabel.vue.js.map +1 -1
- package/components/atoms/JSectionTitle.vue.cjs +7 -0
- package/components/atoms/JSectionTitle.vue.cjs.map +1 -0
- package/components/atoms/JSectionTitle.vue.js +13 -0
- package/components/atoms/JSectionTitle.vue.js.map +1 -0
- package/components/atoms/JSectionTitle.vue2.cjs +2 -0
- package/components/atoms/JSectionTitle.vue2.cjs.map +1 -0
- package/components/atoms/JSectionTitle.vue2.js +67 -0
- package/components/atoms/JSectionTitle.vue2.js.map +1 -0
- package/components/atoms/JSplitter.vue.cjs +6 -1
- package/components/atoms/JSplitter.vue.cjs.map +1 -1
- package/components/atoms/JSplitter.vue.js +10 -59
- package/components/atoms/JSplitter.vue.js.map +1 -1
- package/components/atoms/JSplitter.vue2.cjs +1 -1
- package/components/atoms/JSplitter.vue2.cjs.map +1 -1
- package/components/atoms/JSplitter.vue2.js +59 -2
- package/components/atoms/JSplitter.vue2.js.map +1 -1
- package/components/examples/ExampleCrudPage.vue.cjs +1 -1
- package/components/examples/ExampleCrudPage.vue.cjs.map +1 -1
- package/components/examples/ExampleCrudPage.vue.js +228 -208
- package/components/examples/ExampleCrudPage.vue.js.map +1 -1
- package/components/examples/ExampleTabMappingPage.vue.cjs +1 -1
- package/components/examples/ExampleTabMappingPage.vue.cjs.map +1 -1
- package/components/examples/ExampleTabMappingPage.vue.js +341 -368
- package/components/examples/ExampleTabMappingPage.vue.js.map +1 -1
- package/components/molecules/JAlert.vue.cjs +1 -1
- package/components/molecules/JAlert.vue.cjs.map +1 -1
- package/components/molecules/JAlert.vue.js +18 -16
- package/components/molecules/JAlert.vue.js.map +1 -1
- package/components/molecules/JCard.vue.cjs +1 -1
- package/components/molecules/JCard.vue.cjs.map +1 -1
- package/components/molecules/JCard.vue.js +55 -39
- package/components/molecules/JCard.vue.js.map +1 -1
- package/components/molecules/JEmptyState.vue.cjs +7 -0
- package/components/molecules/JEmptyState.vue.cjs.map +1 -0
- package/components/molecules/JEmptyState.vue.js +13 -0
- package/components/molecules/JEmptyState.vue.js.map +1 -0
- package/components/molecules/JEmptyState.vue2.cjs +2 -0
- package/components/molecules/JEmptyState.vue2.cjs.map +1 -0
- package/components/molecules/JEmptyState.vue2.js +127 -0
- package/components/molecules/JEmptyState.vue2.js.map +1 -0
- package/components/molecules/JFormField.vue.cjs +6 -1
- package/components/molecules/JFormField.vue.cjs.map +1 -1
- package/components/molecules/JFormField.vue.js +10 -264
- package/components/molecules/JFormField.vue.js.map +1 -1
- package/components/molecules/JFormField.vue2.cjs +2 -0
- package/components/molecules/JFormField.vue2.cjs.map +1 -0
- package/components/molecules/JFormField.vue2.js +271 -0
- package/components/molecules/JFormField.vue2.js.map +1 -0
- package/components/molecules/JTabs.vue.cjs +1 -1
- package/components/molecules/JTabs.vue.js +1 -1
- package/components/molecules/JTabs.vue2.cjs +1 -1
- package/components/molecules/JTabs.vue2.cjs.map +1 -1
- package/components/molecules/JTabs.vue2.js +44 -50
- package/components/molecules/JTabs.vue2.js.map +1 -1
- package/components/molecules/JTitlebar.vue.cjs +1 -1
- package/components/molecules/JTitlebar.vue.cjs.map +1 -1
- package/components/molecules/JTitlebar.vue.js +23 -20
- package/components/molecules/JTitlebar.vue.js.map +1 -1
- package/components/organisms/JDynamicForm.vue2.cjs +1 -1
- package/components/organisms/JDynamicForm.vue2.cjs.map +1 -1
- package/components/organisms/JDynamicForm.vue2.js +35 -32
- package/components/organisms/JDynamicForm.vue2.js.map +1 -1
- package/components/organisms/JDynamicTabs.vue.cjs +1 -1
- package/components/organisms/JDynamicTabs.vue.cjs.map +1 -1
- package/components/organisms/JDynamicTabs.vue.js +47 -52
- package/components/organisms/JDynamicTabs.vue.js.map +1 -1
- package/components/organisms/JFilterBar.vue.cjs +6 -1
- package/components/organisms/JFilterBar.vue.cjs.map +1 -1
- package/components/organisms/JFilterBar.vue.js +10 -137
- package/components/organisms/JFilterBar.vue.js.map +1 -1
- package/components/organisms/JFilterBar.vue2.cjs +1 -1
- package/components/organisms/JFilterBar.vue2.cjs.map +1 -1
- package/components/organisms/JFilterBar.vue2.js +141 -2
- package/components/organisms/JFilterBar.vue2.js.map +1 -1
- package/components/organisms/JFormModal.vue.cjs +1 -1
- package/components/organisms/JFormModal.vue.cjs.map +1 -1
- package/components/organisms/JFormModal.vue.js +54 -49
- package/components/organisms/JFormModal.vue.js.map +1 -1
- package/components/organisms/JHeader.vue.cjs +1 -1
- package/components/organisms/JHeader.vue.cjs.map +1 -1
- package/components/organisms/JHeader.vue.js +191 -190
- package/components/organisms/JHeader.vue.js.map +1 -1
- package/components/organisms/JModal.vue.cjs +1 -1
- package/components/organisms/JModal.vue.cjs.map +1 -1
- package/components/organisms/JModal.vue.js +47 -45
- package/components/organisms/JModal.vue.js.map +1 -1
- package/components/organisms/JPageContainer.vue.cjs +1 -1
- package/components/organisms/JPageContainer.vue.cjs.map +1 -1
- package/components/organisms/JPageContainer.vue.js +22 -22
- package/components/organisms/JPageContainer.vue.js.map +1 -1
- package/components/organisms/JSearchPanel.vue2.cjs +1 -1
- package/components/organisms/JSearchPanel.vue2.cjs.map +1 -1
- package/components/organisms/JSearchPanel.vue2.js +34 -32
- package/components/organisms/JSearchPanel.vue2.js.map +1 -1
- package/components/organisms/JShuttle.vue.cjs +7 -0
- package/components/organisms/JShuttle.vue.cjs.map +1 -0
- package/components/organisms/JShuttle.vue.js +13 -0
- package/components/organisms/JShuttle.vue.js.map +1 -0
- package/components/organisms/JShuttle.vue2.cjs +2 -0
- package/components/organisms/JShuttle.vue2.cjs.map +1 -0
- package/components/organisms/JShuttle.vue2.js +216 -0
- package/components/organisms/JShuttle.vue2.js.map +1 -0
- package/components/organisms/JSidebarSimple/JDynamicMenuItem.vue.cjs +1 -1
- package/components/organisms/JSidebarSimple/JDynamicMenuItem.vue.cjs.map +1 -1
- package/components/organisms/JSidebarSimple/JDynamicMenuItem.vue.js +52 -52
- package/components/organisms/JSidebarSimple/JDynamicMenuItem.vue.js.map +1 -1
- package/components/shadcn/Card.vue.cjs +1 -1
- package/components/shadcn/Card.vue.cjs.map +1 -1
- package/components/shadcn/Card.vue.js +1 -1
- package/components/shadcn/Card.vue.js.map +1 -1
- package/components/shadcn/CardContent.vue.cjs +1 -1
- package/components/shadcn/CardContent.vue.cjs.map +1 -1
- package/components/shadcn/CardContent.vue.js +4 -4
- package/components/shadcn/CardContent.vue.js.map +1 -1
- package/components/shadcn/CardHeader.vue.cjs +1 -1
- package/components/shadcn/CardHeader.vue.cjs.map +1 -1
- package/components/shadcn/CardHeader.vue.js +5 -5
- package/components/shadcn/CardHeader.vue.js.map +1 -1
- package/components/shadcn/Input.vue.cjs +1 -1
- package/components/shadcn/Input.vue.cjs.map +1 -1
- package/components/shadcn/Input.vue.js +3 -3
- package/components/shadcn/Input.vue.js.map +1 -1
- package/components/shadcn/SelectTrigger.vue.cjs +1 -1
- package/components/shadcn/SelectTrigger.vue.cjs.map +1 -1
- package/components/shadcn/SelectTrigger.vue.js +1 -1
- package/components/shadcn/SelectTrigger.vue.js.map +1 -1
- package/components/shadcn/TabsContent.vue.cjs +1 -1
- package/components/shadcn/TabsContent.vue.cjs.map +1 -1
- package/components/shadcn/TabsContent.vue.js +1 -1
- package/components/shadcn/TabsContent.vue.js.map +1 -1
- package/components/shadcn/TabsList.vue.cjs +1 -1
- package/components/shadcn/TabsList.vue.cjs.map +1 -1
- package/components/shadcn/TabsList.vue.js +10 -10
- package/components/shadcn/TabsList.vue.js.map +1 -1
- package/components/shadcn/Textarea.vue.cjs +1 -1
- package/components/shadcn/Textarea.vue.cjs.map +1 -1
- package/components/shadcn/Textarea.vue.js +1 -1
- package/components/shadcn/Textarea.vue.js.map +1 -1
- package/components/shadcn/index.cjs +1 -1
- package/components/shadcn/index.cjs.map +1 -1
- package/components/shadcn/index.js +4 -4
- package/components/shadcn/index.js.map +1 -1
- package/components/templates/JLayout.vue.cjs.map +1 -1
- package/components/templates/JLayout.vue.js.map +1 -1
- package/index.cjs +1 -1
- package/index.js +73 -67
- package/package.json +1 -1
- package/types/index.d.ts +920 -777
- package/assets/jwms-portal-frontend-CwxPfHfa.css +0 -1
- package/components/molecules/JFormField.vue3.cjs +0 -2
- package/components/molecules/JFormField.vue3.cjs.map +0 -1
- package/components/molecules/JFormField.vue3.js +0 -6
- package/components/molecules/JFormField.vue3.js.map +0 -1
|
@@ -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"),
|
|
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
|
|
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: "
|
|
11
|
-
}, U = /* @__PURE__ */
|
|
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:
|
|
30
|
-
const t = e, c =
|
|
31
|
-
if (!
|
|
32
|
-
const
|
|
33
|
-
return t.expandedKeys?.has(
|
|
34
|
-
}),
|
|
35
|
-
if (
|
|
36
|
-
|
|
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 (
|
|
40
|
-
const i = t.item.menuKey || t.item.label,
|
|
41
|
-
c("expandChange", i,
|
|
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:
|
|
44
|
+
event: a
|
|
45
45
|
});
|
|
46
46
|
} else
|
|
47
|
-
!t.disableNavigation && t.item.path &&
|
|
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:
|
|
51
|
+
event: a
|
|
52
52
|
});
|
|
53
|
-
},
|
|
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 =
|
|
66
|
-
if (!
|
|
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
|
|
69
|
-
let
|
|
70
|
-
for (const
|
|
71
|
-
|
|
72
|
-
return
|
|
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
|
|
74
|
+
return a(t.item.children);
|
|
75
75
|
});
|
|
76
|
-
return (
|
|
77
|
-
const
|
|
78
|
-
return
|
|
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
|
-
|
|
81
|
+
F("div", {
|
|
82
82
|
class: u(v(f)(
|
|
83
83
|
d.value.itemClass,
|
|
84
84
|
{
|
|
85
|
-
"bg-
|
|
86
|
-
"hover:bg-accent/50": !
|
|
87
|
-
"opacity-50 cursor-not-allowed":
|
|
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":
|
|
89
|
+
"font-semibold": r.value
|
|
90
90
|
// 폴더인 경우 볼드체
|
|
91
91
|
}
|
|
92
92
|
)),
|
|
93
|
-
style: B(
|
|
94
|
-
onClick:
|
|
93
|
+
style: B(A.value),
|
|
94
|
+
onClick: E
|
|
95
95
|
}, [
|
|
96
|
-
|
|
96
|
+
r.value ? (l(), C(b, {
|
|
97
97
|
key: 0,
|
|
98
|
-
name:
|
|
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"])) : (
|
|
103
|
-
e.item.icon && !
|
|
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
|
-
|
|
109
|
+
F("span", {
|
|
110
110
|
class: u(d.value.labelClass)
|
|
111
|
-
},
|
|
112
|
-
|
|
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
|
-
}, " (" +
|
|
119
|
-
e.item.menuKey && e.item.menuType === "L" && e.onFavoriteToggle ? (
|
|
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((
|
|
126
|
+
onClick: i[0] || (i[0] = L((s) => e.onFavoriteToggle(e.item.menuKey), ["stop"]))
|
|
127
127
|
}, [
|
|
128
|
-
|
|
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
|
-
|
|
136
|
-
(
|
|
137
|
-
key:
|
|
138
|
-
item:
|
|
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,
|
|
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);
|