moru-krds-react 0.1.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 (247) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +61 -0
  3. package/dist/accordion/index.d.ts +63 -0
  4. package/dist/accordion/index.d.ts.map +1 -0
  5. package/dist/accordion/index.js +145 -0
  6. package/dist/accordion/index.js.map +1 -0
  7. package/dist/alert/index.d.ts +44 -0
  8. package/dist/alert/index.d.ts.map +1 -0
  9. package/dist/alert/index.js +66 -0
  10. package/dist/alert/index.js.map +1 -0
  11. package/dist/avatar/index.d.ts +102 -0
  12. package/dist/avatar/index.d.ts.map +1 -0
  13. package/dist/avatar/index.js +146 -0
  14. package/dist/avatar/index.js.map +1 -0
  15. package/dist/badge/index.d.ts +49 -0
  16. package/dist/badge/index.d.ts.map +1 -0
  17. package/dist/badge/index.js +41 -0
  18. package/dist/badge/index.js.map +1 -0
  19. package/dist/breadcrumb/index.d.ts +43 -0
  20. package/dist/breadcrumb/index.d.ts.map +1 -0
  21. package/dist/breadcrumb/index.js +43 -0
  22. package/dist/breadcrumb/index.js.map +1 -0
  23. package/dist/button/index.d.ts +78 -0
  24. package/dist/button/index.d.ts.map +1 -0
  25. package/dist/button/index.js +83 -0
  26. package/dist/button/index.js.map +1 -0
  27. package/dist/checkbox/index.d.ts +97 -0
  28. package/dist/checkbox/index.d.ts.map +1 -0
  29. package/dist/checkbox/index.js +110 -0
  30. package/dist/checkbox/index.js.map +1 -0
  31. package/dist/combobox/index.d.ts +135 -0
  32. package/dist/combobox/index.d.ts.map +1 -0
  33. package/dist/combobox/index.js +416 -0
  34. package/dist/combobox/index.js.map +1 -0
  35. package/dist/context-menu/index.d.ts +94 -0
  36. package/dist/context-menu/index.d.ts.map +1 -0
  37. package/dist/context-menu/index.js +399 -0
  38. package/dist/context-menu/index.js.map +1 -0
  39. package/dist/critical-alert/index.d.ts +39 -0
  40. package/dist/critical-alert/index.d.ts.map +1 -0
  41. package/dist/critical-alert/index.js +50 -0
  42. package/dist/critical-alert/index.js.map +1 -0
  43. package/dist/data-table/index.d.ts +94 -0
  44. package/dist/data-table/index.d.ts.map +1 -0
  45. package/dist/data-table/index.js +128 -0
  46. package/dist/data-table/index.js.map +1 -0
  47. package/dist/disclosure/index.d.ts +35 -0
  48. package/dist/disclosure/index.d.ts.map +1 -0
  49. package/dist/disclosure/index.js +54 -0
  50. package/dist/disclosure/index.js.map +1 -0
  51. package/dist/drawer/index.d.ts +106 -0
  52. package/dist/drawer/index.d.ts.map +1 -0
  53. package/dist/drawer/index.js +352 -0
  54. package/dist/drawer/index.js.map +1 -0
  55. package/dist/empty-state/index.d.ts +86 -0
  56. package/dist/empty-state/index.d.ts.map +1 -0
  57. package/dist/empty-state/index.js +138 -0
  58. package/dist/empty-state/index.js.map +1 -0
  59. package/dist/env.d.js +1 -0
  60. package/dist/env.d.js.map +1 -0
  61. package/dist/field/index.d.ts +149 -0
  62. package/dist/field/index.d.ts.map +1 -0
  63. package/dist/field/index.js +121 -0
  64. package/dist/field/index.js.map +1 -0
  65. package/dist/file-upload/index.d.ts +140 -0
  66. package/dist/file-upload/index.d.ts.map +1 -0
  67. package/dist/file-upload/index.js +249 -0
  68. package/dist/file-upload/index.js.map +1 -0
  69. package/dist/footer/index.d.ts +155 -0
  70. package/dist/footer/index.d.ts.map +1 -0
  71. package/dist/footer/index.js +186 -0
  72. package/dist/footer/index.js.map +1 -0
  73. package/dist/header/index.d.ts +170 -0
  74. package/dist/header/index.d.ts.map +1 -0
  75. package/dist/header/index.js +258 -0
  76. package/dist/header/index.js.map +1 -0
  77. package/dist/identifier/index.d.ts +38 -0
  78. package/dist/identifier/index.d.ts.map +1 -0
  79. package/dist/identifier/index.js +55 -0
  80. package/dist/identifier/index.js.map +1 -0
  81. package/dist/in-page-navigation/index.d.ts +71 -0
  82. package/dist/in-page-navigation/index.d.ts.map +1 -0
  83. package/dist/in-page-navigation/index.js +137 -0
  84. package/dist/in-page-navigation/index.js.map +1 -0
  85. package/dist/index.d.ts +57 -0
  86. package/dist/index.d.ts.map +1 -0
  87. package/dist/index.js +47 -0
  88. package/dist/index.js.map +1 -0
  89. package/dist/input-group/index.d.ts +66 -0
  90. package/dist/input-group/index.d.ts.map +1 -0
  91. package/dist/input-group/index.js +85 -0
  92. package/dist/input-group/index.js.map +1 -0
  93. package/dist/lib/krds.d.ts +45 -0
  94. package/dist/lib/krds.d.ts.map +1 -0
  95. package/dist/lib/krds.js +11 -0
  96. package/dist/lib/krds.js.map +1 -0
  97. package/dist/lib/link.d.ts +34 -0
  98. package/dist/lib/link.d.ts.map +1 -0
  99. package/dist/lib/link.js +8 -0
  100. package/dist/lib/link.js.map +1 -0
  101. package/dist/main-menu/index.d.ts +123 -0
  102. package/dist/main-menu/index.d.ts.map +1 -0
  103. package/dist/main-menu/index.js +613 -0
  104. package/dist/main-menu/index.js.map +1 -0
  105. package/dist/masthead/index.d.ts +25 -0
  106. package/dist/masthead/index.d.ts.map +1 -0
  107. package/dist/masthead/index.js +21 -0
  108. package/dist/masthead/index.js.map +1 -0
  109. package/dist/modal/index.d.ts +105 -0
  110. package/dist/modal/index.d.ts.map +1 -0
  111. package/dist/modal/index.js +285 -0
  112. package/dist/modal/index.js.map +1 -0
  113. package/dist/pagination/index.d.ts +58 -0
  114. package/dist/pagination/index.d.ts.map +1 -0
  115. package/dist/pagination/index.js +82 -0
  116. package/dist/pagination/index.js.map +1 -0
  117. package/dist/progress/index.d.ts +58 -0
  118. package/dist/progress/index.d.ts.map +1 -0
  119. package/dist/progress/index.js +125 -0
  120. package/dist/progress/index.js.map +1 -0
  121. package/dist/radio/index.d.ts +111 -0
  122. package/dist/radio/index.d.ts.map +1 -0
  123. package/dist/radio/index.js +136 -0
  124. package/dist/radio/index.js.map +1 -0
  125. package/dist/resize/index.d.ts +118 -0
  126. package/dist/resize/index.d.ts.map +1 -0
  127. package/dist/resize/index.js +342 -0
  128. package/dist/resize/index.js.map +1 -0
  129. package/dist/scroll-area/index.d.ts +40 -0
  130. package/dist/scroll-area/index.d.ts.map +1 -0
  131. package/dist/scroll-area/index.js +52 -0
  132. package/dist/scroll-area/index.js.map +1 -0
  133. package/dist/select/index.d.ts +67 -0
  134. package/dist/select/index.d.ts.map +1 -0
  135. package/dist/select/index.js +61 -0
  136. package/dist/select/index.js.map +1 -0
  137. package/dist/separator/index.d.ts +47 -0
  138. package/dist/separator/index.d.ts.map +1 -0
  139. package/dist/separator/index.js +61 -0
  140. package/dist/separator/index.js.map +1 -0
  141. package/dist/side-navigation/index.d.ts +78 -0
  142. package/dist/side-navigation/index.d.ts.map +1 -0
  143. package/dist/side-navigation/index.js +188 -0
  144. package/dist/side-navigation/index.js.map +1 -0
  145. package/dist/skeleton/index.d.ts +76 -0
  146. package/dist/skeleton/index.d.ts.map +1 -0
  147. package/dist/skeleton/index.js +78 -0
  148. package/dist/skeleton/index.js.map +1 -0
  149. package/dist/skip-link/index.d.ts +46 -0
  150. package/dist/skip-link/index.d.ts.map +1 -0
  151. package/dist/skip-link/index.js +37 -0
  152. package/dist/skip-link/index.js.map +1 -0
  153. package/dist/spinner/index.d.ts +58 -0
  154. package/dist/spinner/index.d.ts.map +1 -0
  155. package/dist/spinner/index.js +36 -0
  156. package/dist/spinner/index.js.map +1 -0
  157. package/dist/step-indicator/index.d.ts +41 -0
  158. package/dist/step-indicator/index.d.ts.map +1 -0
  159. package/dist/step-indicator/index.js +63 -0
  160. package/dist/step-indicator/index.js.map +1 -0
  161. package/dist/structured-list/index.d.ts +150 -0
  162. package/dist/structured-list/index.d.ts.map +1 -0
  163. package/dist/structured-list/index.js +194 -0
  164. package/dist/structured-list/index.js.map +1 -0
  165. package/dist/tab/index.d.ts +108 -0
  166. package/dist/tab/index.d.ts.map +1 -0
  167. package/dist/tab/index.js +198 -0
  168. package/dist/tab/index.js.map +1 -0
  169. package/dist/table/index.d.ts +75 -0
  170. package/dist/table/index.d.ts.map +1 -0
  171. package/dist/table/index.js +91 -0
  172. package/dist/table/index.js.map +1 -0
  173. package/dist/tag/index.d.ts +71 -0
  174. package/dist/tag/index.d.ts.map +1 -0
  175. package/dist/tag/index.js +45 -0
  176. package/dist/tag/index.js.map +1 -0
  177. package/dist/text-input/index.d.ts +69 -0
  178. package/dist/text-input/index.d.ts.map +1 -0
  179. package/dist/text-input/index.js +70 -0
  180. package/dist/text-input/index.js.map +1 -0
  181. package/dist/text-list/index.d.ts +71 -0
  182. package/dist/text-list/index.d.ts.map +1 -0
  183. package/dist/text-list/index.js +79 -0
  184. package/dist/text-list/index.js.map +1 -0
  185. package/dist/textarea/index.d.ts +29 -0
  186. package/dist/textarea/index.d.ts.map +1 -0
  187. package/dist/textarea/index.js +69 -0
  188. package/dist/textarea/index.js.map +1 -0
  189. package/dist/toast/index.d.ts +76 -0
  190. package/dist/toast/index.d.ts.map +1 -0
  191. package/dist/toast/index.js +183 -0
  192. package/dist/toast/index.js.map +1 -0
  193. package/dist/tooltip/index.d.ts +84 -0
  194. package/dist/tooltip/index.d.ts.map +1 -0
  195. package/dist/tooltip/index.js +329 -0
  196. package/dist/tooltip/index.js.map +1 -0
  197. package/package.json +57 -0
  198. package/src/accordion/index.tsx +251 -0
  199. package/src/alert/index.tsx +111 -0
  200. package/src/avatar/index.tsx +272 -0
  201. package/src/badge/index.tsx +91 -0
  202. package/src/breadcrumb/index.tsx +125 -0
  203. package/src/button/index.tsx +190 -0
  204. package/src/checkbox/index.tsx +247 -0
  205. package/src/combobox/index.tsx +700 -0
  206. package/src/context-menu/index.tsx +623 -0
  207. package/src/critical-alert/index.tsx +102 -0
  208. package/src/data-table/index.tsx +289 -0
  209. package/src/disclosure/index.tsx +87 -0
  210. package/src/drawer/index.tsx +516 -0
  211. package/src/empty-state/index.tsx +256 -0
  212. package/src/env.d.ts +10 -0
  213. package/src/field/index.tsx +307 -0
  214. package/src/file-upload/index.tsx +492 -0
  215. package/src/footer/index.tsx +393 -0
  216. package/src/header/index.tsx +498 -0
  217. package/src/identifier/index.tsx +96 -0
  218. package/src/in-page-navigation/index.tsx +258 -0
  219. package/src/index.ts +72 -0
  220. package/src/input-group/index.tsx +197 -0
  221. package/src/lib/krds.ts +75 -0
  222. package/src/lib/link.tsx +35 -0
  223. package/src/main-menu/index.tsx +912 -0
  224. package/src/masthead/index.tsx +50 -0
  225. package/src/modal/index.tsx +457 -0
  226. package/src/pagination/index.tsx +198 -0
  227. package/src/progress/index.tsx +198 -0
  228. package/src/radio/index.tsx +283 -0
  229. package/src/resize/index.tsx +541 -0
  230. package/src/scroll-area/index.tsx +96 -0
  231. package/src/select/index.tsx +136 -0
  232. package/src/separator/index.tsx +107 -0
  233. package/src/side-navigation/index.tsx +336 -0
  234. package/src/skeleton/index.tsx +172 -0
  235. package/src/skip-link/index.tsx +89 -0
  236. package/src/spinner/index.tsx +98 -0
  237. package/src/step-indicator/index.tsx +122 -0
  238. package/src/structured-list/index.tsx +395 -0
  239. package/src/tab/index.tsx +348 -0
  240. package/src/table/index.tsx +186 -0
  241. package/src/tag/index.tsx +127 -0
  242. package/src/text-input/index.tsx +156 -0
  243. package/src/text-list/index.tsx +177 -0
  244. package/src/textarea/index.tsx +113 -0
  245. package/src/toast/index.tsx +311 -0
  246. package/src/tooltip/index.tsx +496 -0
  247. package/styles/krds-tailwind.css +335 -0
