@j-solution/components 1.6.1 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/README.md +8 -6
  2. package/assets/jwms-portal-frontend-CwxPfHfa.css +1 -0
  3. package/assets/styles/j-components.css +1 -1
  4. package/assets/styles/themes.css +107 -0
  5. package/components/atoms/JAvatar.vue.cjs +1 -1
  6. package/components/atoms/JAvatar.vue.cjs.map +1 -1
  7. package/components/atoms/JAvatar.vue.js +10 -7
  8. package/components/atoms/JAvatar.vue.js.map +1 -1
  9. package/components/atoms/JBadge.vue.cjs +1 -1
  10. package/components/atoms/JBadge.vue.cjs.map +1 -1
  11. package/components/atoms/JBadge.vue.js +7 -6
  12. package/components/atoms/JBadge.vue.js.map +1 -1
  13. package/components/atoms/JButton.vue.cjs +1 -1
  14. package/components/atoms/JButton.vue.cjs.map +1 -1
  15. package/components/atoms/JButton.vue.js +5 -5
  16. package/components/atoms/JButton.vue.js.map +1 -1
  17. package/components/atoms/JDatepicker.vue.cjs +1 -1
  18. package/components/atoms/JDatepicker.vue.cjs.map +1 -1
  19. package/components/atoms/JDatepicker.vue.js +10 -10
  20. package/components/atoms/JDatepicker.vue.js.map +1 -1
  21. package/components/atoms/JEditor.vue.cjs +1 -1
  22. package/components/atoms/JEditor.vue.js +1 -1
  23. package/components/atoms/JEditor.vue2.cjs +1 -1
  24. package/components/atoms/JEditor.vue2.cjs.map +1 -1
  25. package/components/atoms/JEditor.vue2.js +31 -17
  26. package/components/atoms/JEditor.vue2.js.map +1 -1
  27. package/components/atoms/JGrid.vue.cjs +1 -1
  28. package/components/atoms/JGrid.vue.js +2 -2
  29. package/components/atoms/JGrid.vue2.cjs +1 -1
  30. package/components/atoms/JGrid.vue2.cjs.map +1 -1
  31. package/components/atoms/JGrid.vue2.js +45 -33
  32. package/components/atoms/JGrid.vue2.js.map +1 -1
  33. package/components/atoms/JIcon.vue.cjs +1 -1
  34. package/components/atoms/JIcon.vue.cjs.map +1 -1
  35. package/components/atoms/JIcon.vue.js +14 -13
  36. package/components/atoms/JIcon.vue.js.map +1 -1
  37. package/components/atoms/JKbd.vue.cjs +1 -1
  38. package/components/atoms/JKbd.vue.cjs.map +1 -1
  39. package/components/atoms/JKbd.vue.js +13 -10
  40. package/components/atoms/JKbd.vue.js.map +1 -1
  41. package/components/atoms/JLabel.vue.cjs +1 -1
  42. package/components/atoms/JLabel.vue.cjs.map +1 -1
  43. package/components/atoms/JLabel.vue.js +4 -4
  44. package/components/atoms/JLabel.vue.js.map +1 -1
  45. package/components/atoms/JLink.vue.cjs +1 -1
  46. package/components/atoms/JLink.vue.cjs.map +1 -1
  47. package/components/atoms/JLink.vue.js +5 -5
  48. package/components/atoms/JLink.vue.js.map +1 -1
  49. package/components/atoms/JPreview.vue.cjs +1 -1
  50. package/components/atoms/JPreview.vue.js +2 -2
  51. package/components/atoms/JPreview.vue2.cjs +1 -1
  52. package/components/atoms/JPreview.vue2.cjs.map +1 -1
  53. package/components/atoms/JPreview.vue2.js +33 -20
  54. package/components/atoms/JPreview.vue2.js.map +1 -1
  55. package/components/atoms/JProgress.vue.cjs +1 -1
  56. package/components/atoms/JProgress.vue.cjs.map +1 -1
  57. package/components/atoms/JProgress.vue.js +15 -9
  58. package/components/atoms/JProgress.vue.js.map +1 -1
  59. package/components/atoms/JRadio.vue.cjs +1 -1
  60. package/components/atoms/JRadio.vue.cjs.map +1 -1
  61. package/components/atoms/JRadio.vue.js +1 -1
  62. package/components/atoms/JRadio.vue.js.map +1 -1
  63. package/components/atoms/JSearchCombo.vue.cjs +1 -1
  64. package/components/atoms/JSearchCombo.vue.cjs.map +1 -1
  65. package/components/atoms/JSearchCombo.vue.js +38 -37
  66. package/components/atoms/JSearchCombo.vue.js.map +1 -1
  67. package/components/atoms/JSpinner.vue.cjs +1 -1
  68. package/components/atoms/JSpinner.vue.cjs.map +1 -1
  69. package/components/atoms/JSpinner.vue.js +8 -7
  70. package/components/atoms/JSpinner.vue.js.map +1 -1
  71. package/components/atoms/JSplitter.vue.cjs +1 -1
  72. package/components/atoms/JSplitter.vue.cjs.map +1 -1
  73. package/components/atoms/JSplitter.vue.js +32 -27
  74. package/components/atoms/JSplitter.vue.js.map +1 -1
  75. package/components/atoms/JTooltip.vue.cjs +1 -1
  76. package/components/atoms/JTooltip.vue.cjs.map +1 -1
  77. package/components/atoms/JTooltip.vue.js +18 -15
  78. package/components/atoms/JTooltip.vue.js.map +1 -1
  79. package/components/examples/ExampleCrudPage.vue.cjs +1 -1
  80. package/components/examples/ExampleCrudPage.vue.cjs.map +1 -1
  81. package/components/examples/ExampleCrudPage.vue.js +162 -108
  82. package/components/examples/ExampleCrudPage.vue.js.map +1 -1
  83. package/components/examples/ExampleTabMappingPage.vue.cjs +1 -1
  84. package/components/examples/ExampleTabMappingPage.vue.cjs.map +1 -1
  85. package/components/examples/ExampleTabMappingPage.vue.js +162 -119
  86. package/components/examples/ExampleTabMappingPage.vue.js.map +1 -1
  87. package/components/molecules/JBreadcrumb.vue.cjs +1 -1
  88. package/components/molecules/JBreadcrumb.vue.cjs.map +1 -1
  89. package/components/molecules/JBreadcrumb.vue.js +3 -3
  90. package/components/molecules/JBreadcrumb.vue.js.map +1 -1
  91. package/components/molecules/JFormField.vue.cjs +1 -1
  92. package/components/molecules/JFormField.vue.cjs.map +1 -1
  93. package/components/molecules/JFormField.vue.js +26 -24
  94. package/components/molecules/JFormField.vue.js.map +1 -1
  95. package/components/molecules/JTabs.vue.cjs +1 -1
  96. package/components/molecules/JTabs.vue.js +1 -1
  97. package/components/molecules/JTabs.vue2.cjs +1 -1
  98. package/components/molecules/JTabs.vue2.cjs.map +1 -1
  99. package/components/molecules/JTabs.vue2.js +7 -7
  100. package/components/molecules/JTabs.vue2.js.map +1 -1
  101. package/components/molecules/JTitlebar.vue.cjs +1 -1
  102. package/components/molecules/JTitlebar.vue.cjs.map +1 -1
  103. package/components/molecules/JTitlebar.vue.js +35 -36
  104. package/components/molecules/JTitlebar.vue.js.map +1 -1
  105. package/components/organisms/JFilterBar.vue.cjs +1 -1
  106. package/components/organisms/JFilterBar.vue.cjs.map +1 -1
  107. package/components/organisms/JFilterBar.vue.js +5 -5
  108. package/components/organisms/JFilterBar.vue.js.map +1 -1
  109. package/components/organisms/JHeader.vue.cjs +1 -1
  110. package/components/organisms/JHeader.vue.cjs.map +1 -1
  111. package/components/organisms/JHeader.vue.js +25 -23
  112. package/components/organisms/JHeader.vue.js.map +1 -1
  113. package/components/organisms/JModal.vue.cjs +1 -1
  114. package/components/organisms/JModal.vue.cjs.map +1 -1
  115. package/components/organisms/JModal.vue.js +30 -27
  116. package/components/organisms/JModal.vue.js.map +1 -1
  117. package/components/organisms/JSidebarAdvanced.vue.cjs +1 -1
  118. package/components/organisms/JSidebarAdvanced.vue.js +7 -7
  119. package/components/organisms/JSidebarAdvanced.vue2.cjs +1 -1
  120. package/components/organisms/JSidebarAdvanced.vue2.cjs.map +1 -1
  121. package/components/organisms/JSidebarAdvanced.vue2.js +40 -40
  122. package/components/organisms/JSidebarAdvanced.vue2.js.map +1 -1
  123. package/components/organisms/JSidebarSimple/JDynamicMenuItem.vue.cjs +1 -1
  124. package/components/organisms/JSidebarSimple/JDynamicMenuItem.vue.cjs.map +1 -1
  125. package/components/organisms/JSidebarSimple/JDynamicMenuItem.vue.js +83 -63
  126. package/components/organisms/JSidebarSimple/JDynamicMenuItem.vue.js.map +1 -1
  127. package/components/organisms/JSidebarSimple.vue.cjs +1 -1
  128. package/components/organisms/JSidebarSimple.vue.js +2 -2
  129. package/components/organisms/JSidebarSimple.vue2.cjs +1 -1
  130. package/components/organisms/JSidebarSimple.vue2.cjs.map +1 -1
  131. package/components/organisms/JSidebarSimple.vue2.js +2 -2
  132. package/components/organisms/JSidebarSimple.vue2.js.map +1 -1
  133. package/components/shadcn/AccordionTrigger.vue.cjs +1 -1
  134. package/components/shadcn/AccordionTrigger.vue.cjs.map +1 -1
  135. package/components/shadcn/AccordionTrigger.vue.js +3 -3
  136. package/components/shadcn/AccordionTrigger.vue.js.map +1 -1
  137. package/components/shadcn/CardContent.vue.cjs +1 -1
  138. package/components/shadcn/CardContent.vue.cjs.map +1 -1
  139. package/components/shadcn/CardContent.vue.js +1 -1
  140. package/components/shadcn/CardContent.vue.js.map +1 -1
  141. package/components/shadcn/CardDescription.vue.cjs +1 -1
  142. package/components/shadcn/CardDescription.vue.cjs.map +1 -1
  143. package/components/shadcn/CardDescription.vue.js +1 -1
  144. package/components/shadcn/CardDescription.vue.js.map +1 -1
  145. package/components/shadcn/CardFooter.vue.cjs +1 -1
  146. package/components/shadcn/CardFooter.vue.cjs.map +1 -1
  147. package/components/shadcn/CardFooter.vue.js +7 -7
  148. package/components/shadcn/CardFooter.vue.js.map +1 -1
  149. package/components/shadcn/CardHeader.vue.cjs +1 -1
  150. package/components/shadcn/CardHeader.vue.cjs.map +1 -1
  151. package/components/shadcn/CardHeader.vue.js +8 -8
  152. package/components/shadcn/CardHeader.vue.js.map +1 -1
  153. package/components/shadcn/CardTitle.vue.cjs +1 -1
  154. package/components/shadcn/CardTitle.vue.cjs.map +1 -1
  155. package/components/shadcn/CardTitle.vue.js +5 -5
  156. package/components/shadcn/CardTitle.vue.js.map +1 -1
  157. package/components/shadcn/Input.vue.cjs +1 -1
  158. package/components/shadcn/Input.vue.cjs.map +1 -1
  159. package/components/shadcn/Input.vue.js +1 -1
  160. package/components/shadcn/Input.vue.js.map +1 -1
  161. package/components/shadcn/SelectTrigger.vue.cjs +1 -1
  162. package/components/shadcn/SelectTrigger.vue.cjs.map +1 -1
  163. package/components/shadcn/SelectTrigger.vue.js +2 -2
  164. package/components/shadcn/SelectTrigger.vue.js.map +1 -1
  165. package/components/shadcn/Switch.vue.cjs +1 -1
  166. package/components/shadcn/Switch.vue.cjs.map +1 -1
  167. package/components/shadcn/Switch.vue.js +2 -2
  168. package/components/shadcn/Switch.vue.js.map +1 -1
  169. package/components/shadcn/TabsList.vue.cjs +1 -1
  170. package/components/shadcn/TabsList.vue.cjs.map +1 -1
  171. package/components/shadcn/TabsList.vue.js +1 -1
  172. package/components/shadcn/TabsList.vue.js.map +1 -1
  173. package/components/shadcn/TabsTrigger.vue.cjs +1 -1
  174. package/components/shadcn/TabsTrigger.vue.cjs.map +1 -1
  175. package/components/shadcn/TabsTrigger.vue.js +4 -4
  176. package/components/shadcn/TabsTrigger.vue.js.map +1 -1
  177. package/components/shadcn/Textarea.vue.cjs +1 -1
  178. package/components/shadcn/Textarea.vue.cjs.map +1 -1
  179. package/components/shadcn/Textarea.vue.js +2 -2
  180. package/components/shadcn/Textarea.vue.js.map +1 -1
  181. package/components/shadcn/index.cjs +1 -1
  182. package/components/shadcn/index.cjs.map +1 -1
  183. package/components/shadcn/index.js +8 -7
  184. package/components/shadcn/index.js.map +1 -1
  185. package/package.json +1 -1
  186. package/types/index.d.ts +131 -15
  187. package/assets/jwms-portal-frontend-DntSIcYt.css +0 -1
@@ -1,4 +1,4 @@
1
- import { defineComponent as w, computed as _, createElementBlock as c, openBlock as l, normalizeClass as o, unref as t, createElementVNode as m, createCommentVNode as i, renderSlot as z, createBlock as n, createVNode as y, withCtx as f, toDisplayString as g, Fragment as I, renderList as $ } from "vue";
1
+ import { defineComponent as w, computed as _, createElementBlock as c, openBlock as o, normalizeClass as r, unref as t, createElementVNode as m, createCommentVNode as l, renderSlot as z, createBlock as n, createVNode as y, withCtx as f, toDisplayString as g, Fragment as I, renderList as $ } from "vue";
2
2
  import j from "../atoms/JButton.vue.js";
3
3
  import "../shadcn/index.js";
4
4
  import "lucide-vue-next";
@@ -25,7 +25,7 @@ import "ag-grid-enterprise";
25
25
  /* empty css */
26
26
  /* empty css */
27
27
  import "vue-sonner";
