@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.
- package/README.md +413 -415
- package/assets/jwms-portal-frontend-Ct2Tc7yj.css +1 -0
- package/assets/styles/global-utilities.css +34 -0
- package/assets/styles/j-components.css +1 -1
- package/assets/styles/themes.css +443 -443
- 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 +143 -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/JSidebar/JSidebar.vue.cjs +2 -0
- package/components/organisms/JSidebar/JSidebar.vue.cjs.map +1 -0
- package/components/organisms/JSidebar/JSidebar.vue.js +189 -0
- package/components/organisms/JSidebar/JSidebar.vue.js.map +1 -0
- package/components/{molecules/JFormField.vue3.cjs → organisms/JSidebar/JSidebar.vue2.cjs} +2 -2
- package/components/organisms/JSidebar/JSidebar.vue2.cjs.map +1 -0
- package/components/organisms/JSidebar/JSidebar.vue2.js +5 -0
- package/components/organisms/JSidebar/JSidebar.vue2.js.map +1 -0
- package/components/organisms/JSidebar/JSidebarGroup.vue.cjs +2 -0
- package/components/organisms/JSidebar/JSidebarGroup.vue.cjs.map +1 -0
- package/components/organisms/JSidebar/JSidebarGroup.vue.js +89 -0
- package/components/organisms/JSidebar/JSidebarGroup.vue.js.map +1 -0
- package/components/organisms/JSidebar/JSidebarGroup.vue2.cjs +2 -0
- package/components/organisms/JSidebar/JSidebarGroup.vue2.cjs.map +1 -0
- package/components/organisms/JSidebar/JSidebarGroup.vue2.js +5 -0
- package/components/organisms/JSidebar/JSidebarGroup.vue2.js.map +1 -0
- package/components/organisms/JSidebar/JSidebarItem.vue.cjs +2 -0
- package/components/organisms/JSidebar/JSidebarItem.vue.cjs.map +1 -0
- package/components/organisms/JSidebar/JSidebarItem.vue.js +79 -0
- package/components/organisms/JSidebar/JSidebarItem.vue.js.map +1 -0
- package/components/organisms/JSidebar/JSidebarItem.vue2.cjs +2 -0
- package/components/organisms/JSidebar/JSidebarItem.vue2.cjs.map +1 -0
- package/components/organisms/JSidebar/JSidebarItem.vue2.js +5 -0
- package/components/organisms/JSidebar/JSidebarItem.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/components/templates/JLayoutSimple.vue.cjs +1 -1
- package/components/templates/JLayoutSimple.vue.cjs.map +1 -1
- package/components/templates/JLayoutSimple.vue.js +36 -30
- package/components/templates/JLayoutSimple.vue.js.map +1 -1
- package/index.cjs +1 -1
- package/index.js +75 -67
- package/package.json +1 -1
- package/types/index.d.ts +662 -461
- package/types/sidebar.types.cjs +2 -0
- package/types/sidebar.types.cjs.map +1 -0
- package/types/sidebar.types.js +5 -0
- package/types/sidebar.types.js.map +1 -0
- package/assets/jwms-portal-frontend-CwxPfHfa.css +0 -1
- 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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"}
|
|
@@ -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":"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"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { defineComponent as T, ref as C, reactive as W, watch as S, provide as q, computed as w, createElementBlock as r, openBlock as s, normalizeStyle as D, createCommentVNode as m, createElementVNode as a, createVNode as d, Fragment as u, normalizeClass as B, unref as I, toDisplayString as R, renderList as _, createBlock as k } from "vue";
|
|
2
|
+
import { SIDEBAR_INJECTION_KEY as j } from "../../../types/sidebar.types.js";
|
|
3
|
+
import v from "../../atoms/JIcon.vue.js";
|
|
4
|
+
import A from "../../atoms/JInput.vue.js";
|
|
5
|
+
import Q from "./JSidebarGroup.vue.js";
|
|
6
|
+
import L from "./JSidebarItem.vue.js";
|
|
7
|
+
import { cn as N } from "../../../lib/utils.js";
|
|
8
|
+
const U = {
|
|
9
|
+
key: 0,
|
|
10
|
+
class: "p-2 flex-shrink-0"
|
|
11
|
+
}, Y = { class: "relative" }, G = { class: "flex-1 overflow-y-auto overflow-x-hidden px-1 py-1" }, H = { class: "text-[10px] text-yellow-500/60" }, X = { class: "overflow-hidden" }, Z = { class: "flex justify-center py-1" }, ee = {
|
|
12
|
+
key: 1,
|
|
13
|
+
class: "text-center py-6 text-muted-foreground text-xs"
|
|
14
|
+
}, te = { class: "flex-shrink-0 border-t border-border p-1" }, le = { key: 0 }, ue = /* @__PURE__ */ T({
|
|
15
|
+
__name: "JSidebar",
|
|
16
|
+
props: {
|
|
17
|
+
items: {},
|
|
18
|
+
collapsed: { type: Boolean, default: !1 },
|
|
19
|
+
activePath: {},
|
|
20
|
+
width: { default: "220px" },
|
|
21
|
+
collapsedWidth: { default: "56px" },
|
|
22
|
+
storageKey: {},
|
|
23
|
+
showSearch: { type: Boolean, default: !0 },
|
|
24
|
+
showFavorites: { type: Boolean, default: !0 }
|
|
25
|
+
},
|
|
26
|
+
emits: ["update:collapsed", "menu-click"],
|
|
27
|
+
setup(i, { emit: E }) {
|
|
28
|
+
const o = i, z = E, n = C(new Set(V()));
|
|
29
|
+
function V() {
|
|
30
|
+
if (!o.storageKey) return [];
|
|
31
|
+
try {
|
|
32
|
+
const e = localStorage.getItem(o.storageKey);
|
|
33
|
+
return e ? JSON.parse(e) : [];
|
|
34
|
+
} catch {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function M() {
|
|
39
|
+
o.storageKey && localStorage.setItem(o.storageKey, JSON.stringify([...n.value]));
|
|
40
|
+
}
|
|
41
|
+
function P(e) {
|
|
42
|
+
n.value.has(e) ? n.value.delete(e) : n.value.add(e), n.value = new Set(n.value), M();
|
|
43
|
+
}
|
|
44
|
+
const p = W({
|
|
45
|
+
collapsed: o.collapsed,
|
|
46
|
+
activePath: o.activePath,
|
|
47
|
+
favorites: n.value,
|
|
48
|
+
toggleFavorite: P
|
|
49
|
+
});
|
|
50
|
+
S(() => o.collapsed, (e) => {
|
|
51
|
+
p.collapsed = e;
|
|
52
|
+
}), S(() => o.activePath, (e) => {
|
|
53
|
+
p.activePath = e;
|
|
54
|
+
}), S(n, (e) => {
|
|
55
|
+
p.favorites = e;
|
|
56
|
+
}), q(j, p);
|
|
57
|
+
const f = C(""), F = w(() => {
|
|
58
|
+
if (!o.storageKey || n.value.size === 0) return [];
|
|
59
|
+
const e = [], t = (l) => {
|
|
60
|
+
for (const c of l)
|
|
61
|
+
c.menuType === "L" && n.value.has(c.id) && e.push(c), c.children && t(c.children);
|
|
62
|
+
};
|
|
63
|
+
return t(o.items), e;
|
|
64
|
+
}), K = w(() => {
|
|
65
|
+
const e = f.value.trim().toLowerCase();
|
|
66
|
+
if (!e) return o.items;
|
|
67
|
+
const t = (l) => {
|
|
68
|
+
const c = [];
|
|
69
|
+
for (const g of l) {
|
|
70
|
+
const O = g.label.toLowerCase().includes(e), b = g.children ? t(g.children) : void 0;
|
|
71
|
+
(O || b && b.length > 0) && c.push({ ...g, children: b });
|
|
72
|
+
}
|
|
73
|
+
return c;
|
|
74
|
+
};
|
|
75
|
+
return t(o.items);
|
|
76
|
+
}), h = w(() => {
|
|
77
|
+
const e = f.value.trim().toLowerCase();
|
|
78
|
+
return e ? F.value.filter((t) => t.label.toLowerCase().includes(e)) : F.value;
|
|
79
|
+
}), x = C(!0), y = (e, t) => {
|
|
80
|
+
z("menu-click", e, t);
|
|
81
|
+
}, $ = () => {
|
|
82
|
+
z("update:collapsed", !o.collapsed);
|
|
83
|
+
}, J = w(() => o.collapsed ? o.collapsedWidth : o.width);
|
|
84
|
+
return (e, t) => (s(), r("aside", {
|
|
85
|
+
class: "h-full bg-background border-r border-border flex flex-col flex-shrink-0 overflow-hidden transition-[width] duration-200 ease-out",
|
|
86
|
+
style: D({ width: J.value })
|
|
87
|
+
}, [
|
|
88
|
+
!i.collapsed && i.showSearch ? (s(), r("div", U, [
|
|
89
|
+
a("div", Y, [
|
|
90
|
+
d(v, {
|
|
91
|
+
name: "search",
|
|
92
|
+
size: "sm",
|
|
93
|
+
class: "absolute left-2 top-1/2 -translate-y-1/2 text-muted-foreground"
|
|
94
|
+
}),
|
|
95
|
+
d(A, {
|
|
96
|
+
modelValue: f.value,
|
|
97
|
+
"onUpdate:modelValue": t[0] || (t[0] = (l) => f.value = l),
|
|
98
|
+
placeholder: "메뉴 검색...",
|
|
99
|
+
class: "pl-8 h-7 text-xs"
|
|
100
|
+
}, null, 8, ["modelValue"])
|
|
101
|
+
])
|
|
102
|
+
])) : m("", !0),
|
|
103
|
+
a("nav", G, [
|
|
104
|
+
i.showFavorites && i.storageKey && h.value.length > 0 ? (s(), r(u, { key: 0 }, [
|
|
105
|
+
i.collapsed ? (s(), r(u, { key: 1 }, [
|
|
106
|
+
a("div", Z, [
|
|
107
|
+
d(v, {
|
|
108
|
+
name: "star",
|
|
109
|
+
size: "sm",
|
|
110
|
+
class: "text-yellow-500"
|
|
111
|
+
})
|
|
112
|
+
]),
|
|
113
|
+
(s(!0), r(u, null, _(h.value, (l) => (s(), k(L, {
|
|
114
|
+
key: "fav-c-" + l.id,
|
|
115
|
+
item: l,
|
|
116
|
+
onMenuClick: y
|
|
117
|
+
}, null, 8, ["item"]))), 128))
|
|
118
|
+
], 64)) : (s(), r(u, { key: 0 }, [
|
|
119
|
+
a("button", {
|
|
120
|
+
class: "flex items-center gap-1.5 w-full px-2 py-1.5 text-xs font-semibold text-yellow-600 cursor-pointer select-none transition-colors hover:text-yellow-700",
|
|
121
|
+
onClick: t[1] || (t[1] = (l) => x.value = !x.value)
|
|
122
|
+
}, [
|
|
123
|
+
d(v, {
|
|
124
|
+
name: "chevronRight",
|
|
125
|
+
size: "sm",
|
|
126
|
+
class: B(I(N)(
|
|
127
|
+
"flex-shrink-0 transition-transform duration-200",
|
|
128
|
+
x.value && "rotate-90"
|
|
129
|
+
))
|
|
130
|
+
}, null, 8, ["class"]),
|
|
131
|
+
d(v, {
|
|
132
|
+
name: "star",
|
|
133
|
+
size: "sm",
|
|
134
|
+
class: "flex-shrink-0 text-yellow-500"
|
|
135
|
+
}),
|
|
136
|
+
t[2] || (t[2] = a("span", { class: "flex-1 text-left" }, "즐겨찾기", -1)),
|
|
137
|
+
a("span", H, R(h.value.length), 1)
|
|
138
|
+
]),
|
|
139
|
+
a("div", {
|
|
140
|
+
class: B(I(N)(
|
|
141
|
+
"grid transition-[grid-template-rows] duration-200 ease-out",
|
|
142
|
+
x.value ? "grid-rows-[1fr]" : "grid-rows-[0fr]"
|
|
143
|
+
))
|
|
144
|
+
}, [
|
|
145
|
+
a("div", X, [
|
|
146
|
+
(s(!0), r(u, null, _(h.value, (l) => (s(), k(L, {
|
|
147
|
+
key: "fav-" + l.id,
|
|
148
|
+
item: l,
|
|
149
|
+
onMenuClick: y
|
|
150
|
+
}, null, 8, ["item"]))), 128))
|
|
151
|
+
])
|
|
152
|
+
], 2)
|
|
153
|
+
], 64)),
|
|
154
|
+
t[3] || (t[3] = a("div", { class: "h-px bg-border mx-2 my-1" }, null, -1))
|
|
155
|
+
], 64)) : m("", !0),
|
|
156
|
+
(s(!0), r(u, null, _(K.value, (l) => (s(), r(u, {
|
|
157
|
+
key: l.id
|
|
158
|
+
}, [
|
|
159
|
+
l.menuType === "F" ? (s(), k(Q, {
|
|
160
|
+
key: 0,
|
|
161
|
+
item: l,
|
|
162
|
+
onMenuClick: y
|
|
163
|
+
}, null, 8, ["item"])) : l.menuType === "L" ? (s(), k(L, {
|
|
164
|
+
key: 1,
|
|
165
|
+
item: l,
|
|
166
|
+
onMenuClick: y
|
|
167
|
+
}, null, 8, ["item"])) : m("", !0)
|
|
168
|
+
], 64))), 128)),
|
|
169
|
+
K.value.length === 0 && f.value.trim() ? (s(), r("div", ee, " 검색 결과가 없습니다. ")) : m("", !0)
|
|
170
|
+
]),
|
|
171
|
+
a("div", te, [
|
|
172
|
+
a("button", {
|
|
173
|
+
class: "flex items-center gap-2 w-full px-2 py-1 rounded-md text-xs text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors",
|
|
174
|
+
onClick: $
|
|
175
|
+
}, [
|
|
176
|
+
d(v, {
|
|
177
|
+
name: i.collapsed ? "panelLeftOpen" : "panelLeftClose",
|
|
178
|
+
size: "sm"
|
|
179
|
+
}, null, 8, ["name"]),
|
|
180
|
+
i.collapsed ? m("", !0) : (s(), r("span", le, "접기"))
|
|
181
|
+
])
|
|
182
|
+
])
|
|
183
|
+
], 4));
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
export {
|
|
187
|
+
ue as default
|
|
188
|
+
};
|
|
189
|
+
//# sourceMappingURL=JSidebar.vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JSidebar.vue.js","sources":["../../../../../src/components/organisms/JSidebar/JSidebar.vue"],"sourcesContent":["<script setup lang=\"ts\">\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("./
|
|
2
|
-
//# sourceMappingURL=
|
|
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 @@
|
|
|
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"}
|