@@ -0,0 +1,399 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import {
4
+ useCallback,
5
+ useEffect,
6
+ useId,
7
+ useRef,
8
+ useState
9
+ } from "react";
10
+ import { Button } from "../button/index.js";
11
+ import { cx } from "../lib/krds.js";
12
+ function alignClass(align) {
13
+ return align === "left" || align === "right" ? `drop-${align}` : void 0;
14
+ }
15
+ function itemText(item) {
16
+ return (item.text ?? (typeof item.label === "string" ? item.label : "")).trim().toLowerCase();
17
+ }
18
+ const TYPEAHEAD_RESET_MS = 500;
19
+ const VIEWPORT_GUTTER_PX = 8;
20
+ function useMenuController() {
21
+ const [open, setOpen] = useState(false);
22
+ const wrapRef = useRef(null);
23
+ const menuRef = useRef(null);
24
+ const returnFocusRef = useRef(null);
25
+ const initialFocusRef = useRef("first");
26
+ const items = useCallback(
27
+ () => Array.from(menuRef.current?.querySelectorAll('[role="menuitem"]') ?? []),
28
+ []
29
+ );
30
+ const openMenu = useCallback((initial = "first", returnTo) => {
31
+ initialFocusRef.current = initial;
32
+ returnFocusRef.current = returnTo ?? document.activeElement ?? null;
33
+ setOpen(true);
34
+ }, []);
35
+ const close = useCallback((refocus = false) => {
36
+ setOpen(false);
37
+ if (refocus) returnFocusRef.current?.focus();
38
+ }, []);
39
+ useEffect(() => {
40
+ if (!open) return;
41
+ const list = items();
42
+ if (!list.length) return;
43
+ (initialFocusRef.current === "last" ? list[list.length - 1] : list[0]).focus();
44
+ }, [open, items]);
45
+ useEffect(() => {
46
+ if (!open) return;
47
+ const onPointerDown = (e) => {
48
+ if (!wrapRef.current?.contains(e.target)) setOpen(false);
49
+ };
50
+ document.addEventListener("pointerdown", onPointerDown);
51
+ return () => document.removeEventListener("pointerdown", onPointerDown);
52
+ }, [open]);
53
+ return { open, openMenu, close, wrapRef, menuRef, items };
54
+ }
55
+ function mergeRef(inner, outer) {
56
+ return (node) => {
57
+ inner.current = node;
58
+ if (typeof outer === "function") outer(node);
59
+ else if (outer) outer.current = node;
60
+ };
61
+ }
62
+ function useOpenChange(open, onOpenChange) {
63
+ const latest = useRef(onOpenChange);
64
+ const mounted = useRef(false);
65
+ useEffect(() => {
66
+ latest.current = onOpenChange;
67
+ });
68
+ useEffect(() => {
69
+ if (!mounted.current) {
70
+ mounted.current = true;
71
+ return;
72
+ }
73
+ latest.current?.(open);
74
+ }, [open]);
75
+ }
76
+ function MenuList({ id, items, label, labelledBy, menuRef, onKeyDown, onActivate }) {
77
+ return /* @__PURE__ */ jsx(
78
+ "ul",
79
+ {
80
+ ref: menuRef,
81
+ id,
82
+ role: "menu",
83
+ "aria-label": labelledBy ? void 0 : label,
84
+ "aria-labelledby": labelledBy,
85
+ className: "drop-list",
86
+ onKeyDown,
87
+ children: items.map((item, index) => {
88
+ if (item.separator) {
89
+ return /* @__PURE__ */ jsx(
90
+ "li",
91
+ {
92
+ role: "separator",
93
+ className: "my-1 h-px bg-divider"
94
+ },
95
+ item.id ?? `separator-${index}`
96
+ );
97
+ }
98
+ const { id: itemId, label: itemLabel, icon, href, target, disabled, danger, active } = item;
99
+ const cls = cx(
100
+ "item-link",
101
+ active && "active",
102
+ // ํ‚ท์— ์œ„ํ—˜ยท๋น„ํ™œ์„ฑ ํ•ญ๋ชฉ ๋ชจ์–‘์ด ์—†๋‹ค โ€” ์œ ํ‹ธ๋ฆฌํ‹ฐ๋กœ ๋ณด์ถฉํ•œ๋‹ค.
103
+ danger && !disabled && "text-danger",
104
+ disabled && "text-fg-disabled cursor-not-allowed hover:bg-transparent"
105
+ );
106
+ const iconNode = icon ? /* @__PURE__ */ jsx(
107
+ "i",
108
+ {
109
+ className: cx(
110
+ "svg-icon",
111
+ `ico-${icon}`,
112
+ danger && !disabled && "bg-danger",
113
+ disabled && "bg-fg-disabled"
114
+ ),
115
+ "aria-hidden": "true"
116
+ }
117
+ ) : null;
118
+ const shared = {
119
+ role: "menuitem",
120
+ // ๋ฉ”๋‰ด ์•ˆ์—์„œ๋Š” โ†‘โ†“ ๋กœ๋งŒ ์˜ค๊ฐ„๋‹ค(roving tabindex). ์ดˆ์ ์€ ๋ถ€ํ’ˆ์ด ์˜ฎ๊ธด๋‹ค.
121
+ tabIndex: -1,
122
+ className: cls,
123
+ // ๋น„ํ™œ์„ฑ์€ disabled ์†์„ฑ์ด ์•„๋‹ˆ๋ผ aria-disabled ๋‹ค โ€” ์ดˆ์ ์€ ๋ฐ›์•„์•ผ ํ•œ๋‹ค(APG).
124
+ "aria-disabled": disabled || void 0,
125
+ "aria-current": active ? "true" : void 0
126
+ };
127
+ return /* @__PURE__ */ jsx("li", { role: "none", children: href !== void 0 ? /* @__PURE__ */ jsxs(
128
+ "a",
129
+ {
130
+ ...shared,
131
+ href: disabled ? void 0 : href,
132
+ target,
133
+ title: target === "_blank" ? "\uC0C8 \uCC3D \uC5F4\uB9BC" : void 0,
134
+ onClick: (e) => {
135
+ if (disabled) {
136
+ e.preventDefault();
137
+ return;
138
+ }
139
+ onActivate(item);
140
+ },
141
+ children: [
142
+ iconNode,
143
+ itemLabel
144
+ ]
145
+ }
146
+ ) : /* @__PURE__ */ jsxs(
147
+ "button",
148
+ {
149
+ ...shared,
150
+ type: "button",
151
+ onClick: () => {
152
+ if (disabled) return;
153
+ onActivate(item);
154
+ },
155
+ children: [
156
+ iconNode,
157
+ itemLabel
158
+ ]
159
+ }
160
+ ) }, itemId);
161
+ })
162
+ }
163
+ );
164
+ }
165
+ function useMenuKeyDown({ items, close }, entries) {
166
+ const buffer = useRef("");
167
+ const timer = useRef(null);
168
+ useEffect(
169
+ () => () => {
170
+ if (timer.current) clearTimeout(timer.current);
171
+ },
172
+ []
173
+ );
174
+ const focusAt = (list, index) => {
175
+ if (!list.length) return;
176
+ list[(index + list.length) % list.length].focus();
177
+ };
178
+ return (e) => {
179
+ const list = items();
180
+ const at = list.indexOf(document.activeElement);
181
+ switch (e.key) {
182
+ case "ArrowDown":
183
+ e.preventDefault();
184
+ focusAt(list, at + 1);
185
+ return;
186
+ case "ArrowUp":
187
+ e.preventDefault();
188
+ focusAt(list, at - 1);
189
+ return;
190
+ case "Home":
191
+ e.preventDefault();
192
+ focusAt(list, 0);
193
+ return;
194
+ case "End":
195
+ e.preventDefault();
196
+ focusAt(list, list.length - 1);
197
+ return;
198
+ case "Escape":
199
+ e.stopPropagation();
200
+ e.preventDefault();
201
+ close(true);
202
+ return;
203
+ case "Tab":
204
+ close();
205
+ return;
206
+ default:
207
+ break;
208
+ }
209
+ if (e.key.length !== 1 || e.altKey || e.ctrlKey || e.metaKey) return;
210
+ const key = e.key.toLowerCase();
211
+ if (key === " " && !buffer.current) return;
212
+ e.preventDefault();
213
+ buffer.current += key;
214
+ if (timer.current) clearTimeout(timer.current);
215
+ timer.current = setTimeout(() => {
216
+ buffer.current = "";
217
+ }, TYPEAHEAD_RESET_MS);
218
+ const actions = entries.filter((i) => !i.separator);
219
+ const from = at < 0 ? 0 : at + (buffer.current.length > 1 ? 0 : 1);
220
+ for (let n = 0; n < actions.length; n += 1) {
221
+ const i = (from + n) % actions.length;
222
+ if (itemText(actions[i]).startsWith(buffer.current)) {
223
+ focusAt(list, i);
224
+ return;
225
+ }
226
+ }
227
+ };
228
+ }
229
+ function ContextMenu({
230
+ items,
231
+ label,
232
+ triggerLabel,
233
+ icon = "more",
234
+ size = "small",
235
+ align = "right",
236
+ onSelect,
237
+ onOpenChange,
238
+ triggerClassName,
239
+ className,
240
+ ref,
241
+ ...rest
242
+ }) {
243
+ const ctl = useMenuController();
244
+ const triggerRef = useRef(null);
245
+ const baseId = useId();
246
+ const menuId = `${baseId}-menu`;
247
+ const triggerId = `${baseId}-trigger`;
248
+ const onKeyDownMenu = useMenuKeyDown({ items: ctl.items, close: ctl.close }, items);
249
+ useOpenChange(ctl.open, onOpenChange);
250
+ const toggle = () => {
251
+ if (ctl.open) ctl.close(true);
252
+ else ctl.openMenu("first", triggerRef.current);
253
+ };
254
+ const onTriggerKeyDown = (e) => {
255
+ if (e.key !== "ArrowDown" && e.key !== "ArrowUp") return;
256
+ e.preventDefault();
257
+ ctl.openMenu(e.key === "ArrowDown" ? "first" : "last", triggerRef.current);
258
+ };
259
+ const triggerProps = {
260
+ id: triggerId,
261
+ ref: triggerRef,
262
+ size,
263
+ variant: "text",
264
+ // drop-btn ์€ ํ‚ท CSS ์— ์—†๋Š” ์†์žก์ด ํด๋ž˜์Šค์ง€๋งŒ ์ƒ˜ํ”Œ์— ์žˆ์œผ๋ฏ€๋กœ ๋‚จ๊ธด๋‹ค.
265
+ className: cx("drop-btn", triggerClassName),
266
+ "aria-haspopup": "true",
267
+ "aria-expanded": ctl.open,
268
+ "aria-controls": menuId,
269
+ onClick: toggle,
270
+ onKeyDown: onTriggerKeyDown
271
+ };
272
+ return /* @__PURE__ */ jsxs(
273
+ "div",
274
+ {
275
+ ...rest,
276
+ ref: mergeRef(ctl.wrapRef, ref),
277
+ className: cx("krds-drop-wrap", alignClass(align), className),
278
+ onKeyDown: (e) => {
279
+ if (e.key === "Escape" && ctl.open) {
280
+ e.stopPropagation();
281
+ ctl.close(true);
282
+ }
283
+ },
284
+ children: [
285
+ triggerLabel === void 0 ? /* @__PURE__ */ jsx(Button, { iconOnly: true, icon, label, ...triggerProps }) : /* @__PURE__ */ jsx(Button, { icon: "toggle", ...triggerProps, children: triggerLabel }),
286
+ /* @__PURE__ */ jsx("div", { className: "drop-menu", style: ctl.open ? { display: "block" } : void 0, children: /* @__PURE__ */ jsx("div", { className: "drop-in", children: /* @__PURE__ */ jsx(
287
+ MenuList,
288
+ {
289
+ id: menuId,
290
+ items,
291
+ labelledBy: triggerId,
292
+ menuRef: ctl.menuRef,
293
+ onKeyDown: onKeyDownMenu,
294
+ onActivate: (item) => {
295
+ ctl.close(true);
296
+ item.onSelect?.(item.id);
297
+ onSelect?.(item.id);
298
+ }
299
+ }
300
+ ) }) })
301
+ ]
302
+ }
303
+ );
304
+ }
305
+ function ContextMenuArea({
306
+ items,
307
+ label,
308
+ onSelect,
309
+ onOpenChange,
310
+ disabled,
311
+ className,
312
+ children,
313
+ ref,
314
+ ...rest
315
+ }) {
316
+ const ctl = useMenuController();
317
+ const menuId = useId();
318
+ const [pos, setPos] = useState({ x: 0, y: 0 });
319
+ const [side, setSide] = useState("left");
320
+ const anchorRef = useRef(null);
321
+ const onKeyDownMenu = useMenuKeyDown({ items: ctl.items, close: ctl.close }, items);
322
+ useOpenChange(ctl.open, onOpenChange);
323
+ useEffect(() => {
324
+ if (!ctl.open) return;
325
+ const anchor = anchorRef.current;
326
+ const box = anchor?.firstElementChild?.getBoundingClientRect();
327
+ if (!anchor || !box) return;
328
+ const over = box.bottom - (window.innerHeight - VIEWPORT_GUTTER_PX);
329
+ if (over > 0) anchor.style.top = `${pos.y - over}px`;
330
+ }, [ctl.open, pos.x, pos.y]);
331
+ const openAt = (clientX, clientY) => {
332
+ const rect = ctl.wrapRef.current?.getBoundingClientRect();
333
+ if (!rect) return;
334
+ setPos({ x: clientX - rect.left, y: clientY - rect.top });
335
+ setSide(clientX > window.innerWidth / 2 ? "right" : "left");
336
+ ctl.openMenu("first");
337
+ };
338
+ const onContextMenu = (e) => {
339
+ if (disabled) return;
340
+ e.preventDefault();
341
+ openAt(e.clientX, e.clientY);
342
+ };
343
+ const onKeyDown = (e) => {
344
+ if (ctl.open) {
345
+ if (e.key === "Escape") {
346
+ e.stopPropagation();
347
+ ctl.close(true);
348
+ }
349
+ return;
350
+ }
351
+ if (disabled) return;
352
+ if (e.key !== "ContextMenu" && !(e.key === "F10" && e.shiftKey)) return;
353
+ e.preventDefault();
354
+ const box = e.target.getBoundingClientRect?.();
355
+ if (box) openAt(box.left, box.bottom);
356
+ };
357
+ return /* @__PURE__ */ jsxs(
358
+ "div",
359
+ {
360
+ ...rest,
361
+ ref: mergeRef(ctl.wrapRef, ref),
362
+ className: cx("krds-drop-wrap block", ctl.open && `drop-${side}`, className),
363
+ onContextMenu,
364
+ onKeyDown,
365
+ children: [
366
+ children,
367
+ ctl.open && // 0 ํฌ๊ธฐ ์•ต์ปค. ๋ฉ”๋‰ด์˜ ๊ธฐ์ค€์ ์ผ ๋ฟ์ด๋ผ ํ™”๋ฉด์—๋„ ์Šคํฌ๋ฆฐ๋ฆฌ๋”์—๋„ ์žกํžˆ์ง€ ์•Š๋Š”๋‹ค.
368
+ /* @__PURE__ */ jsx(
369
+ "div",
370
+ {
371
+ ref: anchorRef,
372
+ className: "absolute",
373
+ style: { left: pos.x, top: pos.y, width: 0, height: 0 },
374
+ children: /* @__PURE__ */ jsx("div", { className: "drop-menu", style: { display: "block" }, children: /* @__PURE__ */ jsx("div", { className: "drop-in", children: /* @__PURE__ */ jsx(
375
+ MenuList,
376
+ {
377
+ id: menuId,
378
+ items,
379
+ label,
380
+ menuRef: ctl.menuRef,
381
+ onKeyDown: onKeyDownMenu,
382
+ onActivate: (item) => {
383
+ ctl.close(true);
384
+ item.onSelect?.(item.id);
385
+ onSelect?.(item.id);
386
+ }
387
+ }
388
+ ) }) })
389
+ }
390
+ )
391
+ ]
392
+ }
393
+ );
394
+ }
395
+ export {
396
+ ContextMenu,
397
+ ContextMenuArea
398
+ };
399
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/context-menu/index.tsx"],"sourcesContent":["'use client';\n\nimport {\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n type ComponentPropsWithRef,\n type KeyboardEvent as ReactKeyboardEvent,\n type MouseEvent as ReactMouseEvent,\n type ReactNode,\n type Ref,\n} from 'react';\nimport { Button } from '../button';\nimport { cx, type KrdsIcon, type KrdsSize } from '../lib/krds';\n\n/**\n * ์ƒํ™ฉ ๋ฉ”๋‰ด(๋”๋ณด๊ธฐ ยท ์šฐํด๋ฆญ) โ€” KRDS ์— ์—†๋Š” ๋ณด์ถฉ ๋ถ€ํ’ˆ์ด๋‹ค.\n *\n * ๋ชจ์–‘์€ ํ‚ท ๋“œ๋กญ๋‹ค์šด(`krds-drop-wrap`), ๋™์ž‘์€ WAI-ARIA APG ์˜ Menu Button ํŒจํ„ด์ด๋‹ค.\n * ํ‘œ ํ•œ ์นธ์— ๋Š˜์–ด์„  ํ–‰ ๋™์ž‘์„ ํ•˜๋‚˜๋กœ ์ ‘์„ ๋•Œ ์“ด๋‹ค โ€” ใ€Œ์‚ญ์ œใ€๋Š” `danger` ๋กœ ํ‘œ์‹œํ•˜๊ณ \n * ๊ตฌ๋ถ„์„ (`{ separator: true }`)์œผ๋กœ ๋‚˜๋จธ์ง€์™€ ๋–ผ์–ด ๋†“๋Š”๋‹ค.\n * **๋™์ž‘ ๋ชฉ๋ก์—๋งŒ ์“ด๋‹ค.** ๋ฉ”๋‰ด๋Š” Tab ์„ ๊ฐ€๋กœ๋ง‰์œผ๋ฏ€๋กœ ์–ธ์–ด ์ „ํ™˜ยทํ™”๋ฉด ํฌ๊ธฐ์ฒ˜๋Ÿผ Tab ์œผ๋กœ ๋‹ฟ์•„์•ผ\n * ํ•˜๋Š” ๋ชฉ๋ก์—๋Š” `ui/resize` ์˜ ๋“œ๋กญ๋‹ค์šด์„ ์“ด๋‹ค. ํ•˜์œ„ ๋ฉ”๋‰ด(2๋‹จ)๋Š” ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค.\n *\n * @example\n * <ContextMenu\n * label={`${row.name} ํ–‰ ๋”๋ณด๊ธฐ`}\n * items={[\n * { id: 'edit', label: '์ˆ˜์ •', icon: 'setting', onSelect: () => edit(row) },\n * { separator: true },\n * { id: 'delete', label: '์‚ญ์ œ', icon: 'delete', danger: true, onSelect: () => remove(row) },\n * ]}\n * />\n *\n * ์ž์„ธํžˆ: docs/krds/09-๋ถ€ํ’ˆ-๋…ธํŠธ.md#์ปจํ…์ŠคํŠธ๋ฉ”๋‰ด\n */\n\n/* โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * 1. ํ•ญ๋ชฉ\n * โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */\n\n/** ์‹คํ–‰ ํ•ญ๋ชฉ. `href` ๊ฐ€ ์žˆ์œผ๋ฉด ๋งํฌ(`<a>`), ์—†์œผ๋ฉด ๋‹จ์ถ”(`<button>`)๋กœ ๊ทธ๋ฆฐ๋‹ค. */\nexport type ContextMenuAction = {\n id: string;\n label: ReactNode;\n /** ๊ธ€์ž ํƒ์ƒ‰์— ์“ธ ๋ฌธ์ž์—ด. `label` ์ด ๋ฌธ์ž์—ด์ด๋ฉด ํ•„์š” ์—†๋‹ค. */\n text?: string;\n /** ํ‚ท ์•„์ด์ฝ˜ ์ด๋ฆ„(`ico-` ๋บ€ ๊ฒƒ). ์˜ˆ: `delete` ยท `setting` ยท `download` */\n icon?: KrdsIcon;\n /** ์ด๋™์ด๋ฉด ๋งํฌ๋กœ ๊ทธ๋ฆฐ๋‹ค. ๋™์ž‘์—๋Š” ์ฃผ์ง€ ์•Š๋Š”๋‹ค. */\n href?: string;\n target?: string;\n /** ์ดˆ์ ์€ ๋ฐ›๋˜ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค(`aria-disabled`). */\n disabled?: boolean;\n /** ๋˜๋Œ๋ฆด ์ˆ˜ ์—†๋Š” ๋™์ž‘(์‚ญ์ œ ๋“ฑ). ๊ธ€์ž๋ฅผ ์œ„ํ—˜์ƒ‰์œผ๋กœ ๋ฌผ๋“ค์ธ๋‹ค. */\n danger?: boolean;\n /** ํ˜„์žฌ ์ƒํƒœ ํ‘œ์‹œ(ํ‚ท `.active`). ์ •๋ ฌ ๊ธฐ์ค€์ฒ˜๋Ÿผ ใ€Œ์ง€๊ธˆ ์ด๊ฒƒใ€์„ ๋ณด์ผ ๋•Œ. */\n active?: boolean;\n onSelect?: (id: string) => void;\n separator?: never;\n};\n\n/** ๊ตฌ๋ถ„์„ . ใ€Œ์‚ญ์ œใ€์ฒ˜๋Ÿผ ๋˜๋Œ๋ฆด ์ˆ˜ ์—†๋Š” ํ•ญ๋ชฉ์„ ๋‚˜๋จธ์ง€์™€ ๋–ผ์–ด ๋†“๋Š”๋‹ค. */\nexport type ContextMenuSeparatorItem = {\n separator: true;\n /** ๋ชฉ๋ก key ์šฉ. ์—†์œผ๋ฉด ์ˆœ๋ฒˆ์œผ๋กœ ๋งŒ๋“ ๋‹ค. */\n id?: string;\n};\n\nexport type ContextMenuItem = ContextMenuAction | ContextMenuSeparatorItem;\n\n/** ํ‚ท์˜ ๋“œ๋กญ๋‹ค์šด ์ •๋ ฌ ํด๋ž˜์Šค(`.drop-left` ยท `.drop-right`). ์ƒ๋žตํ•˜๋ฉด ๊ฐ€์šด๋ฐ(ํ‚ท ๊ธฐ๋ณธ). */\nexport type ContextMenuAlign = 'left' | 'center' | 'right';\n\n/** ์ •๋ ฌ โ†’ ํ‚ท ํด๋ž˜์Šค. `center` ๋Š” ํ‚ท ๊ธฐ๋ณธ๊ฐ’์ด๋ผ ํด๋ž˜์Šค๊ฐ€ ์—†๋‹ค. */\nfunction alignClass(align: ContextMenuAlign | undefined): string | undefined {\n return align === 'left' || align === 'right' ? `drop-${align}` : undefined;\n}\n\n/** ๊ธ€์ž ํƒ์ƒ‰์— ์“ธ ๋ฌธ์ž์—ด์„ ๋ฝ‘๋Š”๋‹ค. */\nfunction itemText(item: ContextMenuAction): string {\n return (item.text ?? (typeof item.label === 'string' ? item.label : '')).trim().toLowerCase();\n}\n\n/* โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * 2. ์—ฌ๋‹ซ๊ธฐยท์ดˆ์  โ€” ๋‘ ๋ถ€ํ’ˆ์ด ํ•จ๊ป˜ ์“ด๋‹ค\n * โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */\n\n/** ์—ด ๋•Œ ์–ด๋А ํ•ญ๋ชฉ์— ์ดˆ์ ์„ ๋‘˜์ง€. โ†‘ ๋กœ ์—ด๋ฉด ๋งˆ์ง€๋ง‰ ํ•ญ๋ชฉ์ด๋‹ค(APG). */\ntype InitialFocus = 'first' | 'last';\n\n/** ๊ธ€์ž ํƒ์ƒ‰ ๋ฒ„ํผ๊ฐ€ ์‚ด์•„ ์žˆ๋Š” ์‹œ๊ฐ„(ms). APG ์˜ˆ์ œ๊ฐ€ ์“ฐ๋Š” ๊ฐ’์ด๋‹ค. */\nconst TYPEAHEAD_RESET_MS = 500;\n\n/** ์ปค์„œ ๋ฉ”๋‰ด๊ฐ€ ํ™”๋ฉด ์•„๋ž˜๋กœ ๋„˜์น  ๋•Œ ๋‚จ๊ธธ ์—ฌ์œ (px). KRDS ๋ˆˆ๊ธˆ์— ์—†์–ด ๊ทธ๋ฆผ์ž๊ฐ€ ์ž˜๋ฆฌ์ง€ ์•Š์„ ๊ฐ’์œผ๋กœ ์ •ํ–ˆ๋‹ค. */\nconst VIEWPORT_GUTTER_PX = 8;\n\nfunction useMenuController() {\n const [open, setOpen] = useState(false);\n const wrapRef = useRef<HTMLDivElement>(null);\n const menuRef = useRef<HTMLUListElement>(null);\n /** ๋‹ซ์„ ๋•Œ ์ดˆ์ ์„ ๋Œ๋ ค์ค„ ๊ณณ. ์—ด๊ธฐ ์ง์ „์— ์ฑ„์šด๋‹ค. */\n const returnFocusRef = useRef<HTMLElement | null>(null);\n /** ์—ด๋ฆฐ ์งํ›„ ์ดˆ์ ์„ ๋‘˜ ์ž๋ฆฌ. */\n const initialFocusRef = useRef<InitialFocus>('first');\n\n /** ์ง€๊ธˆ ๋ฉ”๋‰ด ์•ˆ์— ์žˆ๋Š” ํ•ญ๋ชฉ๋“ค. ๊ตฌ๋ถ„์„ ์€ ๊ฑธ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค. */\n const items = useCallback(\n () => Array.from(menuRef.current?.querySelectorAll<HTMLElement>('[role=\"menuitem\"]') ?? []),\n [],\n );\n\n /** ์—ฐ๋‹ค. `returnTo` ๋ฅผ ์ฃผ์ง€ ์•Š์œผ๋ฉด ์—ด๊ธฐ ์ง์ „ ์ดˆ์ ์ด ์žˆ๋˜ ๊ณณ์œผ๋กœ ๋Œ์•„๊ฐ„๋‹ค. */\n const openMenu = useCallback((initial: InitialFocus = 'first', returnTo?: HTMLElement | null) => {\n initialFocusRef.current = initial;\n returnFocusRef.current = returnTo ?? (document.activeElement as HTMLElement | null) ?? null;\n setOpen(true);\n }, []);\n\n const close = useCallback((refocus = false) => {\n setOpen(false);\n if (refocus) returnFocusRef.current?.focus();\n }, []);\n\n // ์—ด๋ฆฐ ์งํ›„ ์ฒซ(๋˜๋Š” ๋งˆ์ง€๋ง‰) ํ•ญ๋ชฉ์œผ๋กœ ์ดˆ์ ์„ ์˜ฎ๊ธด๋‹ค โ€” APG ์˜ Menu Button ๊ทœ์ •์ด๋‹ค.\n useEffect(() => {\n if (!open) return;\n const list = items();\n if (!list.length) return;\n (initialFocusRef.current === 'last' ? list[list.length - 1] : list[0]).focus();\n }, [open, items]);\n\n // ๋ฐ”๊นฅ์„ ๋ˆ„๋ฅด๋ฉด ๋‹ซ๋Š”๋‹ค. ์ดˆ์ ์€ ๋Œ๋ ค์ฃผ์ง€ ์•Š๋Š”๋‹ค(๋ˆ„๋ฅธ ๊ณณ์œผ๋กœ ๊ฐ€๋Š” ๊ฒŒ ์ž์—ฐ์Šค๋Ÿฝ๋‹ค).\n useEffect(() => {\n if (!open) return;\n const onPointerDown = (e: PointerEvent) => {\n if (!wrapRef.current?.contains(e.target as Node)) setOpen(false);\n };\n document.addEventListener('pointerdown', onPointerDown);\n return () => document.removeEventListener('pointerdown', onPointerDown);\n }, [open]);\n\n return { open, openMenu, close, wrapRef, menuRef, items };\n}\n\n/** ๋ฐ–์—์„œ ์˜จ ref ์™€ ๋ถ€ํ’ˆ ๋‚ด๋ถ€ ref ๋ฅผ ํ•จ๊ป˜ ์ฑ„์šด๋‹ค. ๊ฐ์‹ธ๊ฐœ ref ๋ฅผ ์‚ผํ‚ค์ง€ ์•Š์œผ๋ ค๊ณ  ๋‘”๋‹ค. */\nfunction mergeRef<T>(inner: { current: T | null }, outer: Ref<T> | undefined) {\n return (node: T | null) => {\n inner.current = node;\n if (typeof outer === 'function') outer(node);\n else if (outer) outer.current = node;\n };\n}\n\n/** ์—ด๋ฆผ์ด ๋ฐ”๋€” ๋•Œ๋งŒ ์•Œ๋ฆฐ๋‹ค. ์ธ๋ผ์ธ ์ฝœ๋ฐฑ์ด ๋งค ๋ Œ๋” ์ƒˆ ํ•จ์ˆ˜๋ผ ref ์— ๋‹ด์•„ ์˜์กด์„ฑ์—์„œ ๋บ€๋‹ค. */\nfunction useOpenChange(open: boolean, onOpenChange?: (open: boolean) => void) {\n const latest = useRef(onOpenChange);\n const mounted = useRef(false);\n useEffect(() => {\n latest.current = onOpenChange;\n });\n useEffect(() => {\n // ๋ถ™์ž๋งˆ์ž `false` ๋ฅผ ์•Œ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค โ€” ๋ฐ”๋€ ๊ฒƒ์ด ์—†๋‹ค.\n if (!mounted.current) {\n mounted.current = true;\n return;\n }\n latest.current?.(open);\n }, [open]);\n}\n\n/* โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * 3. ๋ชฉ๋ก ๊ทธ๋ฆฌ๊ธฐ\n * โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */\n\ntype MenuListProps = {\n id: string;\n items: ReadonlyArray<ContextMenuItem>;\n /** ๋ฉ”๋‰ด ์ด๋ฆ„. ์—ฌ๋Š” ๋‹จ์ถ”๊ฐ€ ์žˆ์œผ๋ฉด ๊ทธ id ๋ฅผ `labelledBy` ๋กœ ์ค€๋‹ค. */\n label?: string;\n labelledBy?: string;\n menuRef: Ref<HTMLUListElement>;\n onKeyDown: (e: ReactKeyboardEvent<HTMLUListElement>) => void;\n onActivate: (item: ContextMenuAction) => void;\n};\n\nfunction MenuList({ id, items, label, labelledBy, menuRef, onKeyDown, onActivate }: MenuListProps) {\n return (\n <ul\n ref={menuRef}\n id={id}\n // โ˜… role=\"menu\" ๋Š” ์ด <ul> ์— ์ค€๋‹ค. ๋ฐ”๊นฅ .drop-menu ์— ์ฃผ๋ฉด ul/li ๊ฐ€ ํ—ˆ์šฉ๋˜์ง€ ์•Š๋Š” ์ž์‹์ด ๋œ๋‹ค.\n role=\"menu\"\n aria-label={labelledBy ? undefined : label}\n aria-labelledby={labelledBy}\n className=\"drop-list\"\n onKeyDown={onKeyDown}\n >\n {items.map((item, index) => {\n if (item.separator) {\n // ํ‚ท์— ๊ตฌ๋ถ„์„  ํด๋ž˜์Šค๊ฐ€ ์—†๋‹ค โ€” ์œ ํ‹ธ๋ฆฌํ‹ฐ๋กœ ๊ทธ๋ฆฐ๋‹ค. ํ‚ท ๋ชฉ๋ก ๊ฐ„๊ฒฉ(--krds-gap-3=8px)์— ๋งž์ถฐ my-1(4px)์”ฉ.\n return (\n <li\n key={item.id ?? `separator-${index}`}\n role=\"separator\"\n className=\"my-1 h-px bg-divider\"\n />\n );\n }\n\n const { id: itemId, label: itemLabel, icon, href, target, disabled, danger, active } = item;\n const cls = cx(\n 'item-link',\n active && 'active',\n // ํ‚ท์— ์œ„ํ—˜ยท๋น„ํ™œ์„ฑ ํ•ญ๋ชฉ ๋ชจ์–‘์ด ์—†๋‹ค โ€” ์œ ํ‹ธ๋ฆฌํ‹ฐ๋กœ ๋ณด์ถฉํ•œ๋‹ค.\n danger && !disabled && 'text-danger',\n disabled && 'text-fg-disabled cursor-not-allowed hover:bg-transparent',\n );\n const iconNode = icon ? (\n <i\n // .svg-icon ์€ background-color ๋ฅผ mask ์ƒ‰์œผ๋กœ ์“ด๋‹ค โ€” ๊ธ€์ž์ƒ‰๊ณผ ๋งž์ถ”๋ ค๋ฉด bg-* ๋ฅผ ์–น๋Š”๋‹ค.\n className={cx(\n 'svg-icon',\n `ico-${icon}`,\n danger && !disabled && 'bg-danger',\n disabled && 'bg-fg-disabled',\n )}\n aria-hidden=\"true\"\n />\n ) : null;\n\n const shared = {\n role: 'menuitem' as const,\n // ๋ฉ”๋‰ด ์•ˆ์—์„œ๋Š” โ†‘โ†“ ๋กœ๋งŒ ์˜ค๊ฐ„๋‹ค(roving tabindex). ์ดˆ์ ์€ ๋ถ€ํ’ˆ์ด ์˜ฎ๊ธด๋‹ค.\n tabIndex: -1,\n className: cls,\n // ๋น„ํ™œ์„ฑ์€ disabled ์†์„ฑ์ด ์•„๋‹ˆ๋ผ aria-disabled ๋‹ค โ€” ์ดˆ์ ์€ ๋ฐ›์•„์•ผ ํ•œ๋‹ค(APG).\n 'aria-disabled': disabled || undefined,\n 'aria-current': active ? ('true' as const) : undefined,\n };\n\n return (\n <li key={itemId} role=\"none\">\n {href !== undefined ? (\n <a\n {...shared}\n // ๋น„ํ™œ์„ฑ ๋งํฌ๋Š” href ๋ฅผ ๋–ผ์–ด ์ด๋™์„ ๋ง‰๋Š”๋‹ค. tabIndex=-1 ์ด๋ผ ์ดˆ์ ์€ ๊ทธ๋Œ€๋กœ ๋ฐ›๋Š”๋‹ค.\n href={disabled ? undefined : href}\n target={target}\n // ์ƒˆ ์ฐฝ์œผ๋กœ ์—ด๋ฆฌ๋ฉด ๋ฏธ๋ฆฌ ์•Œ๋ฆฐ๋‹ค โ€” KRDS ์ ‘๊ทผ์„ฑ ์š”๊ตฌ.\n title={target === '_blank' ? '์ƒˆ ์ฐฝ ์—ด๋ฆผ' : undefined}\n onClick={(e) => {\n if (disabled) {\n e.preventDefault();\n return;\n }\n onActivate(item);\n }}\n >\n {iconNode}\n {itemLabel}\n </a>\n ) : (\n <button\n {...shared}\n type=\"button\"\n onClick={() => {\n if (disabled) return;\n onActivate(item);\n }}\n >\n {iconNode}\n {itemLabel}\n </button>\n )}\n </li>\n );\n })}\n </ul>\n );\n}\n\n/* โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * 4. ํ‚ค๋ณด๋“œ โ€” APG Menu Button\n * โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */\n\ntype Nav = {\n items: () => HTMLElement[];\n close: (refocus?: boolean) => void;\n};\n\n/**\n * ๋ฉ”๋‰ด ์•ˆ ํ‚ค ์ฒ˜๋ฆฌ(APG) โ€” โ†“โ†‘ ๋Š” ๋์—์„œ ๋Œ๊ณ , Home/End ๋Š” ์–‘ ๋, Esc ๋Š” ๋‹ซ๊ณ  ์ดˆ์  ๋ณต๊ท€,\n * Tab ์€ ๋‹ซ๊ณ  ํ˜๋ ค๋ณด๋‚ด๋ฉฐ, ๊ธ€์ž๋ฅผ ์น˜๋ฉด ๊ทธ ๊ธ€์ž๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋‹ค์Œ ํ•ญ๋ชฉ์œผ๋กœ ๊ฐ„๋‹ค.\n */\nfunction useMenuKeyDown({ items, close }: Nav, entries: ReadonlyArray<ContextMenuItem>) {\n const buffer = useRef('');\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // ๋”ธ๋ฆฐ ํƒ€์ด๋จธ๋Š” ๋ถ€ํ’ˆ์ด ์‚ฌ๋ผ์งˆ ๋•Œ ๋ฐ˜๋“œ์‹œ ๋ˆ๋‹ค.\n useEffect(\n () => () => {\n if (timer.current) clearTimeout(timer.current);\n },\n [],\n );\n\n const focusAt = (list: HTMLElement[], index: number) => {\n if (!list.length) return;\n list[(index + list.length) % list.length].focus();\n };\n\n return (e: ReactKeyboardEvent<HTMLUListElement>) => {\n const list = items();\n const at = list.indexOf(document.activeElement as HTMLElement);\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n focusAt(list, at + 1);\n return;\n case 'ArrowUp':\n e.preventDefault();\n focusAt(list, at - 1);\n return;\n case 'Home':\n e.preventDefault();\n focusAt(list, 0);\n return;\n case 'End':\n e.preventDefault();\n focusAt(list, list.length - 1);\n return;\n case 'Escape':\n // ๋ฐ”๊นฅ(๋ชจ๋‹ฌ ๋“ฑ)์ด ๊ฐ™์€ Esc ๋ฅผ ๋ฐ›์ง€ ์•Š๋„๋ก ์—ฌ๊ธฐ์„œ ๋ฉˆ์ถ˜๋‹ค.\n e.stopPropagation();\n e.preventDefault();\n close(true);\n return;\n case 'Tab':\n // APG: Tab ์€ ๋ฉ”๋‰ด๋ฅผ ๋‹ซ๊ณ  ์ดˆ์ ์„ ๋‹ค์Œ ์š”์†Œ๋กœ ๋„˜๊ธด๋‹ค. preventDefault ํ•˜์ง€ ์•Š๋Š”๋‹ค.\n close();\n return;\n default:\n break;\n }\n\n // ๊ธ€์ž ํƒ์ƒ‰. ์กฐํ•ฉํ‚ค๊ฐ€ ๊ฑธ๋ฆฐ ์ž…๋ ฅ์€ ๋‹จ์ถ•ํ‚ค์ด๋ฏ€๋กœ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.\n if (e.key.length !== 1 || e.altKey || e.ctrlKey || e.metaKey) return;\n const key = e.key.toLowerCase();\n if (key === ' ' && !buffer.current) return; // Space ๋Š” ์‹คํ–‰(๊ธฐ๋ณธ ๋™์ž‘)์ด๋‹ค\n\n e.preventDefault();\n buffer.current += key;\n if (timer.current) clearTimeout(timer.current);\n timer.current = setTimeout(() => {\n buffer.current = '';\n }, TYPEAHEAD_RESET_MS);\n\n // ๊ตฌ๋ถ„์„ ์„ ๋บ€ ์‹ค์ œ ํ•ญ๋ชฉ ์ˆœ์„œ๋กœ ๊ฒฌ์ค€๋‹ค โ€” DOM ์ˆœ์„œ(list)์™€ ์ง์ด ๋งž๋Š”๋‹ค.\n const actions = entries.filter((i): i is ContextMenuAction => !i.separator);\n const from = at < 0 ? 0 : at + (buffer.current.length > 1 ? 0 : 1);\n for (let n = 0; n < actions.length; n += 1) {\n const i = (from + n) % actions.length;\n if (itemText(actions[i]).startsWith(buffer.current)) {\n focusAt(list, i);\n return;\n }\n }\n };\n}\n\n/* โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * 5. ๋”๋ณด๊ธฐ ๋‹จ์ถ” โ€” ContextMenu\n * โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */\n\nexport type ContextMenuProps = Omit<ComponentPropsWithRef<'div'>, 'onSelect'> & {\n items: ReadonlyArray<ContextMenuItem>;\n /** ๋‹จ์ถ”์˜ ์ด๋ฆ„(`sr-only`). ํ‘œ ์•ˆ์—์„œ๋Š” ์–ด๋А ํ–‰์ธ์ง€๊นŒ์ง€ ๋‹ด๋Š”๋‹ค โ€” ใ€Œํ™๊ธธ๋™ ํ–‰ ๋”๋ณด๊ธฐใ€. */\n label: string;\n /** ๊ธ€์ž๊ฐ€ ์žˆ๋Š” ๋‹จ์ถ”๋กœ ๊ทธ๋ฆฐ๋‹ค. ์ฃผ๋ฉด ์•„์ด์ฝ˜ ๋Œ€์‹  ใ€Œ๊ธ€์ž + ico-toggleใ€์ด ๋œ๋‹ค. */\n triggerLabel?: ReactNode;\n /** ์•„์ด์ฝ˜๋งŒ์ธ ๋‹จ์ถ”์˜ ์•„์ด์ฝ˜. ๊ธฐ๋ณธ `more`(ํ‚ท `ico_more.svg`). */\n icon?: KrdsIcon;\n /** ๋‹จ์ถ” ํฌ๊ธฐ. ๊ธฐ๋ณธ small(40px) โ€” ํ‘œ ์•ˆ ํ–‰ ๋™์ž‘ ์ž๋ฆฌ์— ๋งž์ถ˜ ๊ฐ’์ด๋‹ค. */\n size?: KrdsSize;\n /** ๋ฉ”๋‰ด๊ฐ€ ํŽผ์ณ์ง€๋Š” ์ชฝ. ๊ธฐ๋ณธ `right`(์˜ค๋ฅธ์ชฝ ๋ ์—ด์— ๋†“์ด๋Š” ์ž๋ฆฌ๋ผ ์™ผ์ชฝ์œผ๋กœ ํŽธ๋‹ค). */\n align?: ContextMenuAlign;\n /** ํ•ญ๋ชฉ๋ณ„ `onSelect` ์™€ ๋ณ„๊ฐœ๋กœ, ๋ฌด์—‡์„ ๊ณจ๋ž๋“  ํ•œ ๋ฒˆ ๋ถ€๋ฅธ๋‹ค. */\n onSelect?: (id: string) => void;\n onOpenChange?: (open: boolean) => void;\n /** ์—ฌ๋Š” ๋‹จ์ถ”์— ๋ถ™์ผ ํด๋ž˜์Šค. */\n triggerClassName?: string;\n};\n\n/** ๋”๋ณด๊ธฐ ๋ฉ”๋‰ด โ€” ํ‘œ ํ–‰ ๋™์ž‘ยท์นด๋“œ ์šฐ์ƒ๋‹จ์ฒ˜๋Ÿผ ใ€Œ์ด ๋Œ€์ƒ์— ๋Œ€ํ•œ ๋™์ž‘ ๋ฌถ์Œใ€์„ ์ ‘๋Š”๋‹ค. */\nexport function ContextMenu({\n items,\n label,\n triggerLabel,\n icon = 'more',\n size = 'small',\n align = 'right',\n onSelect,\n onOpenChange,\n triggerClassName,\n className,\n ref,\n ...rest\n}: ContextMenuProps) {\n const ctl = useMenuController();\n const triggerRef = useRef<HTMLButtonElement>(null);\n const baseId = useId();\n const menuId = `${baseId}-menu`;\n const triggerId = `${baseId}-trigger`;\n\n const onKeyDownMenu = useMenuKeyDown({ items: ctl.items, close: ctl.close }, items);\n useOpenChange(ctl.open, onOpenChange);\n\n const toggle = () => {\n // ๋งˆ์šฐ์Šค๋กœ ๋ˆŒ๋ €์„ ๋•Œ Safari ๋Š” ๋‹จ์ถ”์— ์ดˆ์ ์„ ์ฃผ์ง€ ์•Š๋Š”๋‹ค โ€” ๋Œ๋ ค์ค„ ์ž๋ฆฌ๋ฅผ ์ง์ ‘ ์ง€์ •ํ•œ๋‹ค.\n if (ctl.open) ctl.close(true);\n else ctl.openMenu('first', triggerRef.current);\n };\n\n const onTriggerKeyDown = (e: ReactKeyboardEvent<HTMLButtonElement>) => {\n if (e.key !== 'ArrowDown' && e.key !== 'ArrowUp') return;\n e.preventDefault();\n // APG: โ†“ ๋กœ ์—ด๋ฉด ์ฒซ ํ•ญ๋ชฉ, โ†‘ ๋กœ ์—ด๋ฉด ๋งˆ์ง€๋ง‰ ํ•ญ๋ชฉ์— ์ดˆ์ .\n ctl.openMenu(e.key === 'ArrowDown' ? 'first' : 'last', triggerRef.current);\n };\n\n const triggerProps = {\n id: triggerId,\n ref: triggerRef,\n size,\n variant: 'text' as const,\n // drop-btn ์€ ํ‚ท CSS ์— ์—†๋Š” ์†์žก์ด ํด๋ž˜์Šค์ง€๋งŒ ์ƒ˜ํ”Œ์— ์žˆ์œผ๋ฏ€๋กœ ๋‚จ๊ธด๋‹ค.\n className: cx('drop-btn', triggerClassName),\n 'aria-haspopup': 'true' as const,\n 'aria-expanded': ctl.open,\n 'aria-controls': menuId,\n onClick: toggle,\n onKeyDown: onTriggerKeyDown,\n };\n\n return (\n <div\n {...rest}\n ref={mergeRef(ctl.wrapRef, ref)}\n className={cx('krds-drop-wrap', alignClass(align), className)}\n // ์ดˆ์ ์ด ์—ฌ๋Š” ๋‹จ์ถ”์— ์žˆ์„ ๋•Œ์˜ Esc. ๋ชฉ๋ก ์•ˆ์—์„œ๋Š” <ul> ์ด ๋จผ์ € ๋ฐ›์•„ ์—ฌ๊ธฐ๊นŒ์ง€ ์˜ค์ง€ ์•Š๋Š”๋‹ค.\n onKeyDown={(e) => {\n if (e.key === 'Escape' && ctl.open) {\n e.stopPropagation();\n ctl.close(true);\n }\n }}\n >\n {triggerLabel === undefined ? (\n <Button iconOnly icon={icon} label={label} {...triggerProps} />\n ) : (\n <Button icon=\"toggle\" {...triggerProps}>\n {triggerLabel}\n </Button>\n )}\n\n {/*\n ํ‚ท์€ `.drop-menu{display:none}` ๋งŒ ๋‘๊ณ  ์—ฌ๋Š” ํด๋ž˜์Šค๋ฅผ ์ฃผ์ง€ ์•Š๋Š”๋‹ค โ€” ์ธ๋ผ์ธ์œผ๋กœ ์—ฐ๋‹ค.\n ์ ‘ํžŒ ๋™์•ˆ display:none ์ด๋ผ ์Šคํฌ๋ฆฐ๋ฆฌ๋”๋„ ์ฝ์ง€ ์•Š๋Š”๋‹ค(๋ณ„๋„ hidden ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š” ์—†๋‹ค).\n */}\n <div className=\"drop-menu\" style={ctl.open ? { display: 'block' } : undefined}>\n <div className=\"drop-in\">\n <MenuList\n id={menuId}\n items={items}\n labelledBy={triggerId}\n menuRef={ctl.menuRef}\n onKeyDown={onKeyDownMenu}\n onActivate={(item) => {\n ctl.close(true);\n item.onSelect?.(item.id);\n onSelect?.(item.id);\n }}\n />\n </div>\n </div>\n </div>\n );\n}\n\n/* โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * 6. ์šฐํด๋ฆญ ์˜์—ญ โ€” ContextMenuArea\n * โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ */\n\nexport type ContextMenuAreaProps = Omit<ComponentPropsWithRef<'div'>, 'onSelect'> & {\n items: ReadonlyArray<ContextMenuItem>;\n /** ๋ฉ”๋‰ด ์ด๋ฆ„. ์—ฌ๋Š” ๋‹จ์ถ”๊ฐ€ ์—†์œผ๋‹ˆ ์—ฌ๊ธฐ์„œ ์ค€๋‹ค โ€” ใ€Œ๋ชฉ๋ก ํ•ญ๋ชฉ ๋™์ž‘ใ€์ฒ˜๋Ÿผ. */\n label: string;\n onSelect?: (id: string) => void;\n onOpenChange?: (open: boolean) => void;\n /** ์šฐํด๋ฆญ์„ ๊ฐ€๋กœ์ฑ„์ง€ ์•Š๋Š”๋‹ค(๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ ๋ฉ”๋‰ด๊ฐ€ ๋œฌ๋‹ค). */\n disabled?: boolean;\n};\n\n/**\n * ์šฐํด๋ฆญ ๋ฉ”๋‰ด โ€” ์˜์—ญ ์•ˆ์—์„œ ์˜ค๋ฅธ์ชฝ ๋‹จ์ถ”(๋˜๋Š” Shift+F10 ยท ContextMenu ํ‚ค)๋กœ ์—ฐ๋‹ค.\n *\n * **๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ ๋ฉ”๋‰ด๋ฅผ ๊ฐ€๋ฆฐ๋‹ค.** ใ€Œ์ƒˆ ํƒญ์—์„œ ์—ด๊ธฐใ€๋ฅผ ๋นผ์•—์œผ๋ฏ€๋กœ ๊ผญ ํ•„์š”ํ•œ ๊ณณ์—๋งŒ ์“ฐ๊ณ ,\n * ๊ฐ™์€ ๋™์ž‘์„ ์—ฌ๋Š” ๋ˆˆ์— ๋ณด์ด๋Š” ๋‹จ์ถ”(`ContextMenu`)๋ฅผ ํ•จ๊ป˜ ๋‘”๋‹ค.\n *\n * @example\n * <ContextMenuArea label=\"๋ชฉ๋ก ํ•ญ๋ชฉ ๋™์ž‘\" items={rowActions}>\n * <ul>โ€ฆ</ul>\n * </ContextMenuArea>\n */\nexport function ContextMenuArea({\n items,\n label,\n onSelect,\n onOpenChange,\n disabled,\n className,\n children,\n ref,\n ...rest\n}: ContextMenuAreaProps) {\n const ctl = useMenuController();\n const menuId = useId();\n /** ๊ฐ์‹ธ๊ฐœ ์™ผ์ชฝ ์œ„๋ฅผ ์›์ ์œผ๋กœ ํ•œ ์•ต์ปค ์ขŒํ‘œ(px). */\n const [pos, setPos] = useState({ x: 0, y: 0 });\n const [side, setSide] = useState<'left' | 'right'>('left');\n const anchorRef = useRef<HTMLDivElement>(null);\n\n const onKeyDownMenu = useMenuKeyDown({ items: ctl.items, close: ctl.close }, items);\n useOpenChange(ctl.open, onOpenChange);\n\n // ํ™”๋ฉด ์•„๋ž˜๋กœ ๋„˜์น˜๋ฉด ๊ทธ๋งŒํผ ๋Œ์–ด์˜ฌ๋ฆฐ๋‹ค. ์ƒํƒœ๊ฐ€ ์•„๋‹ˆ๋ผ ์•ต์ปค์˜ style.top ์„ ์ง์ ‘ ์“ด๋‹ค โ€”\n // ์žฌ๊ณ  ๋‚˜์„œ ์ƒํƒœ๋ฅผ ๋ฐ”๊พธ๋ฉด ํ•œ ํ”„๋ ˆ์ž„์ด ์ž˜๋ชป๋œ ์ž๋ฆฌ์— ๊ทธ๋ ค์ง„๋‹ค. ์œ„๋กœ ๋’ค์ง‘์ง€๋Š” ์•Š๋Š”๋‹ค(๊ผญ์ง€๊ฐ€ ๊ฑฐ๊พธ๋กœ ์„ ๋‹ค).\n useEffect(() => {\n if (!ctl.open) return;\n const anchor = anchorRef.current;\n const box = anchor?.firstElementChild?.getBoundingClientRect();\n if (!anchor || !box) return;\n const over = box.bottom - (window.innerHeight - VIEWPORT_GUTTER_PX);\n if (over > 0) anchor.style.top = `${pos.y - over}px`;\n }, [ctl.open, pos.x, pos.y]);\n\n /** ๊ฐ์‹ธ๊ฐœ ๊ธฐ์ค€ ์ขŒํ‘œ๋กœ ์˜ฎ๊ฒจ ๋‹ด๊ณ  ์—ฐ๋‹ค. */\n const openAt = (clientX: number, clientY: number) => {\n const rect = ctl.wrapRef.current?.getBoundingClientRect();\n if (!rect) return;\n setPos({ x: clientX - rect.left, y: clientY - rect.top });\n // ์˜ค๋ฅธ์ชฝ ์ ˆ๋ฐ˜์—์„œ ์—ด๋ฉด ๋ฉ”๋‰ด๋ฅผ ์™ผ์ชฝ์œผ๋กœ ํŽธ๋‹ค.\n setSide(clientX > window.innerWidth / 2 ? 'right' : 'left');\n ctl.openMenu('first');\n };\n\n const onContextMenu = (e: ReactMouseEvent<HTMLDivElement>) => {\n if (disabled) return;\n e.preventDefault();\n openAt(e.clientX, e.clientY);\n };\n\n const onKeyDown = (e: ReactKeyboardEvent<HTMLDivElement>) => {\n // ๋ฉ”๋‰ด๊ฐ€ ์—ด๋ ค ์žˆ์œผ๋ฉด ๋ชฉ๋ก ์ชฝ ์ฒ˜๋ฆฌ์— ๋งก๊ธด๋‹ค.\n if (ctl.open) {\n if (e.key === 'Escape') {\n e.stopPropagation();\n ctl.close(true);\n }\n return;\n }\n if (disabled) return;\n // ํ‚ค๋ณด๋“œ๋กœ ์ƒํ™ฉ ๋ฉ”๋‰ด๋ฅผ ์—ฌ๋Š” ํ‘œ์ค€ ๋‘ ๊ฐ€์ง€.\n if (e.key !== 'ContextMenu' && !(e.key === 'F10' && e.shiftKey)) return;\n e.preventDefault();\n // ์ดˆ์ ์ด ์žˆ๋Š” ์š”์†Œ์˜ ์™ผ์ชฝ ์•„๋ž˜์— ๋„์šด๋‹ค โ€” ์ปค์„œ๊ฐ€ ์—†์œผ๋‹ˆ ๊ทธ ์ž๋ฆฌ๊ฐ€ ๊ธฐ์ค€์ด๋‹ค.\n const box = (e.target as HTMLElement).getBoundingClientRect?.();\n if (box) openAt(box.left, box.bottom);\n };\n\n return (\n <div\n {...rest}\n ref={mergeRef(ctl.wrapRef, ref)}\n // ํ‚ท ๊ฐ์‹ธ๊ฐœ๋Š” inline-flex ๋‹ค โ€” ์˜์—ญ์„ ๊ฐ์‹ธ๋Š” ์“ฐ์ž„์ด๋ผ block ์œผ๋กœ ๋˜๋Œ๋ฆฐ๋‹ค.\n className={cx('krds-drop-wrap block', ctl.open && `drop-${side}`, className)}\n onContextMenu={onContextMenu}\n onKeyDown={onKeyDown}\n >\n {children}\n\n {ctl.open && (\n // 0 ํฌ๊ธฐ ์•ต์ปค. ๋ฉ”๋‰ด์˜ ๊ธฐ์ค€์ ์ผ ๋ฟ์ด๋ผ ํ™”๋ฉด์—๋„ ์Šคํฌ๋ฆฐ๋ฆฌ๋”์—๋„ ์žกํžˆ์ง€ ์•Š๋Š”๋‹ค.\n <div\n ref={anchorRef}\n className=\"absolute\"\n style={{ left: pos.x, top: pos.y, width: 0, height: 0 }}\n >\n <div className=\"drop-menu\" style={{ display: 'block' }}>\n <div className=\"drop-in\">\n <MenuList\n id={menuId}\n items={items}\n label={label}\n menuRef={ctl.menuRef}\n onKeyDown={onKeyDownMenu}\n onActivate={(item) => {\n ctl.close(true);\n item.onSelect?.(item.id);\n onSelect?.(item.id);\n }}\n />\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,39 @@
1
+ import type { ComponentPropsWithRef, ReactNode } from 'react';
2
+ /**
3
+ * ๊ธด๊ธ‰ ๊ณต์ง€ โ€” KRDS `krds-critical-alerts`.
4
+ *
5
+ * **์žฌ๋‚œยท์žฌํ•ด, ์˜ˆ๊ณ  ์—†๋Š” ์žฅ์• , ์ ๊ฒ€ ์‚ฌ์ „ ์˜ˆ๊ณ ์—๋งŒ ์“ด๋‹ค.** ๊ทœ๊ฒฉ์ด ํ”ผ๋“œ๋ฐฑ ๋ฉ”์‹œ์ง€ยท์ผ๋ฐ˜ ๊ณต์ง€ยทํ™๋ณด์—
6
+ * ์“ฐ๋Š” ๊ฒƒ์„ ๊ธˆ์ง€ํ•œ๋‹ค โ€” ๋ณธ๋ฌธ ์•ˆ๋‚ด๋Š” `Alert`, ํผ ์˜ค๋ฅ˜๋Š” `Field` ์˜ `error` ๋‹ค.
7
+ * ํ•œ ํ™”๋ฉด์— ํ•˜๋‚˜, ๋งํฌ๋„ ํ•˜๋‚˜, ๋ณธ๋ฌธ์€ ํ•œ ์ค„์ด๋‹ค(ํ‚ท์ด ๋‘ ์ค„์—์„œ ์ž˜๋ผ๋‚ธ๋‹ค).
8
+ * ๋‹ซ๊ธฐ ๋‹จ์ถ”๋Š” ๊ทœ๊ฒฉ์ด ๊ธˆ์ง€ํ•ด prop ์ž์ฒด๊ฐ€ ์—†๋‹ค. ์ž๋ฆฌ๋Š” ์ฃผ๋ฉ”๋‰ด์™€ ํžˆ์–ด๋กœ ์‚ฌ์ด, ๋ณธ๋ฌธ์˜ ์ฒซ ์š”์†Œ๋‹ค.
9
+ *
10
+ * @example
11
+ * <CriticalAlert level="danger" href="/notice/1024" linkTitle="์ง‘์ค‘ํ˜ธ์šฐ ๋Œ€์‘ ์•ˆ๋‚ด">
12
+ * ์ง‘์ค‘ํ˜ธ์šฐ๋กœ ์ผ๋ถ€ ๋ฏผ์› ์ฒ˜๋ฆฌ๊ฐ€ ์ง€์—ฐ๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
13
+ * </CriticalAlert>
14
+ *
15
+ * ์ž์„ธํžˆ: docs/krds/09-๋ถ€ํ’ˆ-๋…ธํŠธ.md#๊ธด๊ธ‰๊ณต์ง€
16
+ */
17
+ /**
18
+ * ๊ธด๊ธ‰๋„. `danger` ์žฌ๋‚œยท๊ธ‰์ž‘์Šค๋Ÿฌ์šด ์žฅ์• (ใ€Œ๊ธด๊ธ‰ใ€) ยท `ok` ์„œ๋น„์Šค ์ค‘๋‹จ ์‚ฌ์ „ ์˜ˆ๊ณ (ใ€Œ์•ˆ์ „ใ€) ยท
19
+ * `info` ๋Œ€๋ถ€๋ถ„์˜ ์‚ฌ์šฉ์ž๊ฐ€ ์•Œ์•„์•ผ ํ•˜๋Š” ์ƒํ™ฉ(ใ€Œ์•ˆ๋‚ดใ€).
20
+ */
21
+ export type CriticalAlertLevel = 'danger' | 'ok' | 'info';
22
+ export type CriticalAlertProps = Omit<ComponentPropsWithRef<'div'>, 'children'> & {
23
+ /** ๊ธด๊ธ‰๋„. ๊ธฐ๋ณธ๊ฐ’ `danger` โ€” ์ด ๋ถ€ํ’ˆ์„ ์“ธ ์ƒํ™ฉ ์ž์ฒด๊ฐ€ ๊ธด๊ธ‰ ์ƒํ™ฉ์ด๋‹ค. */
24
+ level?: CriticalAlertLevel;
25
+ /** ๋ฐฐ์ง€ ๊ธ€์ž. ์ƒ๋žตํ•˜๋ฉด ๊ธด๊ธ‰๋„๋ณ„ ๊ธฐ๋ณธ๊ฐ’(๊ธด๊ธ‰/์•ˆ์ „/์•ˆ๋‚ด). ๊ฐ์ถ”์ง€ ๋งˆ๋ผ โ€” ์•„์ด์ฝ˜๋งŒ์œผ๋กœ๋Š” ์•ˆ ์ฝํžŒ๋‹ค. */
26
+ label?: string;
27
+ /** ๊ณต์ง€ ๋‚ด์šฉ. ํ•œ ์ค„๋กœ ์“ด๋‹ค โ€” ํ‚ท์ด ๋‘ ์ค„์—์„œ ์ž˜๋ผ๋‚ธ๋‹ค. */
28
+ children: ReactNode;
29
+ /** ์ƒ์„ธ ํ™”๋ฉด ๋งํฌ. ๊ทœ๊ฒฉ์ƒ ํ•˜๋‚˜๋งŒ ๋‘˜ ์ˆ˜ ์žˆ๋‹ค. */
30
+ href?: string;
31
+ /** ๋งํฌ ๊ธ€์ž. ๊ธฐ๋ณธ ใ€Œ์ž์„ธํžˆ ๋ณด๊ธฐใ€. ์ž‘์€ ํ™”๋ฉด(<768px)์—์„œ๋Š” ๊ฐ์ถฐ์ง€๊ณ  ํ™”์‚ดํ‘œ๋งŒ ๋‚จ๋Š”๋‹ค. */
32
+ linkLabel?: ReactNode;
33
+ /** ๋งํฌ์˜ ๋ชฉ์ ์ง€ ์„ค๋ช…(`title`). ใ€Œ์ž์„ธํžˆ ๋ณด๊ธฐใ€๋งŒ์œผ๋กœ๋Š” ๋ชฉ์ ์ง€๋ฅผ ์•Œ ์ˆ˜ ์—†์œผ๋‹ˆ ๋ฌธ์„œ ์ œ๋ชฉ์„ ๋„ฃ๋Š”๋‹ค. */
34
+ linkTitle?: string;
35
+ /** ๋งํฌ target. `_blank` ๋ฉด `title` ์— ใ€Œ์ƒˆ ์ฐฝ ์—ด๋ฆผใ€์„ ๋ง๋ถ™์ธ๋‹ค. */
36
+ linkTarget?: ComponentPropsWithRef<'a'>['target'];
37
+ };
38
+ export declare function CriticalAlert({ level, label, children, href, linkLabel, linkTitle, linkTarget, className, role, 'aria-label': ariaLabel, ...rest }: CriticalAlertProps): import("react").JSX.Element;
39
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/critical-alert/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAG9D;;;;;;;;;;;;;;GAcG;AAEH;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,IAAI,GAAG,MAAM,CAAC;AAS1D,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,GAAG;IAChF,kDAAkD;IAClD,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,QAAQ,EAAE,SAAS,CAAC;IACpB,gCAAgC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,UAAU,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;CACnD,CAAC;AAEF,wBAAgB,aAAa,CAAC,EAC5B,KAAgB,EAChB,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,SAAoB,EACpB,SAAS,EACT,UAAU,EACV,SAAS,EAIT,IAAe,EACf,YAAY,EAAE,SAAmB,EACjC,GAAG,IAAI,EACR,EAAE,kBAAkB,+BAqCpB"}
@@ -0,0 +1,50 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { cx } from "../lib/krds.js";
3
+ const DEFAULT_LABEL = {
4
+ danger: "\uAE34\uAE09",
5
+ ok: "\uC548\uC804",
6
+ info: "\uC548\uB0B4"
7
+ };
8
+ function CriticalAlert({
9
+ level = "danger",
10
+ label,
11
+ children,
12
+ href,
13
+ linkLabel = "\uC790\uC138\uD788 \uBCF4\uAE30",
14
+ linkTitle,
15
+ linkTarget,
16
+ className,
17
+ // ์ ‘๊ทผ์„ฑ ๊ฐ€์ด๋“œ: ใ€Œ๊ธด๊ธ‰ ๊ณต์ง€ ์„น์…˜์— role="banner" ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐฐ๋„ˆ ์—ญํ• ์„ ๋ถ€์—ฌใ€.
18
+ // ํ‚ท ์ƒ˜ํ”Œ์—๋Š” ์—†๊ณ  ๊ฐ€์ด๋“œ๊ฐ€ ์š”๊ตฌํ•œ ๊ฒƒ์ด๋‹ค. ํŽ˜์ด์ง€์— <header> ๊ฐ€ ์ด๋ฏธ ์žˆ์œผ๋ฉด banner
19
+ // ๋žœ๋“œ๋งˆํฌ๊ฐ€ ๋‘˜์ด ๋˜๋ฏ€๋กœ ์ด๋ฆ„์„ ๋ถ™์—ฌ ๊ตฌ๋ถ„ํ•œ๋‹ค(aria-label ์€ ์šฐ๋ฆฌ๊ฐ€ ๋ง๋Œ„ ๊ฒƒ).
20
+ role = "banner",
21
+ "aria-label": ariaLabel = "\uAE34\uAE09 \uACF5\uC9C0",
22
+ ...rest
23
+ }) {
24
+ const newWindow = linkTarget === "_blank";
25
+ const title = linkTitle ? newWindow ? `${linkTitle} (\uC0C8 \uCC3D \uC5F4\uB9BC)` : linkTitle : newWindow ? "\uC0C8 \uCC3D \uC5F4\uB9BC" : void 0;
26
+ return (
27
+ // `.main-urgent-wrap` ์€ ํ‚ท CSS ์— ์—†๋‹ค(์œ„ ์ฃผ์„ ์ฐธ๊ณ ) โ€” ์ „ํญ๋งŒ Tailwind ๋กœ ์ค€๋‹ค.
28
+ /* @__PURE__ */ jsx("div", { className: cx("w-full", className), role, "aria-label": ariaLabel, ...rest, children: /* @__PURE__ */ jsx("ul", { className: "krds-critical-alerts", children: /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("div", { className: "critical-ban", children: [
29
+ /* @__PURE__ */ jsx("span", { className: cx("critical-badge", level), children: label ?? DEFAULT_LABEL[level] }),
30
+ /* @__PURE__ */ jsx("p", { className: "critical-txt", children }),
31
+ href && /* @__PURE__ */ jsxs(
32
+ "a",
33
+ {
34
+ href,
35
+ className: "krds-btn medium link basic",
36
+ target: linkTarget,
37
+ title,
38
+ children: [
39
+ /* @__PURE__ */ jsx("span", { className: "hidden md:inline", children: linkLabel }),
40
+ /* @__PURE__ */ jsx("i", { className: "svg-icon ico-angle right", "aria-hidden": "true" })
41
+ ]
42
+ }
43
+ )
44
+ ] }) }) }) })
45
+ );
46
+ }
47
+ export {
48
+ CriticalAlert
49
+ };
50
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/critical-alert/index.tsx"],"sourcesContent":["import type { ComponentPropsWithRef, ReactNode } from 'react';\nimport { cx } from '../lib/krds';\n\n/**\n * ๊ธด๊ธ‰ ๊ณต์ง€ โ€” KRDS `krds-critical-alerts`.\n *\n * **์žฌ๋‚œยท์žฌํ•ด, ์˜ˆ๊ณ  ์—†๋Š” ์žฅ์• , ์ ๊ฒ€ ์‚ฌ์ „ ์˜ˆ๊ณ ์—๋งŒ ์“ด๋‹ค.** ๊ทœ๊ฒฉ์ด ํ”ผ๋“œ๋ฐฑ ๋ฉ”์‹œ์ง€ยท์ผ๋ฐ˜ ๊ณต์ง€ยทํ™๋ณด์—\n * ์“ฐ๋Š” ๊ฒƒ์„ ๊ธˆ์ง€ํ•œ๋‹ค โ€” ๋ณธ๋ฌธ ์•ˆ๋‚ด๋Š” `Alert`, ํผ ์˜ค๋ฅ˜๋Š” `Field` ์˜ `error` ๋‹ค.\n * ํ•œ ํ™”๋ฉด์— ํ•˜๋‚˜, ๋งํฌ๋„ ํ•˜๋‚˜, ๋ณธ๋ฌธ์€ ํ•œ ์ค„์ด๋‹ค(ํ‚ท์ด ๋‘ ์ค„์—์„œ ์ž˜๋ผ๋‚ธ๋‹ค).\n * ๋‹ซ๊ธฐ ๋‹จ์ถ”๋Š” ๊ทœ๊ฒฉ์ด ๊ธˆ์ง€ํ•ด prop ์ž์ฒด๊ฐ€ ์—†๋‹ค. ์ž๋ฆฌ๋Š” ์ฃผ๋ฉ”๋‰ด์™€ ํžˆ์–ด๋กœ ์‚ฌ์ด, ๋ณธ๋ฌธ์˜ ์ฒซ ์š”์†Œ๋‹ค.\n *\n * @example\n * <CriticalAlert level=\"danger\" href=\"/notice/1024\" linkTitle=\"์ง‘์ค‘ํ˜ธ์šฐ ๋Œ€์‘ ์•ˆ๋‚ด\">\n * ์ง‘์ค‘ํ˜ธ์šฐ๋กœ ์ผ๋ถ€ ๋ฏผ์› ์ฒ˜๋ฆฌ๊ฐ€ ์ง€์—ฐ๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.\n * </CriticalAlert>\n *\n * ์ž์„ธํžˆ: docs/krds/09-๋ถ€ํ’ˆ-๋…ธํŠธ.md#๊ธด๊ธ‰๊ณต์ง€\n */\n\n/**\n * ๊ธด๊ธ‰๋„. `danger` ์žฌ๋‚œยท๊ธ‰์ž‘์Šค๋Ÿฌ์šด ์žฅ์• (ใ€Œ๊ธด๊ธ‰ใ€) ยท `ok` ์„œ๋น„์Šค ์ค‘๋‹จ ์‚ฌ์ „ ์˜ˆ๊ณ (ใ€Œ์•ˆ์ „ใ€) ยท\n * `info` ๋Œ€๋ถ€๋ถ„์˜ ์‚ฌ์šฉ์ž๊ฐ€ ์•Œ์•„์•ผ ํ•˜๋Š” ์ƒํ™ฉ(ใ€Œ์•ˆ๋‚ดใ€).\n */\nexport type CriticalAlertLevel = 'danger' | 'ok' | 'info';\n\n/** ๊ธด๊ธ‰๋„๋ณ„ ๊ธฐ๋ณธ ๋ ˆ์ด๋ธ”. ํ‚ท ์ƒ˜ํ”Œ์— ์ ํžŒ ๊ธ€์ž ๊ทธ๋Œ€๋กœ๋‹ค. */\nconst DEFAULT_LABEL: Record<CriticalAlertLevel, string> = {\n danger: '๊ธด๊ธ‰',\n ok: '์•ˆ์ „',\n info: '์•ˆ๋‚ด',\n};\n\nexport type CriticalAlertProps = Omit<ComponentPropsWithRef<'div'>, 'children'> & {\n /** ๊ธด๊ธ‰๋„. ๊ธฐ๋ณธ๊ฐ’ `danger` โ€” ์ด ๋ถ€ํ’ˆ์„ ์“ธ ์ƒํ™ฉ ์ž์ฒด๊ฐ€ ๊ธด๊ธ‰ ์ƒํ™ฉ์ด๋‹ค. */\n level?: CriticalAlertLevel;\n /** ๋ฐฐ์ง€ ๊ธ€์ž. ์ƒ๋žตํ•˜๋ฉด ๊ธด๊ธ‰๋„๋ณ„ ๊ธฐ๋ณธ๊ฐ’(๊ธด๊ธ‰/์•ˆ์ „/์•ˆ๋‚ด). ๊ฐ์ถ”์ง€ ๋งˆ๋ผ โ€” ์•„์ด์ฝ˜๋งŒ์œผ๋กœ๋Š” ์•ˆ ์ฝํžŒ๋‹ค. */\n label?: string;\n /** ๊ณต์ง€ ๋‚ด์šฉ. ํ•œ ์ค„๋กœ ์“ด๋‹ค โ€” ํ‚ท์ด ๋‘ ์ค„์—์„œ ์ž˜๋ผ๋‚ธ๋‹ค. */\n children: ReactNode;\n /** ์ƒ์„ธ ํ™”๋ฉด ๋งํฌ. ๊ทœ๊ฒฉ์ƒ ํ•˜๋‚˜๋งŒ ๋‘˜ ์ˆ˜ ์žˆ๋‹ค. */\n href?: string;\n /** ๋งํฌ ๊ธ€์ž. ๊ธฐ๋ณธ ใ€Œ์ž์„ธํžˆ ๋ณด๊ธฐใ€. ์ž‘์€ ํ™”๋ฉด(<768px)์—์„œ๋Š” ๊ฐ์ถฐ์ง€๊ณ  ํ™”์‚ดํ‘œ๋งŒ ๋‚จ๋Š”๋‹ค. */\n linkLabel?: ReactNode;\n /** ๋งํฌ์˜ ๋ชฉ์ ์ง€ ์„ค๋ช…(`title`). ใ€Œ์ž์„ธํžˆ ๋ณด๊ธฐใ€๋งŒ์œผ๋กœ๋Š” ๋ชฉ์ ์ง€๋ฅผ ์•Œ ์ˆ˜ ์—†์œผ๋‹ˆ ๋ฌธ์„œ ์ œ๋ชฉ์„ ๋„ฃ๋Š”๋‹ค. */\n linkTitle?: string;\n /** ๋งํฌ target. `_blank` ๋ฉด `title` ์— ใ€Œ์ƒˆ ์ฐฝ ์—ด๋ฆผใ€์„ ๋ง๋ถ™์ธ๋‹ค. */\n linkTarget?: ComponentPropsWithRef<'a'>['target'];\n};\n\nexport function CriticalAlert({\n level = 'danger',\n label,\n children,\n href,\n linkLabel = '์ž์„ธํžˆ ๋ณด๊ธฐ',\n linkTitle,\n linkTarget,\n className,\n // ์ ‘๊ทผ์„ฑ ๊ฐ€์ด๋“œ: ใ€Œ๊ธด๊ธ‰ ๊ณต์ง€ ์„น์…˜์— role=\"banner\" ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐฐ๋„ˆ ์—ญํ• ์„ ๋ถ€์—ฌใ€.\n // ํ‚ท ์ƒ˜ํ”Œ์—๋Š” ์—†๊ณ  ๊ฐ€์ด๋“œ๊ฐ€ ์š”๊ตฌํ•œ ๊ฒƒ์ด๋‹ค. ํŽ˜์ด์ง€์— <header> ๊ฐ€ ์ด๋ฏธ ์žˆ์œผ๋ฉด banner\n // ๋žœ๋“œ๋งˆํฌ๊ฐ€ ๋‘˜์ด ๋˜๋ฏ€๋กœ ์ด๋ฆ„์„ ๋ถ™์—ฌ ๊ตฌ๋ถ„ํ•œ๋‹ค(aria-label ์€ ์šฐ๋ฆฌ๊ฐ€ ๋ง๋Œ„ ๊ฒƒ).\n role = 'banner',\n 'aria-label': ariaLabel = '๊ธด๊ธ‰ ๊ณต์ง€',\n ...rest\n}: CriticalAlertProps) {\n const newWindow = linkTarget === '_blank';\n const title = linkTitle\n ? newWindow\n ? `${linkTitle} (์ƒˆ ์ฐฝ ์—ด๋ฆผ)`\n : linkTitle\n : newWindow\n ? '์ƒˆ ์ฐฝ ์—ด๋ฆผ'\n : undefined;\n\n return (\n // `.main-urgent-wrap` ์€ ํ‚ท CSS ์— ์—†๋‹ค(์œ„ ์ฃผ์„ ์ฐธ๊ณ ) โ€” ์ „ํญ๋งŒ Tailwind ๋กœ ์ค€๋‹ค.\n <div className={cx('w-full', className)} role={role} aria-label={ariaLabel} {...rest}>\n <ul className=\"krds-critical-alerts\">\n {/* ๊ทœ๊ฒฉ์ƒ ํ•ญ๋ชฉ์€ ํ•˜๋‚˜๋ฟ์ด๋‹ค. ๋Š˜๋ฆฌ์ง€ ๋งˆ๋ผ. */}\n <li>\n <div className=\"critical-ban\">\n {/* ์•„์ด์ฝ˜์€ ์ด ๋ฐฐ์ง€์˜ ::before ๋‹ค. ์•ˆ์— <i> ๋‚˜ sr-only ๋ฅผ ๋„ฃ์ง€ ๋งˆ๋ผ. */}\n <span className={cx('critical-badge', level)}>{label ?? DEFAULT_LABEL[level]}</span>\n <p className=\"critical-txt\">{children}</p>\n {href && (\n <a\n href={href}\n className=\"krds-btn medium link basic\"\n target={linkTarget}\n title={title}\n >\n {/* ํ‚ท์˜ `m-hide`(๋ชจ๋ฐ”์ผ ์ˆจ๊น€)๊ฐ€ CSS ์— ์—†์–ด Tailwind ๋กœ ๋Œ€์‹ ํ•œ๋‹ค. */}\n <span className=\"hidden md:inline\">{linkLabel}</span>\n <i className=\"svg-icon ico-angle right\" aria-hidden=\"true\" />\n </a>\n )}\n </div>\n </li>\n </ul>\n </div>\n );\n}\n"],"mappings":"AAkFY,cAGE,YAHF;AAjFZ,SAAS,UAAU;AAyBnB,MAAM,gBAAoD;AAAA,EACxD,QAAQ;AAAA,EACR,IAAI;AAAA,EACJ,MAAM;AACR;AAmBO,SAAS,cAAc;AAAA,EAC5B,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA,EAIA,OAAO;AAAA,EACP,cAAc,YAAY;AAAA,EAC1B,GAAG;AACL,GAAuB;AACrB,QAAM,YAAY,eAAe;AACjC,QAAM,QAAQ,YACV,YACE,GAAG,SAAS,kCACZ,YACF,YACE,+BACA;AAEN;AAAA;AAAA,IAEE,oBAAC,SAAI,WAAW,GAAG,UAAU,SAAS,GAAG,MAAY,cAAY,WAAY,GAAG,MAC9E,8BAAC,QAAG,WAAU,wBAEZ,8BAAC,QACC,+BAAC,SAAI,WAAU,gBAEb;AAAA,0BAAC,UAAK,WAAW,GAAG,kBAAkB,KAAK,GAAI,mBAAS,cAAc,KAAK,GAAE;AAAA,MAC7E,oBAAC,OAAE,WAAU,gBAAgB,UAAS;AAAA,MACrC,QACC;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,WAAU;AAAA,UACV,QAAQ;AAAA,UACR;AAAA,UAGA;AAAA,gCAAC,UAAK,WAAU,oBAAoB,qBAAU;AAAA,YAC9C,oBAAC,OAAE,WAAU,4BAA2B,eAAY,QAAO;AAAA;AAAA;AAAA,MAC7D;AAAA,OAEJ,GACF,GACF,GACF;AAAA;AAEJ;","names":[]}