28
- const S = { class: "flex items-center gap-3 flex-1" }, N = { class: "flex items-center gap-2" }, T = { class: "p-2" }, V = { class: "text-sm text-muted-foreground whitespace-normal break-words" }, H = {
28
+ const S = { class: "flex items-center gap-3 flex-1" }, N = { class: "flex items-center gap-2" }, T = { class: "p-2" }, V = { class: "text-xs text-muted-foreground whitespace-normal break-words" }, H = {
29
29
  key: 0,
30
30
  class: "flex items-center gap-2"
31
31
  }, L = { key: 1 }, de = /* @__PURE__ */ w({
@@ -39,65 +39,64 @@ const S = { class: "flex items-center gap-3 flex-1" }, N = { class: "flex items-
39
39
  buttons: { default: () => [] }
40
40
  },
41
41
  emits: ["buttonClick", "help"],
42
- setup(s, { emit: h }) {
43
- const C = s, p = {
42
+ setup(s, { emit: C }) {
43
+ const h = s, p = {
44
44
  default: {
45
45
  // 기본: 흰 배경 + 얇은 보더 (가장 일반적)
46
- class: "flex items-center justify-between w-full h-14 px-4 border-b border-border bg-background",
46
+ class: "flex items-center justify-between w-full h-8 px-3 border-b border-border bg-background",
47
47
  iconClass: "text-primary",
48
- titleClass: "text-foreground",
48
+ titleClass: "text-foreground text-md",
49
49
  infoIconClass: "text-muted-foreground hover:text-primary"
50
50
  },
51
51
  primary: {
52
52
  // 강조: 밝은 파란 배경 + 흰 텍스트 (명확한 강조)
53
53
  // text-white를 제거하여 버튼이 색상 상속을 받지 않도록 함
54
- class: "flex items-center justify-between w-full h-14 px-4 border-b border-blue-400/30 bg-blue-500",
54
+ class: "flex items-center justify-between w-full h-8 px-3 border-b border-blue-400/30 bg-blue-500",
55
55
  iconClass: "text-white",
56
- titleClass: "text-white font-semibold",
56
+ titleClass: "text-white font-semibold text-md",
57
57
  infoIconClass: "text-white/80 hover:text-white"
58
58
  },
59
59
  accent: {
60
60
  // 액센트: 연한 파란 배경 + 진한 파란 텍스트 (부드러운 강조)
61
61
  // text-blue-700을 제거하여 버튼이 색상 상속을 받지 않도록 함
62
- class: "flex items-center justify-between w-full h-14 px-4 border-b border-blue-200 bg-blue-50",
62
+ class: "flex items-center justify-between w-full h-8 px-3 border-b border-blue-200 bg-blue-50",
63
63
  iconClass: "text-blue-600",
64
- titleClass: "text-blue-700 font-semibold",
64
+ titleClass: "text-blue-700 font-semibold text-md",
65
65
  infoIconClass: "text-blue-600/70 hover:text-blue-700"
66
66
  },
67
67
  neutral: {
68
68
  // 중립: 회색 배경 + 회색 텍스트 (확실한 구분)
69
69
  // text-gray-700을 제거하여 버튼이 색상 상속을 받지 않도록 함
70
- class: "flex items-center justify-between w-full h-14 px-4 border-b border-gray-300 bg-gray-100",
70
+ class: "flex items-center justify-between w-full h-8 px-3 border-b border-gray-300 bg-gray-100",
71
71
  iconClass: "text-gray-600",
72
- titleClass: "text-gray-700",
72
+ titleClass: "text-gray-700 text-md",
73
73
  infoIconClass: "text-gray-500 hover:text-gray-700"
74
74
  },
75
75
  elevated: {
76
76
  // 강조: 흰 배경 + 그림자 + 보더 (깊이감 있는 구분)
77
- class: "flex items-center justify-between w-full h-14 px-4 border-b border-border bg-background shadow-md",
77
+ class: "flex items-center justify-between w-full h-8 px-3 border-b border-border bg-background shadow-md",
78
78
  iconClass: "text-primary",
79
- titleClass: "text-foreground font-semibold",
79
+ titleClass: "text-foreground font-semibold text-md",
80
80
  infoIconClass: "text-muted-foreground hover:text-primary"
81
81
  }
82
- }, r = _(() => p[C.styletype] ?? p.default), x = h, k = (a) => {
82
+ }, i = _(() => p[h.styletype] ?? p.default), x = C, k = (a) => {
83
83
  a.onClick?.(), x("buttonClick", a);
84
84
  };
85
- return (a, b) => (l(), c("div", {
86
- class: o(t(d)(r.value.class))
85
+ return (a, b) => (o(), c("div", {
86
+ class: r(t(d)(i.value.class))
87
87
  }, [
88
88
  m("div", S, [
89
- s.icon ? (l(), n(t(u), {
89
+ s.icon ? (o(), n(t(u), {
90
90
  key: 0,
91
91
  name: s.icon,
92
- size: "md",
93
- class: o(r.value.iconClass)
94
- }, null, 8, ["name", "class"])) : i("", !0),
92
+ class: r(i.value.iconClass)
93
+ }, null, 8, ["name", "class"])) : l("", !0),
95
94
  m("div", N, [
96
95
  y(t(B), {
97
96
  text: s.title || "",
98
- class: o(t(d)("text-lg font-semibold", r.value.titleClass))
97
+ class: r(t(d)("font-semibold", i.value.titleClass))
99
98
  }, null, 8, ["text", "class"]),
100
- s.description ? (l(), n(t(E), {
99
+ s.description ? (o(), n(t(E), {
101
100
  key: 0,
102
101
  position: "bottom",
103
102
  align: "center",
@@ -107,7 +106,7 @@ const S = { class: "flex items-center gap-3 flex-1" }, N = { class: "flex items-
107
106
  y(t(u), {
108
107
  name: "info",
109
108
  size: "sm",
110
- class: o(t(d)("cursor-help transition-colors inline-flex", r.value.infoIconClass))
109
+ class: r(t(d)("cursor-help transition-colors inline-flex", i.value.infoIconClass))
111
110
  }, null, 8, ["class"])
112
111
  ]),
113
112
  default: f(() => [
@@ -116,38 +115,38 @@ const S = { class: "flex items-center gap-3 flex-1" }, N = { class: "flex items-
116
115
  ])
117
116
  ]),
118
117
  _: 1
119
- })) : i("", !0),
120
- s.showHelp ? (l(), n(t(u), {
118
+ })) : l("", !0),
119
+ s.showHelp ? (o(), n(t(u), {
121
120
  key: 1,
122
121
  name: "circleQuestionMark",
123
122
  size: "sm",
124
- class: o(t(d)("cursor-pointer transition-colors inline-flex", r.value.infoIconClass)),
123
+ class: r(t(d)("cursor-pointer transition-colors inline-flex", i.value.infoIconClass)),
125
124
  onClick: b[0] || (b[0] = (e) => x("help"))
126
- }, null, 8, ["class"])) : i("", !0)
125
+ }, null, 8, ["class"])) : l("", !0)
127
126
  ])
128
127
  ]),
129
- s.buttons && s.buttons.length > 0 ? (l(), c("div", H, [
130
- (l(!0), c(I, null, $(s.buttons, (e, v) => (l(), n(t(j), {
128
+ s.buttons && s.buttons.length > 0 ? (o(), c("div", H, [
129
+ (o(!0), c(I, null, $(s.buttons, (e, v) => (o(), n(t(j), {
131
130
  key: v,
132
131
  variant: e.variant,
133
132
  styletype: e.styletype,
134
133
  disabled: e.disabled,
135
134
  loading: e.loading,
136
- class: o(e.size === "sm" ? "h-8" : e.size === "lg" ? "h-11" : "h-9"),
135
+ size: e.size,
137
136
  onClick: (D) => k(e)
138
137
  }, {
139
138
  default: f(() => [
140
- e.icon ? (l(), n(t(u), {
139
+ e.icon ? (o(), n(t(u), {
141
140
  key: 0,
142
141
  name: e.icon,
143
142
  size: "sm",
144
143
  class: "mr-1.5"
145
- }, null, 8, ["name"])) : i("", !0),
146
- e.text ? (l(), c("span", L, g(e.text), 1)) : i("", !0)
144
+ }, null, 8, ["name"])) : l("", !0),
145
+ e.text ? (o(), c("span", L, g(e.text), 1)) : l("", !0)
147
146
  ]),
148
147
  _: 2
149
- }, 1032, ["variant", "styletype", "disabled", "loading", "class", "onClick"]))), 128))
150
- ])) : i("", !0),
148
+ }, 1032, ["variant", "styletype", "disabled", "loading", "size", "onClick"]))), 128))
149
+ ])) : l("", !0),
151
150
  z(a.$slots, "buttons")
152
151
  ], 2));
153
152
  }
@@ -1 +1 @@
1
- {"version":3,"file":"JTitlebar.vue.js","sources":["../../../../src/components/molecules/JTitlebar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from \"vue\"\nimport { JIcon, JLabel, JPopover, JButton } from '@/components/atoms'\nimport type { ButtonVariants } from '@/components/shadcn'\nimport { cn } from \"@/lib/utils\"\n\nexport type TitlebarButton = {\n /** 버튼 아이콘 */\n icon?: string\n /** 버튼 텍스트 */\n text?: string\n /** 버튼 클릭 핸들러 */\n onClick?: () => void\n /** 버튼 variant */\n variant?: ButtonVariants['variant']\n /** 버튼 스타일 타입 */\n styletype?: 'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'outline' | 'ghost' | 'link' | 'sm' | 'lg' | 'icon'\n /** 버튼 size */\n size?: 'sm' | 'md' | 'lg'\n /** 버튼 비활성화 */\n disabled?: boolean\n /** 버튼 로딩 상태 */\n loading?: boolean\n}\n\ntype StyleType =\n | 'default' // 기본 스타일: 일반 페이지용 (흰 배경, 얇은 보더)\n | 'primary' // 강조 스타일: 주요 기능용 (밝은 파란 배경, 흰 텍스트)\n | 'accent' // 액센트 스타일: 부드러운 강조용 (연한 파란 배경, 진한 텍스트)\n | 'neutral' // 중립 스타일: 서브 페이지용 (회색 배경, 회색 텍스트)\n | 'elevated' // 강조 스타일: 깊이감 있는 구분 (흰 배경, 그림자, 보더)\n\nconst props = withDefaults(\n defineProps<{\n /** Titlebar 스타일 타입 */\n styletype?: StyleType\n /** 프로그램 아이콘 */\n icon?: string\n /** 프로그램명 */\n title?: string\n /** 프로그램 설명 (Popover에 표시) */\n description?: string\n /** 도움말 아이콘(?) 표시 여부 — 클릭 시 help 이벤트 emit */\n showHelp?: boolean\n /** 메인 버튼 목록 */\n buttons?: TitlebarButton[]\n }>(),\n {\n styletype: 'default',\n buttons: () => [],\n }\n)\n\n/**\n * styletype -> class 매핑 (배경, 보더, 그림자 등)\n */\nconst STYLE_PRESETS: Record<StyleType, { \n class: string\n iconClass: string // 아이콘 색상 클래스\n titleClass: string // 제목 텍스트 색상 클래스\n infoIconClass: string // 정보 아이콘 색상 클래스\n}> = {\n default: {\n // 기본: 흰 배경 + 얇은 보더 (가장 일반적)\n class: 'flex items-center justify-between w-full h-14 px-4 border-b border-border bg-background',\n iconClass: 'text-primary',\n titleClass: 'text-foreground',\n infoIconClass: 'text-muted-foreground hover:text-primary',\n },\n primary: {\n // 강조: 밝은 파란 배경 + 흰 텍스트 (명확한 강조)\n // text-white를 제거하여 버튼이 색상 상속을 받지 않도록 함\n class: 'flex items-center justify-between w-full h-14 px-4 border-b border-blue-400/30 bg-blue-500',\n iconClass: 'text-white',\n titleClass: 'text-white font-semibold',\n infoIconClass: 'text-white/80 hover:text-white',\n },\n accent: {\n // 액센트: 연한 파란 배경 + 진한 파란 텍스트 (부드러운 강조)\n // text-blue-700을 제거하여 버튼이 색상 상속을 받지 않도록 함\n class: 'flex items-center justify-between w-full h-14 px-4 border-b border-blue-200 bg-blue-50',\n iconClass: 'text-blue-600',\n titleClass: 'text-blue-700 font-semibold',\n infoIconClass: 'text-blue-600/70 hover:text-blue-700',\n },\n neutral: {\n // 중립: 회색 배경 + 회색 텍스트 (확실한 구분)\n // text-gray-700을 제거하여 버튼이 색상 상속을 받지 않도록 함\n class: 'flex items-center justify-between w-full h-14 px-4 border-b border-gray-300 bg-gray-100',\n iconClass: 'text-gray-600',\n titleClass: 'text-gray-700',\n infoIconClass: 'text-gray-500 hover:text-gray-700',\n },\n elevated: {\n // 강조: 흰 배경 + 그림자 + 보더 (깊이감 있는 구분)\n class: 'flex items-center justify-between w-full h-14 px-4 border-b border-border bg-background shadow-md',\n iconClass: 'text-primary',\n titleClass: 'text-foreground font-semibold',\n infoIconClass: 'text-muted-foreground hover:text-primary',\n },\n}\n\nconst preset = computed(() => {\n return STYLE_PRESETS[props.styletype] ?? STYLE_PRESETS.default\n})\n\nconst emit = defineEmits<{\n /** 버튼 클릭 이벤트 */\n buttonClick: [button: TitlebarButton]\n /** 도움말 아이콘 클릭 이벤트 */\n help: []\n}>()\n\nconst handleButtonClick = (button: TitlebarButton) => {\n // 버튼 클릭 핸들러 패턴:\n // 1. button.onClick이 있으면 실행 (인라인 핸들러)\n // 2. 항상 emit('buttonClick', button) 실행 (부모 컴포넌트로 전달)\n // 주의: onClick과 emit이 모두 실행되므로 중복 처리 가능성 있음\n button.onClick?.()\n emit('buttonClick', button)\n}\n</script>\n\n<template>\n <div :class=\"cn(preset.class)\">\n <!-- 왼쪽: 아이콘 + 프로그램명 -->\n <div class=\"flex items-center gap-3 flex-1\">\n <!-- 아이콘 -->\n <JIcon \n v-if=\"icon\" \n :name=\"icon\" \n size=\"md\" \n :class=\"preset.iconClass\"\n />\n \n <!-- 프로그램명 + 정보 아이콘 + Popover -->\n <div class=\"flex items-center gap-2\">\n <!-- 프로그램명 -->\n <JLabel \n :text=\"title || ''\" \n :class=\"cn('text-lg font-semibold', preset.titleClass)\"\n />\n \n <!-- 정보 아이콘 (description이 있을 때만 표시) -->\n <JPopover \n v-if=\"description\" \n position=\"bottom\"\n align=\"center\"\n :side-offset=\"8\"\n >\n <template #trigger>\n <JIcon \n name=\"info\" \n size=\"sm\" \n :class=\"cn('cursor-help transition-colors inline-flex', preset.infoIconClass)\"\n />\n </template>\n <div class=\"p-2\">\n <p class=\"text-sm text-muted-foreground whitespace-normal break-words\">{{ description }}</p>\n </div>\n </JPopover>\n\n <!-- 도움말 아이콘 (showHelp일 때 표시, 클릭 시 help 이벤트) -->\n <JIcon\n v-if=\"showHelp\"\n name=\"circleQuestionMark\"\n size=\"sm\"\n :class=\"cn('cursor-pointer transition-colors inline-flex', preset.infoIconClass)\"\n @click=\"emit('help')\"\n />\n </div>\n </div>\n\n <!-- 오른쪽: 메인 버튼들 -->\n <!-- buttons prop으로 정의된 버튼들 (v-if로 조건부 렌더링) -->\n <div v-if=\"buttons && buttons.length > 0\" class=\"flex items-center gap-2\">\n <JButton\n v-for=\"(button, index) in buttons\"\n :key=\"index\"\n :variant=\"button.variant\"\n :styletype=\"button.styletype\"\n :disabled=\"button.disabled\"\n :loading=\"button.loading\"\n :class=\"button.size === 'sm' ? 'h-8' : button.size === 'lg' ? 'h-11' : 'h-9'\"\n @click=\"handleButtonClick(button)\"\n >\n <JIcon v-if=\"button.icon\" :name=\"button.icon\" size=\"sm\" class=\"mr-1.5\" />\n <span v-if=\"button.text\">{{ button.text }}</span>\n </JButton>\n </div>\n\n <!-- 버튼 슬롯 레이아웃 설명:\n - buttons prop과 buttons slot이 모두 제공되면 함께 표시됨\n - buttons prop: 구조화된 버튼 정의 (JButton 컴포넌트 사용)\n - buttons slot: 완전한 커스텀 버튼 제어 가능\n - 레이아웃: buttons prop이 먼저 렌더링되고, 그 다음 slot이 렌더링됨 -->\n <slot name=\"buttons\" />\n </div>\n</template>\n\n"],"names":["props","__props","STYLE_PRESETS","preset","computed","emit","__emit","handleButtonClick","button","_createElementBlock","_normalizeClass","_unref","cn","_createElementVNode","_hoisted_1","_createBlock","JIcon","_hoisted_2","_createVNode","JLabel","JPopover","_hoisted_3","_hoisted_4","_toDisplayString","_openBlock","_hoisted_5","_Fragment","_renderList","index","JButton","$event","_hoisted_6","_renderSlot","_ctx"],"mappings":"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"}
1
+ {"version":3,"file":"JTitlebar.vue.js","sources":["../../../../src/components/molecules/JTitlebar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from \"vue\"\nimport { JIcon, JLabel, JPopover, JButton } from '@/components/atoms'\nimport type { ButtonVariants } from '@/components/shadcn'\nimport { cn } from \"@/lib/utils\"\n\nexport type TitlebarButton = {\n /** 버튼 아이콘 */\n icon?: string\n /** 버튼 텍스트 */\n text?: string\n /** 버튼 클릭 핸들러 */\n onClick?: () => void\n /** 버튼 variant */\n variant?: ButtonVariants['variant']\n /** 버튼 스타일 타입 */\n styletype?: 'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'outline' | 'ghost' | 'link' | 'sm' | 'lg' | 'icon'\n /** 버튼 size */\n size?: 'sm' | 'md' | 'lg'\n /** 버튼 비활성화 */\n disabled?: boolean\n /** 버튼 로딩 상태 */\n loading?: boolean\n}\n\ntype StyleType =\n | 'default' // 기본 스타일: 일반 페이지용 (흰 배경, 얇은 보더)\n | 'primary' // 강조 스타일: 주요 기능용 (밝은 파란 배경, 흰 텍스트)\n | 'accent' // 액센트 스타일: 부드러운 강조용 (연한 파란 배경, 진한 텍스트)\n | 'neutral' // 중립 스타일: 서브 페이지용 (회색 배경, 회색 텍스트)\n | 'elevated' // 강조 스타일: 깊이감 있는 구분 (흰 배경, 그림자, 보더)\n\nconst props = withDefaults(\n defineProps<{\n /** Titlebar 스타일 타입 */\n styletype?: StyleType\n /** 프로그램 아이콘 */\n icon?: string\n /** 프로그램명 */\n title?: string\n /** 프로그램 설명 (Popover에 표시) */\n description?: string\n /** 도움말 아이콘(?) 표시 여부 — 클릭 시 help 이벤트 emit */\n showHelp?: boolean\n /** 메인 버튼 목록 */\n buttons?: TitlebarButton[]\n }>(),\n {\n styletype: 'default',\n buttons: () => [],\n }\n)\n\n/**\n * styletype -> class 매핑 (배경, 보더, 그림자 등)\n */\nconst STYLE_PRESETS: Record<StyleType, { \n class: string\n iconClass: string // 아이콘 색상 클래스\n titleClass: string // 제목 텍스트 색상 클래스\n infoIconClass: string // 정보 아이콘 색상 클래스\n}> = {\n default: {\n // 기본: 흰 배경 + 얇은 보더 (가장 일반적)\n class: 'flex items-center justify-between w-full h-8 px-3 border-b border-border bg-background',\n iconClass: 'text-primary',\n titleClass: 'text-foreground text-md',\n infoIconClass: 'text-muted-foreground hover:text-primary',\n },\n primary: {\n // 강조: 밝은 파란 배경 + 흰 텍스트 (명확한 강조)\n // text-white를 제거하여 버튼이 색상 상속을 받지 않도록 함\n class: 'flex items-center justify-between w-full h-8 px-3 border-b border-blue-400/30 bg-blue-500',\n iconClass: 'text-white',\n titleClass: 'text-white font-semibold text-md',\n infoIconClass: 'text-white/80 hover:text-white',\n },\n accent: {\n // 액센트: 연한 파란 배경 + 진한 파란 텍스트 (부드러운 강조)\n // text-blue-700을 제거하여 버튼이 색상 상속을 받지 않도록 함\n class: 'flex items-center justify-between w-full h-8 px-3 border-b border-blue-200 bg-blue-50',\n iconClass: 'text-blue-600',\n titleClass: 'text-blue-700 font-semibold text-md',\n infoIconClass: 'text-blue-600/70 hover:text-blue-700',\n },\n neutral: {\n // 중립: 회색 배경 + 회색 텍스트 (확실한 구분)\n // text-gray-700을 제거하여 버튼이 색상 상속을 받지 않도록 함\n class: 'flex items-center justify-between w-full h-8 px-3 border-b border-gray-300 bg-gray-100',\n iconClass: 'text-gray-600',\n titleClass: 'text-gray-700 text-md',\n infoIconClass: 'text-gray-500 hover:text-gray-700',\n },\n elevated: {\n // 강조: 흰 배경 + 그림자 + 보더 (깊이감 있는 구분)\n class: 'flex items-center justify-between w-full h-8 px-3 border-b border-border bg-background shadow-md',\n iconClass: 'text-primary',\n titleClass: 'text-foreground font-semibold text-md',\n infoIconClass: 'text-muted-foreground hover:text-primary',\n },\n}\n\nconst preset = computed(() => {\n return STYLE_PRESETS[props.styletype] ?? STYLE_PRESETS.default\n})\n\nconst emit = defineEmits<{\n /** 버튼 클릭 이벤트 */\n buttonClick: [button: TitlebarButton]\n /** 도움말 아이콘 클릭 이벤트 */\n help: []\n}>()\n\nconst handleButtonClick = (button: TitlebarButton) => {\n // 버튼 클릭 핸들러 패턴:\n // 1. button.onClick이 있으면 실행 (인라인 핸들러)\n // 2. 항상 emit('buttonClick', button) 실행 (부모 컴포넌트로 전달)\n // 주의: onClick과 emit이 모두 실행되므로 중복 처리 가능성 있음\n button.onClick?.()\n emit('buttonClick', button)\n}\n</script>\n\n<template>\n <div :class=\"cn(preset.class)\">\n <!-- 왼쪽: 아이콘 + 프로그램명 -->\n <div class=\"flex items-center gap-3 flex-1\">\n <!-- 아이콘 -->\n <JIcon \n v-if=\"icon\" \n :name=\"icon\" \n :class=\"preset.iconClass\"\n />\n \n <!-- 프로그램명 + 정보 아이콘 + Popover -->\n <div class=\"flex items-center gap-2\">\n <!-- 프로그램명 -->\n <JLabel \n :text=\"title || ''\" \n :class=\"cn('font-semibold', preset.titleClass)\"\n />\n \n <!-- 정보 아이콘 (description이 있을 때만 표시) -->\n <JPopover \n v-if=\"description\" \n position=\"bottom\"\n align=\"center\"\n :side-offset=\"8\"\n >\n <template #trigger>\n <JIcon \n name=\"info\" \n size=\"sm\" \n :class=\"cn('cursor-help transition-colors inline-flex', preset.infoIconClass)\"\n />\n </template>\n <div class=\"p-2\">\n <p class=\"text-xs text-muted-foreground whitespace-normal break-words\">{{ description }}</p>\n </div>\n </JPopover>\n\n <!-- 도움말 아이콘 (showHelp일 때 표시, 클릭 시 help 이벤트) -->\n <JIcon\n v-if=\"showHelp\"\n name=\"circleQuestionMark\"\n size=\"sm\"\n :class=\"cn('cursor-pointer transition-colors inline-flex', preset.infoIconClass)\"\n @click=\"emit('help')\"\n />\n </div>\n </div>\n\n <!-- 오른쪽: 메인 버튼들 -->\n <!-- buttons prop으로 정의된 버튼들 (v-if로 조건부 렌더링) -->\n <div v-if=\"buttons && buttons.length > 0\" class=\"flex items-center gap-2\">\n <JButton\n v-for=\"(button, index) in buttons\"\n :key=\"index\"\n :variant=\"button.variant\"\n :styletype=\"button.styletype\"\n :disabled=\"button.disabled\"\n :loading=\"button.loading\"\n :size=\"button.size\"\n @click=\"handleButtonClick(button)\"\n >\n <JIcon v-if=\"button.icon\" :name=\"button.icon\" size=\"sm\" class=\"mr-1.5\" />\n <span v-if=\"button.text\">{{ button.text }}</span>\n </JButton>\n </div>\n\n <!-- 버튼 슬롯 레이아웃 설명:\n - buttons prop과 buttons slot이 모두 제공되면 함께 표시됨\n - buttons prop: 구조화된 버튼 정의 (JButton 컴포넌트 사용)\n - buttons slot: 완전한 커스텀 버튼 제어 가능\n - 레이아웃: buttons prop이 먼저 렌더링되고, 그 다음 slot이 렌더링됨 -->\n <slot name=\"buttons\" />\n </div>\n</template>\n\n"],"names":["props","__props","STYLE_PRESETS","preset","computed","emit","__emit","handleButtonClick","button","_createElementBlock","_normalizeClass","_unref","cn","_createElementVNode","_hoisted_1","_createBlock","JIcon","_hoisted_2","_createVNode","JLabel","JPopover","_hoisted_3","_hoisted_4","_toDisplayString","_openBlock","_hoisted_5","_Fragment","_renderList","index","JButton","$event","_hoisted_6","_renderSlot","_ctx"],"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"),d=require("lucide-vue-next"),_=require("../atoms/JBadge.vue.cjs"),f=require("../atoms/JButton.vue.cjs"),B=require("../atoms/JLabel.vue.cjs"),k={class:"w-full rounded-lg border bg-card text-card-foreground"},v={class:"flex items-center justify-between px-4 py-2"},x={class:"flex items-center gap-2"},b={key:2,class:"flex items-center gap-1 flex-wrap"},w={class:"text-muted-foreground"},C=["onClick"],N={class:"flex items-center gap-2"},E={class:"px-4 pb-4"},S=e.defineComponent({__name:"JFilterBar",props:{title:{},collapsed:{type:Boolean,default:!1},collapsible:{type:Boolean,default:!0},filterValues:{default:()=>({})},filterDisplay:{default:()=>({})},showResetButton:{type:Boolean,default:!1},showSearchButton:{type:Boolean,default:!1},resetButtonText:{default:"초기화"},searchButtonText:{default:"조회"}},emits:["update:collapsed","update:filterValues","search","reset"],setup(r,{emit:p}){const s=r,n=p,c=e.computed(()=>s.collapsible?!s.collapsed:!0);function m(t){return!!(t==null||typeof t=="string"&&t.trim()===""||Array.isArray(t)&&t.length===0)}const u=e.computed(()=>{const t=[];for(const[l,o]of Object.entries(s.filterDisplay)){const a=s.filterValues[l];if(m(a))continue;const i=o.displayValue?o.displayValue(a):String(a);i.trim()!==""&&t.push({key:l,label:o.label,value:i})}return t});function y(){n("update:collapsed",!s.collapsed)}function h(){const t={};for(const l of Object.keys(s.filterValues)){const o=s.filterValues[l];typeof o=="string"?t[l]="":Array.isArray(o)?t[l]=[]:t[l]=null}n("update:filterValues",t),n("reset")}function g(){n("search")}function V(t){const l={...s.filterValues},o=l[t];typeof o=="string"?l[t]="":Array.isArray(o)?l[t]=[]:l[t]=null,n("update:filterValues",l)}return(t,l)=>(e.openBlock(),e.createElementBlock("div",k,[e.createElementVNode("div",v,[e.createElementVNode("div",x,[r.collapsible?(e.openBlock(),e.createElementBlock("button",{key:0,type:"button",class:"flex items-center justify-center h-7 w-7 rounded hover:bg-accent hover:text-accent-foreground transition-colors",onClick:y},[e.createVNode(e.unref(d.ChevronDown),{class:e.normalizeClass(["h-4 w-4 transition-transform",c.value?"rotate-0":"-rotate-90"])},null,8,["class"])])):e.createCommentVNode("",!0),r.title?(e.openBlock(),e.createBlock(B.default,{key:1,text:r.title,class:"text-sm font-semibold text-foreground"},null,8,["text"])):e.createCommentVNode("",!0),u.value.length>0?(e.openBlock(),e.createElementBlock("div",b,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(u.value,o=>(e.openBlock(),e.createBlock(_.default,{key:o.key,variant:"secondary",size:"sm",class:"flex items-center gap-1 cursor-default"},{default:e.withCtx(()=>[e.createElementVNode("span",w,e.toDisplayString(o.label)+":",1),e.createElementVNode("span",null,e.toDisplayString(o.value),1),e.createElementVNode("button",{type:"button",class:"ml-0.5 rounded-full hover:bg-gray-300 p-0.5 transition-colors",onClick:e.withModifiers(a=>V(o.key),["stop"])},[e.createVNode(e.unref(d.X),{class:"h-3 w-3"})],8,C)]),_:2},1024))),128))])):e.createCommentVNode("",!0)]),e.createElementVNode("div",N,[e.renderSlot(t.$slots,"actions"),r.showResetButton?(e.openBlock(),e.createBlock(f.default,{key:0,variant:"secondary",size:"sm",onClick:h},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(r.resetButtonText),1)]),_:1})):e.createCommentVNode("",!0),r.showSearchButton?(e.openBlock(),e.createBlock(f.default,{key:1,styletype:"primary",size:"sm",onClick:g},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(r.searchButtonText),1)]),_:1})):e.createCommentVNode("",!0)])]),e.withDirectives(e.createElementVNode("div",E,[e.renderSlot(t.$slots,"filters")],512),[[e.vShow,c.value]])]))}});exports.default=S;
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue"),d=require("lucide-vue-next"),_=require("../atoms/JBadge.vue.cjs"),f=require("../atoms/JButton.vue.cjs"),B=require("../atoms/JLabel.vue.cjs"),k={class:"w-full rounded-lg border bg-card text-card-foreground"},v={class:"flex items-center justify-between px-3 py-1.5"},x={class:"flex items-center gap-2"},b={key:2,class:"flex items-center gap-1 flex-wrap"},w={class:"text-muted-foreground"},C=["onClick"],N={class:"flex items-center gap-2"},E={class:"px-3 pb-3"},S=e.defineComponent({__name:"JFilterBar",props:{title:{},collapsed:{type:Boolean,default:!0},collapsible:{type:Boolean,default:!0},filterValues:{default:()=>({})},filterDisplay:{default:()=>({})},showResetButton:{type:Boolean,default:!1},showSearchButton:{type:Boolean,default:!1},resetButtonText:{default:"초기화"},searchButtonText:{default:"조회"}},emits:["update:collapsed","update:filterValues","search","reset"],setup(r,{emit:p}){const s=r,n=p,c=e.computed(()=>s.collapsible?!s.collapsed:!0);function m(t){return!!(t==null||typeof t=="string"&&t.trim()===""||Array.isArray(t)&&t.length===0)}const u=e.computed(()=>{const t=[];for(const[l,o]of Object.entries(s.filterDisplay)){const a=s.filterValues[l];if(m(a))continue;const i=o.displayValue?o.displayValue(a):String(a);i.trim()!==""&&t.push({key:l,label:o.label,value:i})}return t});function y(){n("update:collapsed",!s.collapsed)}function h(){const t={};for(const l of Object.keys(s.filterValues)){const o=s.filterValues[l];typeof o=="string"?t[l]="":Array.isArray(o)?t[l]=[]:t[l]=null}n("update:filterValues",t),n("reset")}function g(){n("search")}function V(t){const l={...s.filterValues},o=l[t];typeof o=="string"?l[t]="":Array.isArray(o)?l[t]=[]:l[t]=null,n("update:filterValues",l)}return(t,l)=>(e.openBlock(),e.createElementBlock("div",k,[e.createElementVNode("div",v,[e.createElementVNode("div",x,[r.collapsible?(e.openBlock(),e.createElementBlock("button",{key:0,type:"button",class:"flex items-center justify-center h-6 w-6 rounded hover:bg-accent hover:text-accent-foreground transition-colors",onClick:y},[e.createVNode(e.unref(d.ChevronDown),{class:e.normalizeClass(["h-3.5 w-3.5 transition-transform",c.value?"rotate-0":"-rotate-90"])},null,8,["class"])])):e.createCommentVNode("",!0),r.title?(e.openBlock(),e.createBlock(B.default,{key:1,text:r.title,class:"text-sm font-semibold text-foreground"},null,8,["text"])):e.createCommentVNode("",!0),u.value.length>0?(e.openBlock(),e.createElementBlock("div",b,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(u.value,o=>(e.openBlock(),e.createBlock(_.default,{key:o.key,variant:"secondary",size:"sm",class:"flex items-center gap-1 cursor-default"},{default:e.withCtx(()=>[e.createElementVNode("span",w,e.toDisplayString(o.label)+":",1),e.createElementVNode("span",null,e.toDisplayString(o.value),1),e.createElementVNode("button",{type:"button",class:"ml-0.5 rounded-full hover:bg-gray-300 p-0.5 transition-colors",onClick:e.withModifiers(a=>V(o.key),["stop"])},[e.createVNode(e.unref(d.X),{class:"h-3 w-3"})],8,C)]),_:2},1024))),128))])):e.createCommentVNode("",!0)]),e.createElementVNode("div",N,[e.renderSlot(t.$slots,"actions"),r.showResetButton?(e.openBlock(),e.createBlock(f.default,{key:0,variant:"secondary",size:"sm",onClick:h},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(r.resetButtonText),1)]),_:1})):e.createCommentVNode("",!0),r.showSearchButton?(e.openBlock(),e.createBlock(f.default,{key:1,styletype:"primary",size:"sm",onClick:g},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(r.searchButtonText),1)]),_:1})):e.createCommentVNode("",!0)])]),e.withDirectives(e.createElementVNode("div",E,[e.renderSlot(t.$slots,"filters")],512),[[e.vShow,c.value]])]))}});exports.default=S;
2
2
  //# sourceMappingURL=JFilterBar.vue.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"JFilterBar.vue.cjs","sources":["../../../../src/components/organisms/JFilterBar.vue"],"sourcesContent":["<template>\r\n <div class=\"w-full rounded-lg border bg-card text-card-foreground\">\r\n <!-- Row 1: toolbar -->\r\n <div class=\"flex items-center justify-between px-4 py-2\">\r\n <div class=\"flex items-center gap-2\">\r\n <button\r\n v-if=\"collapsible\"\r\n type=\"button\"\r\n class=\"flex items-center justify-center h-7 w-7 rounded hover:bg-accent hover:text-accent-foreground transition-colors\"\r\n @click=\"toggleCollapsed\"\r\n >\r\n <ChevronDown\r\n :class=\"[\r\n 'h-4 w-4 transition-transform',\r\n isExpanded ? 'rotate-0' : '-rotate-90',\r\n ]\"\r\n />\r\n </button>\r\n <!-- 타이틀 -->\r\n <JLabel\r\n v-if=\"title\"\r\n :text=\"title\"\r\n class=\"text-sm font-semibold text-foreground\"\r\n />\r\n <!-- 선택된 필터 뱃지 표시 -->\r\n <div v-if=\"activeFilters.length > 0\" class=\"flex items-center gap-1 flex-wrap\">\r\n <JBadge\r\n v-for=\"filter in activeFilters\"\r\n :key=\"filter.key\"\r\n variant=\"secondary\"\r\n size=\"sm\"\r\n class=\"flex items-center gap-1 cursor-default\"\r\n >\r\n <span class=\"text-muted-foreground\">{{ filter.label }}:</span>\r\n <span>{{ filter.value }}</span>\r\n <button\r\n type=\"button\"\r\n class=\"ml-0.5 rounded-full hover:bg-gray-300 p-0.5 transition-colors\"\r\n @click.stop=\"removeFilter(filter.key)\"\r\n >\r\n <X class=\"h-3 w-3\" />\r\n </button>\r\n </JBadge>\r\n </div>\r\n </div>\r\n <div class=\"flex items-center gap-2\">\r\n <slot name=\"actions\" />\r\n <JButton\r\n v-if=\"showResetButton\"\r\n variant=\"secondary\"\r\n size=\"sm\"\r\n @click=\"handleReset\"\r\n >\r\n {{ resetButtonText }}\r\n </JButton>\r\n <JButton\r\n v-if=\"showSearchButton\"\r\n styletype=\"primary\"\r\n size=\"sm\"\r\n @click=\"handleSearch\"\r\n >\r\n {{ searchButtonText }}\r\n </JButton>\r\n </div>\r\n </div>\r\n\r\n <!-- Row 2: filters -->\r\n <div v-show=\"isExpanded\" class=\"px-4 pb-4\">\r\n <slot name=\"filters\" />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed } from 'vue'\r\nimport { ChevronDown, X } from 'lucide-vue-next'\r\nimport JBadge from '@/components/atoms/JBadge.vue'\r\nimport JButton from '@/components/atoms/JButton.vue'\r\nimport JLabel from '@/components/atoms/JLabel.vue'\r\n\r\n/** 활성 필터 아이템 타입 */\r\nexport interface ActiveFilterItem {\r\n /** 필터 식별 키 */\r\n key: string\r\n /** 표시할 라벨 (필터명) */\r\n label: string\r\n /** 표시할 값 */\r\n value: string\r\n}\r\n\r\n/** 필터 설정 타입 */\r\nexport interface FilterDisplayItem {\r\n /** 표시할 라벨 */\r\n label: string\r\n /** 값을 표시용 문자열로 변환 (예: combo value -> label) */\r\n displayValue?: (value: unknown) => string\r\n}\r\n\r\nexport interface JFilterBarProps {\r\n /** 필터바 타이틀 */\r\n title?: string\r\n /** 필터 접힘 상태 (v-model 지원) */\r\n collapsed?: boolean\r\n /** 접기/펼치기 가능 여부. false면 토글 버튼 숨김 & 필터 항상 표시 */\r\n collapsible?: boolean\r\n /** 필터 값 객체 (v-model:filterValues 지원) */\r\n filterValues?: Record<string, unknown>\r\n /** 필터 표시 설정 (label, displayValue 등) */\r\n filterDisplay?: Record<string, FilterDisplayItem>\r\n /** 초기화 버튼 표시 여부 */\r\n showResetButton?: boolean\r\n /** 조회 버튼 표시 여부 */\r\n showSearchButton?: boolean\r\n /** 초기화 버튼 텍스트 */\r\n resetButtonText?: string\r\n /** 조회 버튼 텍스트 */\r\n searchButtonText?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<JFilterBarProps>(), {\r\n collapsed: false,\r\n collapsible: true,\r\n filterValues: () => ({}),\r\n filterDisplay: () => ({}),\r\n showResetButton: false,\r\n showSearchButton: false,\r\n resetButtonText: '초기화',\r\n searchButtonText: '조회',\r\n})\r\n\r\nconst emit = defineEmits<{\r\n 'update:collapsed': [value: boolean]\r\n 'update:filterValues': [value: Record<string, unknown>]\r\n /** 조회 버튼 클릭 */\r\n search: []\r\n /** 초기화 버튼 클릭 */\r\n reset: []\r\n}>()\r\n\r\nconst isExpanded = computed(() => {\r\n if (!props.collapsible) return true\r\n return !props.collapsed\r\n})\r\n\r\n/** 값이 비어있는지 확인 */\r\nfunction isEmpty(value: unknown): boolean {\r\n if (value === null || value === undefined) return true\r\n if (typeof value === 'string' && value.trim() === '') return true\r\n if (Array.isArray(value) && value.length === 0) return true\r\n return false\r\n}\r\n\r\n/** filterValues + filterDisplay 기반으로 활성 필터 목록 자동 생성 */\r\nconst activeFilters = computed<ActiveFilterItem[]>(() => {\r\n const filters: ActiveFilterItem[] = []\r\n\r\n for (const [key, config] of Object.entries(props.filterDisplay)) {\r\n const value = props.filterValues[key]\r\n if (isEmpty(value)) continue\r\n\r\n const displayValue = config.displayValue ? config.displayValue(value) : String(value)\r\n if (displayValue.trim() === '') continue\r\n\r\n filters.push({\r\n key,\r\n label: config.label,\r\n value: displayValue,\r\n })\r\n }\r\n\r\n return filters\r\n})\r\n\r\nfunction toggleCollapsed() {\r\n emit('update:collapsed', !props.collapsed)\r\n}\r\n\r\nfunction handleReset() {\r\n // filterValues의 모든 값을 초기화\r\n const resetValues: Record<string, unknown> = {}\r\n for (const key of Object.keys(props.filterValues)) {\r\n const currentValue = props.filterValues[key]\r\n if (typeof currentValue === 'string') {\r\n resetValues[key] = ''\r\n } else if (Array.isArray(currentValue)) {\r\n resetValues[key] = []\r\n } else {\r\n resetValues[key] = null\r\n }\r\n }\r\n emit('update:filterValues', resetValues)\r\n emit('reset')\r\n}\r\n\r\nfunction handleSearch() {\r\n emit('search')\r\n}\r\n\r\nfunction removeFilter(key: string) {\r\n // filterValues 업데이트 (해당 키 값을 초기화)\r\n const newValues = { ...props.filterValues }\r\n const currentValue = newValues[key]\r\n\r\n // 타입에 따라 적절한 초기값으로 설정\r\n if (typeof currentValue === 'string') {\r\n newValues[key] = ''\r\n } else if (Array.isArray(currentValue)) {\r\n newValues[key] = []\r\n } else {\r\n newValues[key] = null\r\n }\r\n\r\n emit('update:filterValues', newValues)\r\n}\r\n</script>\r\n"],"names":["props","__props","emit","__emit","isExpanded","computed","isEmpty","value","activeFilters","filters","key","config","displayValue","toggleCollapsed","handleReset","resetValues","currentValue","handleSearch","removeFilter","newValues","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_hoisted_3","_createVNode","_unref","ChevronDown","_normalizeClass","_createBlock","JLabel","_hoisted_4","_Fragment","_renderList","filter","JBadge","_hoisted_5","_toDisplayString","_withModifiers","$event","X","_hoisted_7","_renderSlot","_ctx","JButton","_withDirectives","_hoisted_8"],"mappings":"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"}
1
+ {"version":3,"file":"JFilterBar.vue.cjs","sources":["../../../../src/components/organisms/JFilterBar.vue"],"sourcesContent":["<template>\r\n <div class=\"w-full rounded-lg border bg-card text-card-foreground\">\r\n <!-- Row 1: toolbar -->\r\n <div class=\"flex items-center justify-between px-3 py-1.5\">\r\n <div class=\"flex items-center gap-2\">\r\n <button\r\n v-if=\"collapsible\"\r\n type=\"button\"\r\n class=\"flex items-center justify-center h-6 w-6 rounded hover:bg-accent hover:text-accent-foreground transition-colors\"\r\n @click=\"toggleCollapsed\"\r\n >\r\n <ChevronDown\r\n :class=\"[\r\n 'h-3.5 w-3.5 transition-transform',\r\n isExpanded ? 'rotate-0' : '-rotate-90',\r\n ]\"\r\n />\r\n </button>\r\n <!-- 타이틀 -->\r\n <JLabel\r\n v-if=\"title\"\r\n :text=\"title\"\r\n class=\"text-sm font-semibold text-foreground\"\r\n />\r\n <!-- 선택된 필터 뱃지 표시 -->\r\n <div v-if=\"activeFilters.length > 0\" class=\"flex items-center gap-1 flex-wrap\">\r\n <JBadge\r\n v-for=\"filter in activeFilters\"\r\n :key=\"filter.key\"\r\n variant=\"secondary\"\r\n size=\"sm\"\r\n class=\"flex items-center gap-1 cursor-default\"\r\n >\r\n <span class=\"text-muted-foreground\">{{ filter.label }}:</span>\r\n <span>{{ filter.value }}</span>\r\n <button\r\n type=\"button\"\r\n class=\"ml-0.5 rounded-full hover:bg-gray-300 p-0.5 transition-colors\"\r\n @click.stop=\"removeFilter(filter.key)\"\r\n >\r\n <X class=\"h-3 w-3\" />\r\n </button>\r\n </JBadge>\r\n </div>\r\n </div>\r\n <div class=\"flex items-center gap-2\">\r\n <slot name=\"actions\" />\r\n <JButton\r\n v-if=\"showResetButton\"\r\n variant=\"secondary\"\r\n size=\"sm\"\r\n @click=\"handleReset\"\r\n >\r\n {{ resetButtonText }}\r\n </JButton>\r\n <JButton\r\n v-if=\"showSearchButton\"\r\n styletype=\"primary\"\r\n size=\"sm\"\r\n @click=\"handleSearch\"\r\n >\r\n {{ searchButtonText }}\r\n </JButton>\r\n </div>\r\n </div>\r\n\r\n <!-- Row 2: filters -->\r\n <div v-show=\"isExpanded\" class=\"px-3 pb-3\">\r\n <slot name=\"filters\" />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed } from 'vue'\r\nimport { ChevronDown, X } from 'lucide-vue-next'\r\nimport JBadge from '@/components/atoms/JBadge.vue'\r\nimport JButton from '@/components/atoms/JButton.vue'\r\nimport JLabel from '@/components/atoms/JLabel.vue'\r\n\r\n/** 활성 필터 아이템 타입 */\r\nexport interface ActiveFilterItem {\r\n /** 필터 식별 키 */\r\n key: string\r\n /** 표시할 라벨 (필터명) */\r\n label: string\r\n /** 표시할 값 */\r\n value: string\r\n}\r\n\r\n/** 필터 설정 타입 */\r\nexport interface FilterDisplayItem {\r\n /** 표시할 라벨 */\r\n label: string\r\n /** 값을 표시용 문자열로 변환 (예: combo value -> label) */\r\n displayValue?: (value: unknown) => string\r\n}\r\n\r\nexport interface JFilterBarProps {\r\n /** 필터바 타이틀 */\r\n title?: string\r\n /** 필터 접힘 상태 (v-model 지원) */\r\n collapsed?: boolean\r\n /** 접기/펼치기 가능 여부. false면 토글 버튼 숨김 & 필터 항상 표시 */\r\n collapsible?: boolean\r\n /** 필터 값 객체 (v-model:filterValues 지원) */\r\n filterValues?: Record<string, unknown>\r\n /** 필터 표시 설정 (label, displayValue 등) */\r\n filterDisplay?: Record<string, FilterDisplayItem>\r\n /** 초기화 버튼 표시 여부 */\r\n showResetButton?: boolean\r\n /** 조회 버튼 표시 여부 */\r\n showSearchButton?: boolean\r\n /** 초기화 버튼 텍스트 */\r\n resetButtonText?: string\r\n /** 조회 버튼 텍스트 */\r\n searchButtonText?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<JFilterBarProps>(), {\r\n collapsed: true,\r\n collapsible: true,\r\n filterValues: () => ({}),\r\n filterDisplay: () => ({}),\r\n showResetButton: false,\r\n showSearchButton: false,\r\n resetButtonText: '초기화',\r\n searchButtonText: '조회',\r\n})\r\n\r\nconst emit = defineEmits<{\r\n 'update:collapsed': [value: boolean]\r\n 'update:filterValues': [value: Record<string, unknown>]\r\n /** 조회 버튼 클릭 */\r\n search: []\r\n /** 초기화 버튼 클릭 */\r\n reset: []\r\n}>()\r\n\r\nconst isExpanded = computed(() => {\r\n if (!props.collapsible) return true\r\n return !props.collapsed\r\n})\r\n\r\n/** 값이 비어있는지 확인 */\r\nfunction isEmpty(value: unknown): boolean {\r\n if (value === null || value === undefined) return true\r\n if (typeof value === 'string' && value.trim() === '') return true\r\n if (Array.isArray(value) && value.length === 0) return true\r\n return false\r\n}\r\n\r\n/** filterValues + filterDisplay 기반으로 활성 필터 목록 자동 생성 */\r\nconst activeFilters = computed<ActiveFilterItem[]>(() => {\r\n const filters: ActiveFilterItem[] = []\r\n\r\n for (const [key, config] of Object.entries(props.filterDisplay)) {\r\n const value = props.filterValues[key]\r\n if (isEmpty(value)) continue\r\n\r\n const displayValue = config.displayValue ? config.displayValue(value) : String(value)\r\n if (displayValue.trim() === '') continue\r\n\r\n filters.push({\r\n key,\r\n label: config.label,\r\n value: displayValue,\r\n })\r\n }\r\n\r\n return filters\r\n})\r\n\r\nfunction toggleCollapsed() {\r\n emit('update:collapsed', !props.collapsed)\r\n}\r\n\r\nfunction handleReset() {\r\n // filterValues의 모든 값을 초기화\r\n const resetValues: Record<string, unknown> = {}\r\n for (const key of Object.keys(props.filterValues)) {\r\n const currentValue = props.filterValues[key]\r\n if (typeof currentValue === 'string') {\r\n resetValues[key] = ''\r\n } else if (Array.isArray(currentValue)) {\r\n resetValues[key] = []\r\n } else {\r\n resetValues[key] = null\r\n }\r\n }\r\n emit('update:filterValues', resetValues)\r\n emit('reset')\r\n}\r\n\r\nfunction handleSearch() {\r\n emit('search')\r\n}\r\n\r\nfunction removeFilter(key: string) {\r\n // filterValues 업데이트 (해당 키 값을 초기화)\r\n const newValues = { ...props.filterValues }\r\n const currentValue = newValues[key]\r\n\r\n // 타입에 따라 적절한 초기값으로 설정\r\n if (typeof currentValue === 'string') {\r\n newValues[key] = ''\r\n } else if (Array.isArray(currentValue)) {\r\n newValues[key] = []\r\n } else {\r\n newValues[key] = null\r\n }\r\n\r\n emit('update:filterValues', newValues)\r\n}\r\n</script>\r\n"],"names":["props","__props","emit","__emit","isExpanded","computed","isEmpty","value","activeFilters","filters","key","config","displayValue","toggleCollapsed","handleReset","resetValues","currentValue","handleSearch","removeFilter","newValues","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_hoisted_3","_createVNode","_unref","ChevronDown","_normalizeClass","_createBlock","JLabel","_hoisted_4","_Fragment","_renderList","filter","JBadge","_hoisted_5","_toDisplayString","_withModifiers","$event","X","_hoisted_7","_renderSlot","_ctx","JButton","_withDirectives","_hoisted_8"],"mappings":"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"}
@@ -3,14 +3,14 @@ import { ChevronDown as N, X as R } from "lucide-vue-next";
3
3
  import O from "../atoms/JBadge.vue.js";
4
4
  import k from "../atoms/JButton.vue.js";
5
5
  import J from "../atoms/JLabel.vue.js";
6
- const L = { class: "w-full rounded-lg border bg-card text-card-foreground" }, M = { class: "flex items-center justify-between px-4 py-2" }, X = { class: "flex items-center gap-2" }, q = {
6
+ const L = { class: "w-full rounded-lg border bg-card text-card-foreground" }, M = { class: "flex items-center justify-between px-3 py-1.5" }, X = { class: "flex items-center gap-2" }, q = {
7
7
  key: 2,
8
8
  class: "flex items-center gap-1 flex-wrap"
9
- }, G = { class: "text-muted-foreground" }, H = ["onClick"], I = { class: "flex items-center gap-2" }, K = { class: "px-4 pb-4" }, Z = /* @__PURE__ */ $({
9
+ }, G = { class: "text-muted-foreground" }, H = ["onClick"], I = { class: "flex items-center gap-2" }, K = { class: "px-3 pb-3" }, Z = /* @__PURE__ */ $({
10
10
  __name: "JFilterBar",
11
11
  props: {
12
12
  title: {},
13
- collapsed: { type: Boolean, default: !1 },
13
+ collapsed: { type: Boolean, default: !0 },
14
14
  collapsible: { type: Boolean, default: !0 },
15
15
  filterValues: { default: () => ({}) },
16
16
  filterDisplay: { default: () => ({}) },
@@ -63,12 +63,12 @@ const L = { class: "w-full rounded-lg border bg-card text-card-foreground" }, M
63
63
  l.collapsible ? (o(), u("button", {
64
64
  key: 0,
65
65
  type: "button",
66
- class: "flex items-center justify-center h-7 w-7 rounded hover:bg-accent hover:text-accent-foreground transition-colors",
66
+ class: "flex items-center justify-center h-6 w-6 rounded hover:bg-accent hover:text-accent-foreground transition-colors",
67
67
  onClick: _
68
68
  }, [
69
69
  x(v(N), {
70
70
  class: T([
71
- "h-4 w-4 transition-transform",
71
+ "h-3.5 w-3.5 transition-transform",
72
72
  m.value ? "rotate-0" : "-rotate-90"
73
73
  ])
74
74
  }, null, 8, ["class"])
@@ -1 +1 @@
1
- {"version":3,"file":"JFilterBar.vue.js","sources":["../../../../src/components/organisms/JFilterBar.vue"],"sourcesContent":["<template>\r\n <div class=\"w-full rounded-lg border bg-card text-card-foreground\">\r\n <!-- Row 1: toolbar -->\r\n <div class=\"flex items-center justify-between px-4 py-2\">\r\n <div class=\"flex items-center gap-2\">\r\n <button\r\n v-if=\"collapsible\"\r\n type=\"button\"\r\n class=\"flex items-center justify-center h-7 w-7 rounded hover:bg-accent hover:text-accent-foreground transition-colors\"\r\n @click=\"toggleCollapsed\"\r\n >\r\n <ChevronDown\r\n :class=\"[\r\n 'h-4 w-4 transition-transform',\r\n isExpanded ? 'rotate-0' : '-rotate-90',\r\n ]\"\r\n />\r\n </button>\r\n <!-- 타이틀 -->\r\n <JLabel\r\n v-if=\"title\"\r\n :text=\"title\"\r\n class=\"text-sm font-semibold text-foreground\"\r\n />\r\n <!-- 선택된 필터 뱃지 표시 -->\r\n <div v-if=\"activeFilters.length > 0\" class=\"flex items-center gap-1 flex-wrap\">\r\n <JBadge\r\n v-for=\"filter in activeFilters\"\r\n :key=\"filter.key\"\r\n variant=\"secondary\"\r\n size=\"sm\"\r\n class=\"flex items-center gap-1 cursor-default\"\r\n >\r\n <span class=\"text-muted-foreground\">{{ filter.label }}:</span>\r\n <span>{{ filter.value }}</span>\r\n <button\r\n type=\"button\"\r\n class=\"ml-0.5 rounded-full hover:bg-gray-300 p-0.5 transition-colors\"\r\n @click.stop=\"removeFilter(filter.key)\"\r\n >\r\n <X class=\"h-3 w-3\" />\r\n </button>\r\n </JBadge>\r\n </div>\r\n </div>\r\n <div class=\"flex items-center gap-2\">\r\n <slot name=\"actions\" />\r\n <JButton\r\n v-if=\"showResetButton\"\r\n variant=\"secondary\"\r\n size=\"sm\"\r\n @click=\"handleReset\"\r\n >\r\n {{ resetButtonText }}\r\n </JButton>\r\n <JButton\r\n v-if=\"showSearchButton\"\r\n styletype=\"primary\"\r\n size=\"sm\"\r\n @click=\"handleSearch\"\r\n >\r\n {{ searchButtonText }}\r\n </JButton>\r\n </div>\r\n </div>\r\n\r\n <!-- Row 2: filters -->\r\n <div v-show=\"isExpanded\" class=\"px-4 pb-4\">\r\n <slot name=\"filters\" />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed } from 'vue'\r\nimport { ChevronDown, X } from 'lucide-vue-next'\r\nimport JBadge from '@/components/atoms/JBadge.vue'\r\nimport JButton from '@/components/atoms/JButton.vue'\r\nimport JLabel from '@/components/atoms/JLabel.vue'\r\n\r\n/** 활성 필터 아이템 타입 */\r\nexport interface ActiveFilterItem {\r\n /** 필터 식별 키 */\r\n key: string\r\n /** 표시할 라벨 (필터명) */\r\n label: string\r\n /** 표시할 값 */\r\n value: string\r\n}\r\n\r\n/** 필터 설정 타입 */\r\nexport interface FilterDisplayItem {\r\n /** 표시할 라벨 */\r\n label: string\r\n /** 값을 표시용 문자열로 변환 (예: combo value -> label) */\r\n displayValue?: (value: unknown) => string\r\n}\r\n\r\nexport interface JFilterBarProps {\r\n /** 필터바 타이틀 */\r\n title?: string\r\n /** 필터 접힘 상태 (v-model 지원) */\r\n collapsed?: boolean\r\n /** 접기/펼치기 가능 여부. false면 토글 버튼 숨김 & 필터 항상 표시 */\r\n collapsible?: boolean\r\n /** 필터 값 객체 (v-model:filterValues 지원) */\r\n filterValues?: Record<string, unknown>\r\n /** 필터 표시 설정 (label, displayValue 등) */\r\n filterDisplay?: Record<string, FilterDisplayItem>\r\n /** 초기화 버튼 표시 여부 */\r\n showResetButton?: boolean\r\n /** 조회 버튼 표시 여부 */\r\n showSearchButton?: boolean\r\n /** 초기화 버튼 텍스트 */\r\n resetButtonText?: string\r\n /** 조회 버튼 텍스트 */\r\n searchButtonText?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<JFilterBarProps>(), {\r\n collapsed: false,\r\n collapsible: true,\r\n filterValues: () => ({}),\r\n filterDisplay: () => ({}),\r\n showResetButton: false,\r\n showSearchButton: false,\r\n resetButtonText: '초기화',\r\n searchButtonText: '조회',\r\n})\r\n\r\nconst emit = defineEmits<{\r\n 'update:collapsed': [value: boolean]\r\n 'update:filterValues': [value: Record<string, unknown>]\r\n /** 조회 버튼 클릭 */\r\n search: []\r\n /** 초기화 버튼 클릭 */\r\n reset: []\r\n}>()\r\n\r\nconst isExpanded = computed(() => {\r\n if (!props.collapsible) return true\r\n return !props.collapsed\r\n})\r\n\r\n/** 값이 비어있는지 확인 */\r\nfunction isEmpty(value: unknown): boolean {\r\n if (value === null || value === undefined) return true\r\n if (typeof value === 'string' && value.trim() === '') return true\r\n if (Array.isArray(value) && value.length === 0) return true\r\n return false\r\n}\r\n\r\n/** filterValues + filterDisplay 기반으로 활성 필터 목록 자동 생성 */\r\nconst activeFilters = computed<ActiveFilterItem[]>(() => {\r\n const filters: ActiveFilterItem[] = []\r\n\r\n for (const [key, config] of Object.entries(props.filterDisplay)) {\r\n const value = props.filterValues[key]\r\n if (isEmpty(value)) continue\r\n\r\n const displayValue = config.displayValue ? config.displayValue(value) : String(value)\r\n if (displayValue.trim() === '') continue\r\n\r\n filters.push({\r\n key,\r\n label: config.label,\r\n value: displayValue,\r\n })\r\n }\r\n\r\n return filters\r\n})\r\n\r\nfunction toggleCollapsed() {\r\n emit('update:collapsed', !props.collapsed)\r\n}\r\n\r\nfunction handleReset() {\r\n // filterValues의 모든 값을 초기화\r\n const resetValues: Record<string, unknown> = {}\r\n for (const key of Object.keys(props.filterValues)) {\r\n const currentValue = props.filterValues[key]\r\n if (typeof currentValue === 'string') {\r\n resetValues[key] = ''\r\n } else if (Array.isArray(currentValue)) {\r\n resetValues[key] = []\r\n } else {\r\n resetValues[key] = null\r\n }\r\n }\r\n emit('update:filterValues', resetValues)\r\n emit('reset')\r\n}\r\n\r\nfunction handleSearch() {\r\n emit('search')\r\n}\r\n\r\nfunction removeFilter(key: string) {\r\n // filterValues 업데이트 (해당 키 값을 초기화)\r\n const newValues = { ...props.filterValues }\r\n const currentValue = newValues[key]\r\n\r\n // 타입에 따라 적절한 초기값으로 설정\r\n if (typeof currentValue === 'string') {\r\n newValues[key] = ''\r\n } else if (Array.isArray(currentValue)) {\r\n newValues[key] = []\r\n } else {\r\n newValues[key] = null\r\n }\r\n\r\n emit('update:filterValues', newValues)\r\n}\r\n</script>\r\n"],"names":["props","__props","emit","__emit","isExpanded","computed","isEmpty","value","activeFilters","filters","key","config","displayValue","toggleCollapsed","handleReset","resetValues","currentValue","handleSearch","removeFilter","newValues","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_hoisted_3","_createVNode","_unref","ChevronDown","_normalizeClass","_createBlock","JLabel","_hoisted_4","_Fragment","_renderList","filter","JBadge","_hoisted_5","_toDisplayString","_withModifiers","$event","X","_hoisted_7","_renderSlot","_ctx","JButton","_withDirectives","_hoisted_8"],"mappings":"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"}
1
+ {"version":3,"file":"JFilterBar.vue.js","sources":["../../../../src/components/organisms/JFilterBar.vue"],"sourcesContent":["<template>\r\n <div class=\"w-full rounded-lg border bg-card text-card-foreground\">\r\n <!-- Row 1: toolbar -->\r\n <div class=\"flex items-center justify-between px-3 py-1.5\">\r\n <div class=\"flex items-center gap-2\">\r\n <button\r\n v-if=\"collapsible\"\r\n type=\"button\"\r\n class=\"flex items-center justify-center h-6 w-6 rounded hover:bg-accent hover:text-accent-foreground transition-colors\"\r\n @click=\"toggleCollapsed\"\r\n >\r\n <ChevronDown\r\n :class=\"[\r\n 'h-3.5 w-3.5 transition-transform',\r\n isExpanded ? 'rotate-0' : '-rotate-90',\r\n ]\"\r\n />\r\n </button>\r\n <!-- 타이틀 -->\r\n <JLabel\r\n v-if=\"title\"\r\n :text=\"title\"\r\n class=\"text-sm font-semibold text-foreground\"\r\n />\r\n <!-- 선택된 필터 뱃지 표시 -->\r\n <div v-if=\"activeFilters.length > 0\" class=\"flex items-center gap-1 flex-wrap\">\r\n <JBadge\r\n v-for=\"filter in activeFilters\"\r\n :key=\"filter.key\"\r\n variant=\"secondary\"\r\n size=\"sm\"\r\n class=\"flex items-center gap-1 cursor-default\"\r\n >\r\n <span class=\"text-muted-foreground\">{{ filter.label }}:</span>\r\n <span>{{ filter.value }}</span>\r\n <button\r\n type=\"button\"\r\n class=\"ml-0.5 rounded-full hover:bg-gray-300 p-0.5 transition-colors\"\r\n @click.stop=\"removeFilter(filter.key)\"\r\n >\r\n <X class=\"h-3 w-3\" />\r\n </button>\r\n </JBadge>\r\n </div>\r\n </div>\r\n <div class=\"flex items-center gap-2\">\r\n <slot name=\"actions\" />\r\n <JButton\r\n v-if=\"showResetButton\"\r\n variant=\"secondary\"\r\n size=\"sm\"\r\n @click=\"handleReset\"\r\n >\r\n {{ resetButtonText }}\r\n </JButton>\r\n <JButton\r\n v-if=\"showSearchButton\"\r\n styletype=\"primary\"\r\n size=\"sm\"\r\n @click=\"handleSearch\"\r\n >\r\n {{ searchButtonText }}\r\n </JButton>\r\n </div>\r\n </div>\r\n\r\n <!-- Row 2: filters -->\r\n <div v-show=\"isExpanded\" class=\"px-3 pb-3\">\r\n <slot name=\"filters\" />\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed } from 'vue'\r\nimport { ChevronDown, X } from 'lucide-vue-next'\r\nimport JBadge from '@/components/atoms/JBadge.vue'\r\nimport JButton from '@/components/atoms/JButton.vue'\r\nimport JLabel from '@/components/atoms/JLabel.vue'\r\n\r\n/** 활성 필터 아이템 타입 */\r\nexport interface ActiveFilterItem {\r\n /** 필터 식별 키 */\r\n key: string\r\n /** 표시할 라벨 (필터명) */\r\n label: string\r\n /** 표시할 값 */\r\n value: string\r\n}\r\n\r\n/** 필터 설정 타입 */\r\nexport interface FilterDisplayItem {\r\n /** 표시할 라벨 */\r\n label: string\r\n /** 값을 표시용 문자열로 변환 (예: combo value -> label) */\r\n displayValue?: (value: unknown) => string\r\n}\r\n\r\nexport interface JFilterBarProps {\r\n /** 필터바 타이틀 */\r\n title?: string\r\n /** 필터 접힘 상태 (v-model 지원) */\r\n collapsed?: boolean\r\n /** 접기/펼치기 가능 여부. false면 토글 버튼 숨김 & 필터 항상 표시 */\r\n collapsible?: boolean\r\n /** 필터 값 객체 (v-model:filterValues 지원) */\r\n filterValues?: Record<string, unknown>\r\n /** 필터 표시 설정 (label, displayValue 등) */\r\n filterDisplay?: Record<string, FilterDisplayItem>\r\n /** 초기화 버튼 표시 여부 */\r\n showResetButton?: boolean\r\n /** 조회 버튼 표시 여부 */\r\n showSearchButton?: boolean\r\n /** 초기화 버튼 텍스트 */\r\n resetButtonText?: string\r\n /** 조회 버튼 텍스트 */\r\n searchButtonText?: string\r\n}\r\n\r\nconst props = withDefaults(defineProps<JFilterBarProps>(), {\r\n collapsed: true,\r\n collapsible: true,\r\n filterValues: () => ({}),\r\n filterDisplay: () => ({}),\r\n showResetButton: false,\r\n showSearchButton: false,\r\n resetButtonText: '초기화',\r\n searchButtonText: '조회',\r\n})\r\n\r\nconst emit = defineEmits<{\r\n 'update:collapsed': [value: boolean]\r\n 'update:filterValues': [value: Record<string, unknown>]\r\n /** 조회 버튼 클릭 */\r\n search: []\r\n /** 초기화 버튼 클릭 */\r\n reset: []\r\n}>()\r\n\r\nconst isExpanded = computed(() => {\r\n if (!props.collapsible) return true\r\n return !props.collapsed\r\n})\r\n\r\n/** 값이 비어있는지 확인 */\r\nfunction isEmpty(value: unknown): boolean {\r\n if (value === null || value === undefined) return true\r\n if (typeof value === 'string' && value.trim() === '') return true\r\n if (Array.isArray(value) && value.length === 0) return true\r\n return false\r\n}\r\n\r\n/** filterValues + filterDisplay 기반으로 활성 필터 목록 자동 생성 */\r\nconst activeFilters = computed<ActiveFilterItem[]>(() => {\r\n const filters: ActiveFilterItem[] = []\r\n\r\n for (const [key, config] of Object.entries(props.filterDisplay)) {\r\n const value = props.filterValues[key]\r\n if (isEmpty(value)) continue\r\n\r\n const displayValue = config.displayValue ? config.displayValue(value) : String(value)\r\n if (displayValue.trim() === '') continue\r\n\r\n filters.push({\r\n key,\r\n label: config.label,\r\n value: displayValue,\r\n })\r\n }\r\n\r\n return filters\r\n})\r\n\r\nfunction toggleCollapsed() {\r\n emit('update:collapsed', !props.collapsed)\r\n}\r\n\r\nfunction handleReset() {\r\n // filterValues의 모든 값을 초기화\r\n const resetValues: Record<string, unknown> = {}\r\n for (const key of Object.keys(props.filterValues)) {\r\n const currentValue = props.filterValues[key]\r\n if (typeof currentValue === 'string') {\r\n resetValues[key] = ''\r\n } else if (Array.isArray(currentValue)) {\r\n resetValues[key] = []\r\n } else {\r\n resetValues[key] = null\r\n }\r\n }\r\n emit('update:filterValues', resetValues)\r\n emit('reset')\r\n}\r\n\r\nfunction handleSearch() {\r\n emit('search')\r\n}\r\n\r\nfunction removeFilter(key: string) {\r\n // filterValues 업데이트 (해당 키 값을 초기화)\r\n const newValues = { ...props.filterValues }\r\n const currentValue = newValues[key]\r\n\r\n // 타입에 따라 적절한 초기값으로 설정\r\n if (typeof currentValue === 'string') {\r\n newValues[key] = ''\r\n } else if (Array.isArray(currentValue)) {\r\n newValues[key] = []\r\n } else {\r\n newValues[key] = null\r\n }\r\n\r\n emit('update:filterValues', newValues)\r\n}\r\n</script>\r\n"],"names":["props","__props","emit","__emit","isExpanded","computed","isEmpty","value","activeFilters","filters","key","config","displayValue","toggleCollapsed","handleReset","resetValues","currentValue","handleSearch","removeFilter","newValues","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_hoisted_3","_createVNode","_unref","ChevronDown","_normalizeClass","_createBlock","JLabel","_hoisted_4","_Fragment","_renderList","filter","JBadge","_hoisted_5","_toDisplayString","_withModifiers","$event","X","_hoisted_7","_renderSlot","_ctx","JButton","_withDirectives","_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("vue"),u=require("../atoms/JIcon.vue.cjs"),K=require("../atoms/JAvatar.vue.cjs"),g=require("../atoms/JButton.vue.cjs"),N=require("../atoms/JPopover.vue.cjs"),v=require("../../lib/utils.cjs"),r=require("../../lib/theme-utils.cjs"),z=require("../../assets/images/logo-fallback.png.cjs"),W={class:"flex items-center gap-6 flex-1"},H=["src"],Q={key:1,class:"text-lg font-bold text-foreground"},X={key:2,class:"flex items-center gap-1"},Z={class:"flex items-center gap-3"},ee={class:"p-2 min-w-[200px]"},te={class:"space-y-1"},oe=["onClick"],le={key:1,class:"w-4"},ne={class:"flex-1 capitalize"},ae={key:0,class:"absolute top-0 right-0 h-4 w-4 rounded-full bg-destructive text-destructive-foreground text-xs flex items-center justify-center"},se={class:"p-2"},re={key:0,class:"max-h-96 overflow-y-auto space-y-1"},ce=["onClick"],ie={class:"flex items-start gap-2"},de={class:"flex-1 min-w-0"},ue={class:"text-sm font-medium text-foreground"},me={key:0,class:"text-xs text-muted-foreground mt-1"},fe={key:1,class:"text-xs text-muted-foreground/60 mt-1"},he={key:1,class:"p-4 text-center text-sm text-muted-foreground"},ke={class:"flex items-center gap-2 cursor-pointer hover:opacity-80 transition-opacity"},ge={class:"text-sm text-foreground hidden sm:inline"},ve={class:"w-full rounded-md overflow-hidden"},pe={class:"px-3 py-2 border-b border-border"},xe={class:"text-sm font-medium text-foreground"},ye={key:0,class:"text-xs text-muted-foreground mt-0.5"},be=["disabled","onClick"],Be={class:"flex-1 text-left truncate"},_e={key:1,class:"h-px bg-muted my-1"},Ce={key:0,class:"h-px bg-muted my-1"},Te=e.defineComponent({__name:"JHeader",props:{logo:{},logoText:{default:"JWMS Portal"},navItems:{},showNotifications:{type:Boolean,default:!1},notifications:{default:()=>[]},userAvatar:{},userName:{},isLoggedIn:{type:Boolean},userEmail:{},userId:{},styletype:{default:"default"},showSidebarToggle:{type:Boolean,default:!0},isSidebarOpen:{type:Boolean,default:!0},showThemeSelector:{type:Boolean,default:!0},defaultTheme:{default:void 0},availableThemes:{default:void 0}},emits:["logoClick","navClick","notificationClick","userMenuSelect","sidebarToggle","login"],setup(l,{emit:L}){const d=l,f=L,S={default:{containerClass:"h-14 px-4 border-b border-border bg-background",navItemClass:"text-sm text-muted-foreground hover:text-foreground transition-colors px-3 py-2 rounded-md hover:bg-accent",navItemActiveClass:"text-foreground font-medium bg-accent"},minimal:{containerClass:"h-12 px-3 border-b border-border bg-background",navItemClass:"text-xs text-muted-foreground hover:text-foreground transition-colors px-2 py-1 rounded-md hover:bg-accent",navItemActiveClass:"text-foreground font-medium bg-accent"}},B=e.computed(()=>S[d.styletype]??S.default),h=e.ref(0),D=e.computed(()=>{if(d.logo)return d.logo}),_=e.computed(()=>{if(d.logo)return h.value>=1&&z.default?z.default:D.value}),O=()=>{h.value===0?h.value=1:h.value===1&&(h.value=2)},C=e.computed(()=>d.notifications?.filter(o=>!o.read).length||0),M=[{id:"profile",label:"프로필",icon:"user"},{id:"settings",label:"설정",icon:"settings"},{id:"separator",label:"",separator:!0},{id:"logout",label:"로그아웃",icon:"logOut"}],V=e.computed(()=>[{items:M}]),A=()=>{f("logoClick")},F=(o,a)=>{o.onClick?.(),f("navClick",o,a)},q=o=>{o.onClick?.(),f("notificationClick",o)},G=o=>{f("userMenuSelect",o)},J=()=>{f("login")},U=()=>{f("sidebarToggle")},m=e.ref(!1),k=e.ref(r.getDefaultTheme()),p=e.ref([]);let x=!1,y=null,b=null;const I=()=>typeof window>"u"?!1:!!(window.location?.href?.includes("storybook")||window.__STORYBOOK_GLOBALS__),T=()=>{const o=r.detectThemeClasses(),a=r.ensureDefaultTheme(o);if(d.availableThemes&&d.availableThemes.length>0){const t=a.filter(n=>d.availableThemes.includes(n));p.value=r.ensureDefaultTheme(t)}else p.value=a},$=()=>{if(typeof window>"u")return"light";const o=localStorage.getItem("theme");return o==="dark"||o==="light"?o:window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"},E=o=>{x=!0;const a=document.documentElement;o==="dark"?(a.classList.add("dark"),m.value=!0):(a.classList.remove("dark"),m.value=!1),localStorage.setItem("theme",o),x=!1},P=()=>{const o=m.value?"light":"dark";E(o)},R=()=>{if(d.defaultTheme)return r.validateThemeExists(d.defaultTheme);const o=r.getStoredTheme("tweakcn-theme");return o?r.validateThemeExists(o):r.getDefaultTheme()},w=o=>{x=!0;const a=r.validateThemeExists(o),t=r.applyTheme(a);if(t){if(k.value=a,r.setStoredTheme(a,"tweakcn-theme"),I())try{const n=window.__STORYBOOK_GLOBALS__;n&&(n.theme=a)}catch{}}else{const n=r.getDefaultTheme();r.applyTheme(n),k.value=n,r.setStoredTheme(n,"tweakcn-theme")}return x=!1,t},Y=o=>{w(String(o))},j=e.computed(()=>m.value?"sun":"moon");return e.onMounted(()=>{T();const o=$();E(o);const a=R();w(a);const t=document.documentElement;m.value=t.classList.contains("dark"),y=new MutationObserver(()=>{if(x)return;const s=t.classList.contains("dark");s!==m.value&&(m.value=s,localStorage.setItem("theme",s?"dark":"light")),T();const c=Array.from(t.classList).find(i=>i.startsWith("theme-"));if(c){const i=c.replace("theme-","");p.value.includes(i)&&(k.value=i)}}),y.observe(document.documentElement,{attributes:!0,attributeFilter:["class"]}),b=new MutationObserver(()=>{T()}),b.observe(document.head,{childList:!0,subtree:!0});let n=null;if(I()){const s=()=>{try{const c=window.__STORYBOOK_GLOBALS__;if(c){if(typeof c.darkMode<"u"){const i=c.darkMode;i!==m.value&&E(i?"dark":"light")}if(c.theme&&c.theme!==k.value){const i=String(c.theme);p.value.includes(i)&&w(i)}}}catch{}};s(),n=setInterval(s,500)}e.onUnmounted(()=>{y&&(y.disconnect(),y=null),b&&(b.disconnect(),b=null),n&&clearInterval(n)})}),(o,a)=>(e.openBlock(),e.createElementBlock("header",{class:e.normalizeClass(e.unref(v.cn)("flex items-center justify-between w-full",B.value.containerClass))},[e.createElementVNode("div",W,[l.showSidebarToggle?(e.openBlock(),e.createBlock(g.default,{key:0,variant:"ghost",size:"icon",class:"flex-shrink-0","aria-label":"사이드바 토글",onClick:U},{default:e.withCtx(()=>[e.createVNode(u.default,{name:"menu",size:"md"})]),_:1})):e.createCommentVNode("",!0),_.value||l.logoText?(e.openBlock(),e.createElementBlock("div",{key:1,class:"flex items-center cursor-pointer",onClick:A},[_.value&&h.value<2?(e.openBlock(),e.createElementBlock("img",{key:0,src:_.value,alt:"로고",class:"h-8 w-auto",onError:O},null,40,H)):l.logoText?(e.openBlock(),e.createElementBlock("span",Q,e.toDisplayString(l.logoText),1)):e.createCommentVNode("",!0)])):e.createCommentVNode("",!0),l.navItems&&l.navItems.length>0?(e.openBlock(),e.createElementBlock("nav",X,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(l.navItems,(t,n)=>(e.openBlock(),e.createBlock(g.default,{key:n,variant:"ghost",class:e.normalizeClass(e.unref(v.cn)(B.value.navItemClass,t.active&&B.value.navItemActiveClass)),onClick:s=>F(t,n)},{default:e.withCtx(()=>[t.icon?(e.openBlock(),e.createBlock(u.default,{key:0,name:t.icon,size:"sm",class:"mr-1.5"},null,8,["name"])):e.createCommentVNode("",!0),e.createTextVNode(" "+e.toDisplayString(t.label),1)]),_:2},1032,["class","onClick"]))),128))])):e.createCommentVNode("",!0)]),e.createElementVNode("div",Z,[l.showThemeSelector?(e.openBlock(),e.createBlock(N.default,{key:0,position:"bottom",align:"end",styletype:"default-sm"},{trigger:e.withCtx(()=>[e.createVNode(g.default,{variant:"ghost",size:"icon","aria-label":"테마 선택"},{default:e.withCtx(()=>[e.createVNode(u.default,{name:"palette",size:"md"})]),_:1})]),default:e.withCtx(()=>[e.createElementVNode("div",ee,[a[0]||(a[0]=e.createElementVNode("div",{class:"text-xs font-medium text-muted-foreground px-2 py-1.5 mb-1"}," 테마 선택 ",-1)),e.createElementVNode("div",te,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(p.value,t=>(e.openBlock(),e.createElementBlock("button",{key:t,class:e.normalizeClass(e.unref(v.cn)("w-full flex items-center gap-2 px-2 py-1.5 text-sm rounded-md transition-colors","hover:bg-accent hover:text-accent-foreground","border-0 outline-none text-left",k.value===t&&"bg-accent text-accent-foreground font-medium")),onClick:n=>Y(t)},[k.value===t?(e.openBlock(),e.createBlock(u.default,{key:0,name:"check",size:"sm",class:"flex-shrink-0"})):(e.openBlock(),e.createElementBlock("span",le)),e.createElementVNode("span",ne,e.toDisplayString(t),1)],10,oe))),128))])])]),_:1})):e.createCommentVNode("",!0),l.showNotifications?(e.openBlock(),e.createBlock(N.default,{key:1,position:"bottom",align:"end",styletype:"default-sm"},{trigger:e.withCtx(()=>[e.createVNode(g.default,{variant:"ghost",size:"icon",class:"relative","aria-label":"알림"},{default:e.withCtx(()=>[e.createVNode(u.default,{name:"circleAlert",size:"md"}),C.value>0?(e.openBlock(),e.createElementBlock("span",ae,e.toDisplayString(C.value>9?"9+":C.value),1)):e.createCommentVNode("",!0)]),_:1})]),default:e.withCtx(()=>[e.createElementVNode("div",se,[l.notifications&&l.notifications.length>0?(e.openBlock(),e.createElementBlock("div",re,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(l.notifications,t=>(e.openBlock(),e.createElementBlock("div",{key:t.id,class:e.normalizeClass(e.unref(v.cn)("p-3 rounded-md cursor-pointer transition-colors",t.read?"hover:bg-accent/50":"bg-accent")),onClick:n=>q(t)},[e.createElementVNode("div",ie,[t.icon?(e.openBlock(),e.createBlock(u.default,{key:0,name:t.icon,size:"sm",class:"mt-0.5 flex-shrink-0"},null,8,["name"])):e.createCommentVNode("",!0),e.createElementVNode("div",de,[e.createElementVNode("p",ue,e.toDisplayString(t.title),1),t.message?(e.openBlock(),e.createElementBlock("p",me,e.toDisplayString(t.message),1)):e.createCommentVNode("",!0),t.time?(e.openBlock(),e.createElementBlock("p",fe,e.toDisplayString(t.time),1)):e.createCommentVNode("",!0)])])],10,ce))),128))])):(e.openBlock(),e.createElementBlock("div",he," 알림이 없습니다. "))])]),_:1})):e.createCommentVNode("",!0),e.createVNode(g.default,{variant:"ghost",size:"icon","aria-label":m.value?"라이트 모드로 전환":"다크 모드로 전환",onClick:P},{default:e.withCtx(()=>[e.createVNode(u.default,{name:j.value,size:"md"},null,8,["name"])]),_:1},8,["aria-label"]),l.userName?(e.openBlock(),e.createBlock(N.default,{key:2,position:"bottom",align:"end",styletype:"default-sm"},{trigger:e.withCtx(()=>[e.createElementVNode("div",ke,[e.createVNode(K.default,{src:l.userAvatar,fallback:l.userName?l.userName[0]:"U",size:"sm"},null,8,["src","fallback"]),e.createElementVNode("span",ge,e.toDisplayString(l.userName),1),e.createVNode(u.default,{name:"chevronDown",size:"sm",class:"text-muted-foreground hidden sm:inline"})])]),default:e.withCtx(()=>[e.createElementVNode("div",ve,[e.createElementVNode("div",pe,[e.createElementVNode("p",xe,e.toDisplayString(l.userName),1),l.userEmail?(e.openBlock(),e.createElementBlock("p",ye,e.toDisplayString(l.userEmail),1)):e.createCommentVNode("",!0)]),(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(V.value,(t,n)=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:n},[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(t.items,(s,c)=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:s.id},[s.separator?s.separator&&c>0?(e.openBlock(),e.createElementBlock("div",_e)):e.createCommentVNode("",!0):(e.openBlock(),e.createElementBlock("button",{key:0,disabled:s.disabled,class:e.normalizeClass(e.unref(v.cn)("w-full flex items-center gap-2 px-3 py-1.5 text-sm transition-colors",s.id==="logout"?"text-destructive hover:bg-destructive/10 hover:text-destructive":"hover:bg-accent hover:text-accent-foreground","disabled:opacity-50 disabled:pointer-events-none","border-0 outline-none",t.items[c+1]?.separator&&"border-b-0")),onClick:i=>G(s.id)},[s.icon?(e.openBlock(),e.createBlock(u.default,{key:0,name:s.icon,size:"sm",class:e.normalizeClass(e.unref(v.cn)("flex-shrink-0",s.id==="logout"&&"text-destructive"))},null,8,["name","class"])):e.createCommentVNode("",!0),e.createElementVNode("span",Be,e.toDisplayString(s.label),1)],10,be))],64))),128)),n<V.value.length-1?(e.openBlock(),e.createElementBlock("div",Ce)):e.createCommentVNode("",!0)],64))),128))])]),_:1})):(e.openBlock(),e.createBlock(g.default,{key:3,variant:"ghost",size:"sm","aria-label":"로그인",onClick:J},{default:e.withCtx(()=>[e.createVNode(u.default,{name:"logIn",size:"sm",class:"sm:mr-1.5"}),a[1]||(a[1]=e.createElementVNode("span",{class:"hidden sm:inline"},"로그인",-1))]),_:1})),e.renderSlot(o.$slots,"actions")])],2))}});exports.default=Te;
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("vue"),u=require("../atoms/JIcon.vue.cjs"),K=require("../atoms/JAvatar.vue.cjs"),g=require("../atoms/JButton.vue.cjs"),N=require("../atoms/JPopover.vue.cjs"),v=require("../../lib/utils.cjs"),r=require("../../lib/theme-utils.cjs"),z=require("../../assets/images/logo-fallback.png.cjs"),W={class:"flex items-center gap-6 flex-1"},H=["src"],Q={key:1,class:"text-sm font-bold text-foreground"},X={key:2,class:"flex items-center gap-1"},Z={class:"flex items-center gap-3"},ee={class:"p-2 min-w-[200px]"},te={class:"space-y-1"},oe=["onClick"],le={key:1,class:"w-4"},ne={class:"flex-1 capitalize"},ae={key:0,class:"absolute top-0 right-0 h-4 w-4 rounded-full bg-destructive text-destructive-foreground text-xs flex items-center justify-center"},se={class:"p-2"},re={key:0,class:"max-h-96 overflow-y-auto space-y-1"},ce=["onClick"],ie={class:"flex items-start gap-2"},de={class:"flex-1 min-w-0"},ue={class:"text-xs font-medium text-foreground"},me={key:0,class:"text-xs text-muted-foreground mt-1"},fe={key:1,class:"text-xs text-muted-foreground/60 mt-1"},he={key:1,class:"p-4 text-center text-xs text-muted-foreground"},ke={class:"flex items-center gap-2 cursor-pointer hover:opacity-80 transition-opacity"},ge={class:"text-xs text-foreground hidden sm:inline"},ve={class:"w-full rounded-md overflow-hidden"},pe={class:"px-3 py-1.5 border-b border-border"},xe={class:"text-xs font-medium text-foreground"},be={key:0,class:"text-xs text-muted-foreground mt-0.5"},ye=["disabled","onClick"],Be={class:"flex-1 text-left truncate"},_e={key:1,class:"h-px bg-muted my-1"},Ce={key:0,class:"h-px bg-muted my-1"},we=e.defineComponent({__name:"JHeader",props:{logo:{},logoText:{default:"JWMS Portal"},navItems:{},showNotifications:{type:Boolean,default:!1},notifications:{default:()=>[]},userAvatar:{},userName:{},isLoggedIn:{type:Boolean},userEmail:{},userId:{},styletype:{default:"default"},showSidebarToggle:{type:Boolean,default:!0},isSidebarOpen:{type:Boolean,default:!0},showThemeSelector:{type:Boolean,default:!0},defaultTheme:{default:void 0},availableThemes:{default:void 0}},emits:["logoClick","navClick","notificationClick","userMenuSelect","sidebarToggle","login"],setup(l,{emit:L}){const d=l,f=L,S={default:{containerClass:"h-10 px-4 border-b border-border bg-background",navItemClass:"text-xs text-muted-foreground hover:text-foreground transition-colors px-3 py-1.5 rounded-md hover:bg-accent",navItemActiveClass:"text-foreground font-medium bg-accent"},minimal:{containerClass:"h-8 px-3 border-b border-border bg-background",navItemClass:"text-xs text-muted-foreground hover:text-foreground transition-colors px-2 py-1 rounded-md hover:bg-accent",navItemActiveClass:"text-foreground font-medium bg-accent"}},B=e.computed(()=>S[d.styletype]??S.default),h=e.ref(0),D=e.computed(()=>{if(d.logo)return d.logo}),_=e.computed(()=>{if(d.logo)return h.value>=1&&z.default?z.default:D.value}),O=()=>{h.value===0?h.value=1:h.value===1&&(h.value=2)},C=e.computed(()=>d.notifications?.filter(o=>!o.read).length||0),M=[{id:"profile",label:"프로필",icon:"user"},{id:"settings",label:"설정",icon:"settings"},{id:"separator",label:"",separator:!0},{id:"logout",label:"로그아웃",icon:"logOut"}],V=e.computed(()=>[{items:M}]),A=()=>{f("logoClick")},F=(o,a)=>{o.onClick?.(),f("navClick",o,a)},q=o=>{o.onClick?.(),f("notificationClick",o)},G=o=>{f("userMenuSelect",o)},J=()=>{f("login")},U=()=>{f("sidebarToggle")},m=e.ref(!1),k=e.ref(r.getDefaultTheme()),p=e.ref([]);let x=!1,b=null,y=null;const I=()=>typeof window>"u"?!1:!!(window.location?.href?.includes("storybook")||window.__STORYBOOK_GLOBALS__),w=()=>{const o=r.detectThemeClasses(),a=r.ensureDefaultTheme(o);if(d.availableThemes&&d.availableThemes.length>0){const t=a.filter(n=>d.availableThemes.includes(n));p.value=r.ensureDefaultTheme(t)}else p.value=a},$=()=>{if(typeof window>"u")return"light";const o=localStorage.getItem("theme");return o==="dark"||o==="light"?o:window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"},T=o=>{x=!0;const a=document.documentElement;o==="dark"?(a.classList.add("dark"),m.value=!0):(a.classList.remove("dark"),m.value=!1),localStorage.setItem("theme",o),x=!1},P=()=>{const o=m.value?"light":"dark";T(o)},R=()=>{if(d.defaultTheme)return r.validateThemeExists(d.defaultTheme);const o=r.getStoredTheme("tweakcn-theme");return o?r.validateThemeExists(o):r.getDefaultTheme()},E=o=>{x=!0;const a=r.validateThemeExists(o),t=r.applyTheme(a);if(t){if(k.value=a,r.setStoredTheme(a,"tweakcn-theme"),I())try{const n=window.__STORYBOOK_GLOBALS__;n&&(n.theme=a)}catch{}}else{const n=r.getDefaultTheme();r.applyTheme(n),k.value=n,r.setStoredTheme(n,"tweakcn-theme")}return x=!1,t},Y=o=>{E(String(o))},j=e.computed(()=>m.value?"sun":"moon");return e.onMounted(()=>{w();const o=$();T(o);const a=R();E(a);const t=document.documentElement;m.value=t.classList.contains("dark"),b=new MutationObserver(()=>{if(x)return;const s=t.classList.contains("dark");s!==m.value&&(m.value=s,localStorage.setItem("theme",s?"dark":"light")),w();const c=Array.from(t.classList).find(i=>i.startsWith("theme-"));if(c){const i=c.replace("theme-","");p.value.includes(i)&&(k.value=i)}}),b.observe(document.documentElement,{attributes:!0,attributeFilter:["class"]}),y=new MutationObserver(()=>{w()}),y.observe(document.head,{childList:!0,subtree:!0});let n=null;if(I()){const s=()=>{try{const c=window.__STORYBOOK_GLOBALS__;if(c){if(typeof c.darkMode<"u"){const i=c.darkMode;i!==m.value&&T(i?"dark":"light")}if(c.theme&&c.theme!==k.value){const i=String(c.theme);p.value.includes(i)&&E(i)}}}catch{}};s(),n=setInterval(s,500)}e.onUnmounted(()=>{b&&(b.disconnect(),b=null),y&&(y.disconnect(),y=null),n&&clearInterval(n)})}),(o,a)=>(e.openBlock(),e.createElementBlock("header",{class:e.normalizeClass(e.unref(v.cn)("flex items-center justify-between w-full",B.value.containerClass))},[e.createElementVNode("div",W,[l.showSidebarToggle?(e.openBlock(),e.createBlock(g.default,{key:0,variant:"ghost",size:"sm",class:"flex-shrink-0 w-8 h-8 p-0","aria-label":"사이드바 토글",onClick:U},{default:e.withCtx(()=>[e.createVNode(u.default,{name:"menu",size:"sm"})]),_:1})):e.createCommentVNode("",!0),_.value||l.logoText?(e.openBlock(),e.createElementBlock("div",{key:1,class:"flex items-center cursor-pointer",onClick:A},[_.value&&h.value<2?(e.openBlock(),e.createElementBlock("img",{key:0,src:_.value,alt:"로고",class:"h-6 w-auto",onError:O},null,40,H)):l.logoText?(e.openBlock(),e.createElementBlock("span",Q,e.toDisplayString(l.logoText),1)):e.createCommentVNode("",!0)])):e.createCommentVNode("",!0),l.navItems&&l.navItems.length>0?(e.openBlock(),e.createElementBlock("nav",X,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(l.navItems,(t,n)=>(e.openBlock(),e.createBlock(g.default,{key:n,variant:"ghost",class:e.normalizeClass(e.unref(v.cn)(B.value.navItemClass,t.active&&B.value.navItemActiveClass)),onClick:s=>F(t,n)},{default:e.withCtx(()=>[t.icon?(e.openBlock(),e.createBlock(u.default,{key:0,name:t.icon,size:"sm",class:"mr-1.5"},null,8,["name"])):e.createCommentVNode("",!0),e.createTextVNode(" "+e.toDisplayString(t.label),1)]),_:2},1032,["class","onClick"]))),128))])):e.createCommentVNode("",!0)]),e.createElementVNode("div",Z,[l.showThemeSelector?(e.openBlock(),e.createBlock(N.default,{key:0,position:"bottom",align:"end",styletype:"default-sm"},{trigger:e.withCtx(()=>[e.createVNode(g.default,{variant:"ghost",size:"sm",class:"w-8 h-8 p-0","aria-label":"테마 선택"},{default:e.withCtx(()=>[e.createVNode(u.default,{name:"palette",size:"sm"})]),_:1})]),default:e.withCtx(()=>[e.createElementVNode("div",ee,[a[0]||(a[0]=e.createElementVNode("div",{class:"text-xs font-medium text-muted-foreground px-2 py-1.5 mb-1"}," 테마 선택 ",-1)),e.createElementVNode("div",te,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(p.value,t=>(e.openBlock(),e.createElementBlock("button",{key:t,class:e.normalizeClass(e.unref(v.cn)("w-full flex items-center gap-2 px-2 py-1.5 text-xs rounded-md transition-colors","hover:bg-accent hover:text-accent-foreground","border-0 outline-none text-left",k.value===t&&"bg-accent text-accent-foreground font-medium")),onClick:n=>Y(t)},[k.value===t?(e.openBlock(),e.createBlock(u.default,{key:0,name:"check",size:"sm",class:"flex-shrink-0"})):(e.openBlock(),e.createElementBlock("span",le)),e.createElementVNode("span",ne,e.toDisplayString(t),1)],10,oe))),128))])])]),_:1})):e.createCommentVNode("",!0),l.showNotifications?(e.openBlock(),e.createBlock(N.default,{key:1,position:"bottom",align:"end",styletype:"default-sm"},{trigger:e.withCtx(()=>[e.createVNode(g.default,{variant:"ghost",size:"sm",class:"relative w-8 h-8 p-0","aria-label":"알림"},{default:e.withCtx(()=>[e.createVNode(u.default,{name:"circleAlert",size:"sm"}),C.value>0?(e.openBlock(),e.createElementBlock("span",ae,e.toDisplayString(C.value>9?"9+":C.value),1)):e.createCommentVNode("",!0)]),_:1})]),default:e.withCtx(()=>[e.createElementVNode("div",se,[l.notifications&&l.notifications.length>0?(e.openBlock(),e.createElementBlock("div",re,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(l.notifications,t=>(e.openBlock(),e.createElementBlock("div",{key:t.id,class:e.normalizeClass(e.unref(v.cn)("p-3 rounded-md cursor-pointer transition-colors",t.read?"hover:bg-accent/50":"bg-accent")),onClick:n=>q(t)},[e.createElementVNode("div",ie,[t.icon?(e.openBlock(),e.createBlock(u.default,{key:0,name:t.icon,size:"sm",class:"mt-0.5 flex-shrink-0"},null,8,["name"])):e.createCommentVNode("",!0),e.createElementVNode("div",de,[e.createElementVNode("p",ue,e.toDisplayString(t.title),1),t.message?(e.openBlock(),e.createElementBlock("p",me,e.toDisplayString(t.message),1)):e.createCommentVNode("",!0),t.time?(e.openBlock(),e.createElementBlock("p",fe,e.toDisplayString(t.time),1)):e.createCommentVNode("",!0)])])],10,ce))),128))])):(e.openBlock(),e.createElementBlock("div",he," 알림이 없습니다. "))])]),_:1})):e.createCommentVNode("",!0),e.createVNode(g.default,{variant:"ghost",size:"sm",class:"w-8 h-8 p-0","aria-label":m.value?"라이트 모드로 전환":"다크 모드로 전환",onClick:P},{default:e.withCtx(()=>[e.createVNode(u.default,{name:j.value,size:"sm"},null,8,["name"])]),_:1},8,["aria-label"]),e.renderSlot(o.$slots,"toolbar"),l.userName?(e.openBlock(),e.createBlock(N.default,{key:2,position:"bottom",align:"end",styletype:"default-sm"},{trigger:e.withCtx(()=>[e.createElementVNode("div",ke,[e.createVNode(K.default,{src:l.userAvatar,fallback:l.userName?l.userName[0]:"U",size:"xs"},null,8,["src","fallback"]),e.createElementVNode("span",ge,e.toDisplayString(l.userName),1),e.createVNode(u.default,{name:"chevronDown",size:"sm",class:"text-muted-foreground hidden sm:inline"})])]),default:e.withCtx(()=>[e.createElementVNode("div",ve,[e.createElementVNode("div",pe,[e.createElementVNode("p",xe,e.toDisplayString(l.userName),1),l.userEmail?(e.openBlock(),e.createElementBlock("p",be,e.toDisplayString(l.userEmail),1)):e.createCommentVNode("",!0)]),(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(V.value,(t,n)=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:n},[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(t.items,(s,c)=>(e.openBlock(),e.createElementBlock(e.Fragment,{key:s.id},[s.separator?s.separator&&c>0?(e.openBlock(),e.createElementBlock("div",_e)):e.createCommentVNode("",!0):(e.openBlock(),e.createElementBlock("button",{key:0,disabled:s.disabled,class:e.normalizeClass(e.unref(v.cn)("w-full flex items-center gap-2 px-3 py-1.5 text-xs transition-colors",s.id==="logout"?"text-destructive hover:bg-destructive/10 hover:text-destructive":"hover:bg-accent hover:text-accent-foreground","disabled:opacity-50 disabled:pointer-events-none","border-0 outline-none",t.items[c+1]?.separator&&"border-b-0")),onClick:i=>G(s.id)},[s.icon?(e.openBlock(),e.createBlock(u.default,{key:0,name:s.icon,size:"sm",class:e.normalizeClass(e.unref(v.cn)("flex-shrink-0",s.id==="logout"&&"text-destructive"))},null,8,["name","class"])):e.createCommentVNode("",!0),e.createElementVNode("span",Be,e.toDisplayString(s.label),1)],10,ye))],64))),128)),n<V.value.length-1?(e.openBlock(),e.createElementBlock("div",Ce)):e.createCommentVNode("",!0)],64))),128))])]),_:1})):(e.openBlock(),e.createBlock(g.default,{key:3,variant:"ghost",size:"sm","aria-label":"로그인",onClick:J},{default:e.withCtx(()=>[e.createVNode(u.default,{name:"logIn",size:"sm",class:"sm:mr-1.5"}),a[1]||(a[1]=e.createElementVNode("span",{class:"hidden sm:inline"},"로그인",-1))]),_:1}))])],2))}});exports.default=we;
2
2
  //# sourceMappingURL=JHeader.vue.cjs.map