@bridger-kr/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 (273) hide show
  1. package/README.md +62 -0
  2. package/dist/components/core/Badge.cjs +36 -0
  3. package/dist/components/core/Badge.cjs.map +1 -0
  4. package/dist/components/core/Badge.d.cts +18 -0
  5. package/dist/components/core/Badge.d.ts +18 -0
  6. package/dist/components/core/Badge.mjs +34 -0
  7. package/dist/components/core/Badge.mjs.map +1 -0
  8. package/dist/components/core/Button.cjs +57 -0
  9. package/dist/components/core/Button.cjs.map +1 -0
  10. package/dist/components/core/Button.d.cts +26 -0
  11. package/dist/components/core/Button.d.ts +26 -0
  12. package/dist/components/core/Button.mjs +55 -0
  13. package/dist/components/core/Button.mjs.map +1 -0
  14. package/dist/components/core/Card.cjs +57 -0
  15. package/dist/components/core/Card.cjs.map +1 -0
  16. package/dist/components/core/Card.d.cts +27 -0
  17. package/dist/components/core/Card.d.ts +27 -0
  18. package/dist/components/core/Card.mjs +54 -0
  19. package/dist/components/core/Card.mjs.map +1 -0
  20. package/dist/components/core/FilterChip.cjs +54 -0
  21. package/dist/components/core/FilterChip.cjs.map +1 -0
  22. package/dist/components/core/FilterChip.d.cts +23 -0
  23. package/dist/components/core/FilterChip.d.ts +23 -0
  24. package/dist/components/core/FilterChip.mjs +52 -0
  25. package/dist/components/core/FilterChip.mjs.map +1 -0
  26. package/dist/components/core/Input.cjs +67 -0
  27. package/dist/components/core/Input.cjs.map +1 -0
  28. package/dist/components/core/Input.d.cts +20 -0
  29. package/dist/components/core/Input.d.ts +20 -0
  30. package/dist/components/core/Input.mjs +65 -0
  31. package/dist/components/core/Input.mjs.map +1 -0
  32. package/dist/components/core/StatusPill.cjs +57 -0
  33. package/dist/components/core/StatusPill.cjs.map +1 -0
  34. package/dist/components/core/StatusPill.d.cts +19 -0
  35. package/dist/components/core/StatusPill.d.ts +19 -0
  36. package/dist/components/core/StatusPill.mjs +55 -0
  37. package/dist/components/core/StatusPill.mjs.map +1 -0
  38. package/dist/components/core/Surface.cjs +52 -0
  39. package/dist/components/core/Surface.cjs.map +1 -0
  40. package/dist/components/core/Surface.d.cts +24 -0
  41. package/dist/components/core/Surface.d.ts +24 -0
  42. package/dist/components/core/Surface.mjs +47 -0
  43. package/dist/components/core/Surface.mjs.map +1 -0
  44. package/dist/components/core/Tabs.cjs +64 -0
  45. package/dist/components/core/Tabs.cjs.map +1 -0
  46. package/dist/components/core/Tabs.d.cts +24 -0
  47. package/dist/components/core/Tabs.d.ts +24 -0
  48. package/dist/components/core/Tabs.mjs +62 -0
  49. package/dist/components/core/Tabs.mjs.map +1 -0
  50. package/dist/components/data/Avatar.cjs +40 -0
  51. package/dist/components/data/Avatar.cjs.map +1 -0
  52. package/dist/components/data/Avatar.d.cts +24 -0
  53. package/dist/components/data/Avatar.d.ts +24 -0
  54. package/dist/components/data/Avatar.mjs +38 -0
  55. package/dist/components/data/Avatar.mjs.map +1 -0
  56. package/dist/components/data/CodeBlock.cjs +92 -0
  57. package/dist/components/data/CodeBlock.cjs.map +1 -0
  58. package/dist/components/data/CodeBlock.d.cts +20 -0
  59. package/dist/components/data/CodeBlock.d.ts +20 -0
  60. package/dist/components/data/CodeBlock.mjs +90 -0
  61. package/dist/components/data/CodeBlock.mjs.map +1 -0
  62. package/dist/components/data/KeyValue.cjs +55 -0
  63. package/dist/components/data/KeyValue.cjs.map +1 -0
  64. package/dist/components/data/KeyValue.d.cts +24 -0
  65. package/dist/components/data/KeyValue.d.ts +24 -0
  66. package/dist/components/data/KeyValue.mjs +53 -0
  67. package/dist/components/data/KeyValue.mjs.map +1 -0
  68. package/dist/components/data/LogRow.cjs +55 -0
  69. package/dist/components/data/LogRow.cjs.map +1 -0
  70. package/dist/components/data/LogRow.d.cts +23 -0
  71. package/dist/components/data/LogRow.d.ts +23 -0
  72. package/dist/components/data/LogRow.mjs +53 -0
  73. package/dist/components/data/LogRow.mjs.map +1 -0
  74. package/dist/components/data/Pagination.cjs +44 -0
  75. package/dist/components/data/Pagination.cjs.map +1 -0
  76. package/dist/components/data/Pagination.d.cts +13 -0
  77. package/dist/components/data/Pagination.d.ts +13 -0
  78. package/dist/components/data/Pagination.mjs +42 -0
  79. package/dist/components/data/Pagination.mjs.map +1 -0
  80. package/dist/components/data/StatTile.cjs +20 -0
  81. package/dist/components/data/StatTile.cjs.map +1 -0
  82. package/dist/components/data/StatTile.d.cts +19 -0
  83. package/dist/components/data/StatTile.d.ts +19 -0
  84. package/dist/components/data/StatTile.mjs +18 -0
  85. package/dist/components/data/StatTile.mjs.map +1 -0
  86. package/dist/components/data/Table.cjs +45 -0
  87. package/dist/components/data/Table.cjs.map +1 -0
  88. package/dist/components/data/Table.d.cts +27 -0
  89. package/dist/components/data/Table.d.ts +27 -0
  90. package/dist/components/data/Table.mjs +43 -0
  91. package/dist/components/data/Table.mjs.map +1 -0
  92. package/dist/components/data/UsageMeter.cjs +28 -0
  93. package/dist/components/data/UsageMeter.cjs.map +1 -0
  94. package/dist/components/data/UsageMeter.d.cts +19 -0
  95. package/dist/components/data/UsageMeter.d.ts +19 -0
  96. package/dist/components/data/UsageMeter.mjs +26 -0
  97. package/dist/components/data/UsageMeter.mjs.map +1 -0
  98. package/dist/components/feedback/Alert.cjs +78 -0
  99. package/dist/components/feedback/Alert.cjs.map +1 -0
  100. package/dist/components/feedback/Alert.d.cts +29 -0
  101. package/dist/components/feedback/Alert.d.ts +29 -0
  102. package/dist/components/feedback/Alert.mjs +74 -0
  103. package/dist/components/feedback/Alert.mjs.map +1 -0
  104. package/dist/components/feedback/Dialog.cjs +62 -0
  105. package/dist/components/feedback/Dialog.cjs.map +1 -0
  106. package/dist/components/feedback/Dialog.d.cts +17 -0
  107. package/dist/components/feedback/Dialog.d.ts +17 -0
  108. package/dist/components/feedback/Dialog.mjs +60 -0
  109. package/dist/components/feedback/Dialog.mjs.map +1 -0
  110. package/dist/components/feedback/Drawer.cjs +58 -0
  111. package/dist/components/feedback/Drawer.cjs.map +1 -0
  112. package/dist/components/feedback/Drawer.d.cts +22 -0
  113. package/dist/components/feedback/Drawer.d.ts +22 -0
  114. package/dist/components/feedback/Drawer.mjs +56 -0
  115. package/dist/components/feedback/Drawer.mjs.map +1 -0
  116. package/dist/components/feedback/EmptyState.cjs +36 -0
  117. package/dist/components/feedback/EmptyState.cjs.map +1 -0
  118. package/dist/components/feedback/EmptyState.d.cts +14 -0
  119. package/dist/components/feedback/EmptyState.d.ts +14 -0
  120. package/dist/components/feedback/EmptyState.mjs +34 -0
  121. package/dist/components/feedback/EmptyState.mjs.map +1 -0
  122. package/dist/components/feedback/Skeleton.cjs +19 -0
  123. package/dist/components/feedback/Skeleton.cjs.map +1 -0
  124. package/dist/components/feedback/Skeleton.d.cts +12 -0
  125. package/dist/components/feedback/Skeleton.d.ts +12 -0
  126. package/dist/components/feedback/Skeleton.mjs +17 -0
  127. package/dist/components/feedback/Skeleton.mjs.map +1 -0
  128. package/dist/components/feedback/Spinner.cjs +17 -0
  129. package/dist/components/feedback/Spinner.cjs.map +1 -0
  130. package/dist/components/feedback/Spinner.d.cts +12 -0
  131. package/dist/components/feedback/Spinner.d.ts +12 -0
  132. package/dist/components/feedback/Spinner.mjs +15 -0
  133. package/dist/components/feedback/Spinner.mjs.map +1 -0
  134. package/dist/components/feedback/Toast.cjs +32 -0
  135. package/dist/components/feedback/Toast.cjs.map +1 -0
  136. package/dist/components/feedback/Toast.d.cts +20 -0
  137. package/dist/components/feedback/Toast.d.ts +20 -0
  138. package/dist/components/feedback/Toast.mjs +30 -0
  139. package/dist/components/feedback/Toast.mjs.map +1 -0
  140. package/dist/components/feedback/Tooltip.cjs +51 -0
  141. package/dist/components/feedback/Tooltip.cjs.map +1 -0
  142. package/dist/components/feedback/Tooltip.d.cts +11 -0
  143. package/dist/components/feedback/Tooltip.d.ts +11 -0
  144. package/dist/components/feedback/Tooltip.mjs +49 -0
  145. package/dist/components/feedback/Tooltip.mjs.map +1 -0
  146. package/dist/components/forms/Checkbox.cjs +74 -0
  147. package/dist/components/forms/Checkbox.cjs.map +1 -0
  148. package/dist/components/forms/Checkbox.d.cts +16 -0
  149. package/dist/components/forms/Checkbox.d.ts +16 -0
  150. package/dist/components/forms/Checkbox.mjs +72 -0
  151. package/dist/components/forms/Checkbox.mjs.map +1 -0
  152. package/dist/components/forms/Combobox.cjs +217 -0
  153. package/dist/components/forms/Combobox.cjs.map +1 -0
  154. package/dist/components/forms/Combobox.d.cts +27 -0
  155. package/dist/components/forms/Combobox.d.ts +27 -0
  156. package/dist/components/forms/Combobox.mjs +215 -0
  157. package/dist/components/forms/Combobox.mjs.map +1 -0
  158. package/dist/components/forms/FileUpload.cjs +187 -0
  159. package/dist/components/forms/FileUpload.cjs.map +1 -0
  160. package/dist/components/forms/FileUpload.d.cts +26 -0
  161. package/dist/components/forms/FileUpload.d.ts +26 -0
  162. package/dist/components/forms/FileUpload.mjs +185 -0
  163. package/dist/components/forms/FileUpload.mjs.map +1 -0
  164. package/dist/components/forms/RadioGroup.cjs +73 -0
  165. package/dist/components/forms/RadioGroup.cjs.map +1 -0
  166. package/dist/components/forms/RadioGroup.d.cts +21 -0
  167. package/dist/components/forms/RadioGroup.d.ts +21 -0
  168. package/dist/components/forms/RadioGroup.mjs +71 -0
  169. package/dist/components/forms/RadioGroup.mjs.map +1 -0
  170. package/dist/components/forms/SegmentedControl.cjs +67 -0
  171. package/dist/components/forms/SegmentedControl.cjs.map +1 -0
  172. package/dist/components/forms/SegmentedControl.d.cts +19 -0
  173. package/dist/components/forms/SegmentedControl.d.ts +19 -0
  174. package/dist/components/forms/SegmentedControl.mjs +65 -0
  175. package/dist/components/forms/SegmentedControl.mjs.map +1 -0
  176. package/dist/components/forms/Select.cjs +67 -0
  177. package/dist/components/forms/Select.cjs.map +1 -0
  178. package/dist/components/forms/Select.d.cts +23 -0
  179. package/dist/components/forms/Select.d.ts +23 -0
  180. package/dist/components/forms/Select.mjs +65 -0
  181. package/dist/components/forms/Select.mjs.map +1 -0
  182. package/dist/components/forms/Slider.cjs +129 -0
  183. package/dist/components/forms/Slider.cjs.map +1 -0
  184. package/dist/components/forms/Slider.d.cts +24 -0
  185. package/dist/components/forms/Slider.d.ts +24 -0
  186. package/dist/components/forms/Slider.mjs +127 -0
  187. package/dist/components/forms/Slider.mjs.map +1 -0
  188. package/dist/components/forms/Switch.cjs +101 -0
  189. package/dist/components/forms/Switch.cjs.map +1 -0
  190. package/dist/components/forms/Switch.d.cts +24 -0
  191. package/dist/components/forms/Switch.d.ts +24 -0
  192. package/dist/components/forms/Switch.mjs +98 -0
  193. package/dist/components/forms/Switch.mjs.map +1 -0
  194. package/dist/components/forms/Textarea.cjs +35 -0
  195. package/dist/components/forms/Textarea.cjs.map +1 -0
  196. package/dist/components/forms/Textarea.d.cts +15 -0
  197. package/dist/components/forms/Textarea.d.ts +15 -0
  198. package/dist/components/forms/Textarea.mjs +33 -0
  199. package/dist/components/forms/Textarea.mjs.map +1 -0
  200. package/dist/components/navigation/Breadcrumb.cjs +27 -0
  201. package/dist/components/navigation/Breadcrumb.cjs.map +1 -0
  202. package/dist/components/navigation/Breadcrumb.d.cts +15 -0
  203. package/dist/components/navigation/Breadcrumb.d.ts +15 -0
  204. package/dist/components/navigation/Breadcrumb.mjs +25 -0
  205. package/dist/components/navigation/Breadcrumb.mjs.map +1 -0
  206. package/dist/components/navigation/CommandPalette.cjs +136 -0
  207. package/dist/components/navigation/CommandPalette.cjs.map +1 -0
  208. package/dist/components/navigation/CommandPalette.d.cts +26 -0
  209. package/dist/components/navigation/CommandPalette.d.ts +26 -0
  210. package/dist/components/navigation/CommandPalette.mjs +134 -0
  211. package/dist/components/navigation/CommandPalette.mjs.map +1 -0
  212. package/dist/components/navigation/Menu.cjs +104 -0
  213. package/dist/components/navigation/Menu.cjs.map +1 -0
  214. package/dist/components/navigation/Menu.d.cts +20 -0
  215. package/dist/components/navigation/Menu.d.ts +20 -0
  216. package/dist/components/navigation/Menu.mjs +102 -0
  217. package/dist/components/navigation/Menu.mjs.map +1 -0
  218. package/dist/components/navigation/Sidebar.cjs +60 -0
  219. package/dist/components/navigation/Sidebar.cjs.map +1 -0
  220. package/dist/components/navigation/Sidebar.d.cts +30 -0
  221. package/dist/components/navigation/Sidebar.d.ts +30 -0
  222. package/dist/components/navigation/Sidebar.mjs +58 -0
  223. package/dist/components/navigation/Sidebar.mjs.map +1 -0
  224. package/dist/components/navigation/Stepper.cjs +55 -0
  225. package/dist/components/navigation/Stepper.cjs.map +1 -0
  226. package/dist/components/navigation/Stepper.d.cts +21 -0
  227. package/dist/components/navigation/Stepper.d.ts +21 -0
  228. package/dist/components/navigation/Stepper.mjs +53 -0
  229. package/dist/components/navigation/Stepper.mjs.map +1 -0
  230. package/dist/components/product/BrandLogo.cjs +159 -0
  231. package/dist/components/product/BrandLogo.cjs.map +1 -0
  232. package/dist/components/product/BrandLogo.d.cts +28 -0
  233. package/dist/components/product/BrandLogo.d.ts +28 -0
  234. package/dist/components/product/BrandLogo.mjs +156 -0
  235. package/dist/components/product/BrandLogo.mjs.map +1 -0
  236. package/dist/components/product/ProductActionPill.cjs +57 -0
  237. package/dist/components/product/ProductActionPill.cjs.map +1 -0
  238. package/dist/components/product/ProductActionPill.d.cts +31 -0
  239. package/dist/components/product/ProductActionPill.d.ts +31 -0
  240. package/dist/components/product/ProductActionPill.mjs +52 -0
  241. package/dist/components/product/ProductActionPill.mjs.map +1 -0
  242. package/dist/components/product/ProductCinematic.cjs +69 -0
  243. package/dist/components/product/ProductCinematic.cjs.map +1 -0
  244. package/dist/components/product/ProductCinematic.d.cts +33 -0
  245. package/dist/components/product/ProductCinematic.d.ts +33 -0
  246. package/dist/components/product/ProductCinematic.mjs +63 -0
  247. package/dist/components/product/ProductCinematic.mjs.map +1 -0
  248. package/dist/components/product/ProductPageHeader.cjs +35 -0
  249. package/dist/components/product/ProductPageHeader.cjs.map +1 -0
  250. package/dist/components/product/ProductPageHeader.d.cts +13 -0
  251. package/dist/components/product/ProductPageHeader.d.ts +13 -0
  252. package/dist/components/product/ProductPageHeader.mjs +33 -0
  253. package/dist/components/product/ProductPageHeader.mjs.map +1 -0
  254. package/dist/components/product/SectionCard.cjs +72 -0
  255. package/dist/components/product/SectionCard.cjs.map +1 -0
  256. package/dist/components/product/SectionCard.d.cts +20 -0
  257. package/dist/components/product/SectionCard.d.ts +20 -0
  258. package/dist/components/product/SectionCard.mjs +70 -0
  259. package/dist/components/product/SectionCard.mjs.map +1 -0
  260. package/dist/components/product/ToolCard.cjs +70 -0
  261. package/dist/components/product/ToolCard.cjs.map +1 -0
  262. package/dist/components/product/ToolCard.d.cts +22 -0
  263. package/dist/components/product/ToolCard.d.ts +22 -0
  264. package/dist/components/product/ToolCard.mjs +68 -0
  265. package/dist/components/product/ToolCard.mjs.map +1 -0
  266. package/dist/index.cjs +2593 -0
  267. package/dist/index.cjs.map +1 -0
  268. package/dist/index.d.cts +49 -0
  269. package/dist/index.d.ts +49 -0
  270. package/dist/index.mjs +2532 -0
  271. package/dist/index.mjs.map +1 -0
  272. package/dist/styles.css +463 -0
  273. package/package.json +50 -0
@@ -0,0 +1,217 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ // src/components/forms/Combobox.tsx
7
+ function Combobox({
8
+ label,
9
+ hint,
10
+ options = [],
11
+ value,
12
+ onChange,
13
+ placeholder = "\uAC80\uC0C9\u2026",
14
+ emptyText = "\uACB0\uACFC \uC5C6\uC74C",
15
+ id,
16
+ style
17
+ }) {
18
+ const [open, setOpen] = react.useState(false);
19
+ const [query, setQuery] = react.useState("");
20
+ const [active, setActive] = react.useState(0);
21
+ const rootRef = react.useRef(null);
22
+ const cbId = id || (label ? `cb-${label.replace(/\s+/g, "-")}` : void 0);
23
+ const selected = options.find((o) => o.value === value) || null;
24
+ react.useEffect(() => {
25
+ const onDoc = (e) => {
26
+ if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false);
27
+ };
28
+ document.addEventListener("mousedown", onDoc);
29
+ return () => document.removeEventListener("mousedown", onDoc);
30
+ }, []);
31
+ const q = query.trim().toLowerCase();
32
+ const filtered = q ? options.filter((o) => (o.label + " " + (o.meta || "")).toLowerCase().includes(q)) : options;
33
+ const commit = (o) => {
34
+ onChange?.(o.value);
35
+ setOpen(false);
36
+ setQuery("");
37
+ };
38
+ const onKey = (e) => {
39
+ if (e.key === "ArrowDown") {
40
+ e.preventDefault();
41
+ setOpen(true);
42
+ setActive((i) => Math.min(i + 1, filtered.length - 1));
43
+ } else if (e.key === "ArrowUp") {
44
+ e.preventDefault();
45
+ setActive((i) => Math.max(i - 1, 0));
46
+ } else if (e.key === "Enter" && open && filtered[active]) {
47
+ e.preventDefault();
48
+ commit(filtered[active]);
49
+ } else if (e.key === "Escape") {
50
+ setOpen(false);
51
+ }
52
+ };
53
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: rootRef, style: { display: "grid", gap: 7, position: "relative", ...style }, children: [
54
+ label ? /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: cbId, style: { fontSize: 13, fontWeight: 600, color: "var(--dt-muted-strong)" }, children: label }) : null,
55
+ /* @__PURE__ */ jsxRuntime.jsxs(
56
+ "div",
57
+ {
58
+ className: "dt-field",
59
+ style: {
60
+ display: "flex",
61
+ alignItems: "center",
62
+ gap: 9,
63
+ height: 44,
64
+ padding: "0 12px",
65
+ boxShadow: open ? "var(--dt-shadow-focus)" : void 0,
66
+ background: open ? "var(--dt-surface)" : "var(--dt-surface-sunken)"
67
+ },
68
+ onClick: () => setOpen(true),
69
+ children: [
70
+ /* @__PURE__ */ jsxRuntime.jsxs(
71
+ "svg",
72
+ {
73
+ width: "16",
74
+ height: "16",
75
+ viewBox: "0 0 24 24",
76
+ fill: "none",
77
+ "aria-hidden": "true",
78
+ style: { color: "var(--dt-muted)", flex: "0 0 auto" },
79
+ children: [
80
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "11", cy: "11", r: "7", stroke: "currentColor", strokeWidth: "2" }),
81
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 21l-4-4", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
82
+ ]
83
+ }
84
+ ),
85
+ /* @__PURE__ */ jsxRuntime.jsx(
86
+ "input",
87
+ {
88
+ id: cbId,
89
+ value: open ? query : selected ? selected.label : "",
90
+ placeholder: selected && !open ? selected.label : placeholder,
91
+ onChange: (e) => {
92
+ setQuery(e.target.value);
93
+ setOpen(true);
94
+ setActive(0);
95
+ },
96
+ onFocus: () => setOpen(true),
97
+ onKeyDown: onKey,
98
+ style: {
99
+ flex: 1,
100
+ minWidth: 0,
101
+ border: "none",
102
+ outline: "none",
103
+ background: "transparent",
104
+ fontSize: 14,
105
+ fontFamily: "inherit",
106
+ color: "var(--dt-ink-strong)"
107
+ }
108
+ }
109
+ ),
110
+ selected && !open ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontFamily: "var(--dt-font-mono)", fontSize: 11, color: "var(--dt-muted)" }, children: selected.meta }) : null
111
+ ]
112
+ }
113
+ ),
114
+ open ? /* @__PURE__ */ jsxRuntime.jsx(
115
+ "div",
116
+ {
117
+ role: "listbox",
118
+ style: {
119
+ position: "absolute",
120
+ top: "calc(100% + 6px)",
121
+ left: 0,
122
+ right: 0,
123
+ zIndex: 20,
124
+ background: "var(--dt-surface)",
125
+ border: "1px solid var(--dt-border-strong)",
126
+ borderRadius: "var(--dt-radius-lg)",
127
+ boxShadow: "var(--dt-shadow-md)",
128
+ maxHeight: 240,
129
+ overflowY: "auto",
130
+ padding: 4
131
+ },
132
+ children: filtered.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "12px 12px", fontSize: 13, color: "var(--dt-muted)" }, children: emptyText }) : filtered.map((o, i) => {
133
+ const isActive = i === active;
134
+ const isSel = o.value === value;
135
+ return /* @__PURE__ */ jsxRuntime.jsxs(
136
+ "div",
137
+ {
138
+ role: "option",
139
+ "aria-selected": isSel,
140
+ onMouseEnter: () => setActive(i),
141
+ onMouseDown: (e) => {
142
+ e.preventDefault();
143
+ commit(o);
144
+ },
145
+ style: {
146
+ display: "flex",
147
+ alignItems: "center",
148
+ gap: 10,
149
+ padding: "9px 10px",
150
+ borderRadius: "var(--dt-radius-md)",
151
+ cursor: "pointer",
152
+ background: isActive ? "var(--dt-surface-sunken)" : "transparent"
153
+ },
154
+ children: [
155
+ /* @__PURE__ */ jsxRuntime.jsx(
156
+ "span",
157
+ {
158
+ style: {
159
+ flex: 1,
160
+ minWidth: 0,
161
+ fontSize: 13.5,
162
+ fontWeight: isSel ? 600 : 500,
163
+ color: "var(--dt-ink-strong)",
164
+ overflow: "hidden",
165
+ textOverflow: "ellipsis",
166
+ whiteSpace: "nowrap"
167
+ },
168
+ children: o.label
169
+ }
170
+ ),
171
+ o.meta ? /* @__PURE__ */ jsxRuntime.jsx(
172
+ "span",
173
+ {
174
+ style: {
175
+ fontFamily: "var(--dt-font-mono)",
176
+ fontSize: 11,
177
+ color: "var(--dt-muted)",
178
+ flex: "0 0 auto"
179
+ },
180
+ children: o.meta
181
+ }
182
+ ) : null,
183
+ isSel ? /* @__PURE__ */ jsxRuntime.jsx(
184
+ "svg",
185
+ {
186
+ width: "15",
187
+ height: "15",
188
+ viewBox: "0 0 24 24",
189
+ fill: "none",
190
+ "aria-hidden": "true",
191
+ style: { color: "var(--dt-accent)", flex: "0 0 auto" },
192
+ children: /* @__PURE__ */ jsxRuntime.jsx(
193
+ "path",
194
+ {
195
+ d: "M20 6L9 17l-5-5",
196
+ stroke: "currentColor",
197
+ strokeWidth: "2.2",
198
+ strokeLinecap: "round",
199
+ strokeLinejoin: "round"
200
+ }
201
+ )
202
+ }
203
+ ) : null
204
+ ]
205
+ },
206
+ o.value
207
+ );
208
+ })
209
+ }
210
+ ) : null,
211
+ hint ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, color: "var(--dt-muted)" }, children: hint }) : null
212
+ ] });
213
+ }
214
+
215
+ exports.Combobox = Combobox;
216
+ //# sourceMappingURL=Combobox.cjs.map
217
+ //# sourceMappingURL=Combobox.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/forms/Combobox.tsx"],"names":["useState","useRef","useEffect","jsxs","jsx"],"mappings":";;;;;;AA0BO,SAAS,QAAA,CAAS;AAAA,EACvB,KAAA;AAAA,EACA,IAAA;AAAA,EACA,UAAU,EAAC;AAAA,EACX,KAAA;AAAA,EACA,QAAA;AAAA,EACA,WAAA,GAAc,oBAAA;AAAA,EACd,SAAA,GAAY,2BAAA;AAAA,EACZ,EAAA;AAAA,EACA;AACF,CAAA,EAAkB;AAChB,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAIA,eAAS,KAAK,CAAA;AACtC,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,eAAS,EAAE,CAAA;AACrC,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAIA,eAAS,CAAC,CAAA;AACtC,EAAA,MAAM,OAAA,GAAUC,aAAuB,IAAI,CAAA;AAC3C,EAAA,MAAM,IAAA,GAAO,OAAO,KAAA,GAAQ,CAAA,GAAA,EAAM,MAAM,OAAA,CAAQ,MAAA,EAAQ,GAAG,CAAC,CAAA,CAAA,GAAK,MAAA,CAAA;AAEjE,EAAA,MAAM,QAAA,GAAW,QAAQ,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,KAAA,KAAU,KAAK,CAAA,IAAK,IAAA;AAE3D,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,MAAM,KAAA,GAAQ,CAAC,CAAA,KAA6B;AAC1C,MAAA,IAAI,OAAA,CAAQ,OAAA,IAAW,CAAC,OAAA,CAAQ,OAAA,CAAQ,SAAS,CAAA,CAAE,MAAc,CAAA,EAAG,OAAA,CAAQ,KAAK,CAAA;AAAA,IACnF,CAAA;AACA,IAAA,QAAA,CAAS,gBAAA,CAAiB,aAAa,KAAK,CAAA;AAC5C,IAAA,OAAO,MAAM,QAAA,CAAS,mBAAA,CAAoB,WAAA,EAAa,KAAK,CAAA;AAAA,EAC9D,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,CAAA,GAAI,KAAA,CAAM,IAAA,EAAK,CAAE,WAAA,EAAY;AACnC,EAAA,MAAM,WAAW,CAAA,GACb,OAAA,CAAQ,MAAA,CAAO,CAAC,OAAO,CAAA,CAAE,KAAA,GAAQ,GAAA,IAAO,CAAA,CAAE,QAAQ,EAAA,CAAA,EAAK,WAAA,GAAc,QAAA,CAAS,CAAC,CAAC,CAAA,GAChF,OAAA;AAEJ,EAAA,MAAM,MAAA,GAAS,CAAC,CAAA,KAAsB;AACpC,IAAA,QAAA,GAAW,EAAE,KAAK,CAAA;AAClB,IAAA,OAAA,CAAQ,KAAK,CAAA;AACb,IAAA,QAAA,CAAS,EAAE,CAAA;AAAA,EACb,CAAA;AAEA,EAAA,MAAM,KAAA,GAAQ,CAAC,CAAA,KAAuC;AACpD,IAAA,IAAI,CAAA,CAAE,QAAQ,WAAA,EAAa;AACzB,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,OAAA,CAAQ,IAAI,CAAA;AACZ,MAAA,SAAA,CAAU,CAAC,MAAM,IAAA,CAAK,GAAA,CAAI,IAAI,CAAA,EAAG,QAAA,CAAS,MAAA,GAAS,CAAC,CAAC,CAAA;AAAA,IACvD,CAAA,MAAA,IAAW,CAAA,CAAE,GAAA,KAAQ,SAAA,EAAW;AAC9B,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,SAAA,CAAU,CAAC,CAAA,KAAM,IAAA,CAAK,IAAI,CAAA,GAAI,CAAA,EAAG,CAAC,CAAC,CAAA;AAAA,IACrC,WAAW,CAAA,CAAE,GAAA,KAAQ,WAAW,IAAA,IAAQ,QAAA,CAAS,MAAM,CAAA,EAAG;AACxD,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,MAAA,CAAO,QAAA,CAAS,MAAM,CAAC,CAAA;AAAA,IACzB,CAAA,MAAA,IAAW,CAAA,CAAE,GAAA,KAAQ,QAAA,EAAU;AAC7B,MAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,IACf;AAAA,EACF,CAAA;AAEA,EAAA,uBACEC,eAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAK,OAAA,EAAS,OAAO,EAAE,OAAA,EAAS,MAAA,EAAQ,GAAA,EAAK,CAAA,EAAG,QAAA,EAAU,UAAA,EAAY,GAAG,OAAM,EACjF,QAAA,EAAA;AAAA,IAAA,KAAA,mBACCC,cAAA,CAAC,OAAA,EAAA,EAAM,OAAA,EAAS,IAAA,EAAM,OAAO,EAAE,QAAA,EAAU,EAAA,EAAI,UAAA,EAAY,GAAA,EAAK,KAAA,EAAO,wBAAA,EAAyB,EAC3F,iBACH,CAAA,GACE,IAAA;AAAA,oBACJD,eAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAU,UAAA;AAAA,QACV,KAAA,EAAO;AAAA,UACL,OAAA,EAAS,MAAA;AAAA,UACT,UAAA,EAAY,QAAA;AAAA,UACZ,GAAA,EAAK,CAAA;AAAA,UACL,MAAA,EAAQ,EAAA;AAAA,UACR,OAAA,EAAS,QAAA;AAAA,UACT,SAAA,EAAW,OAAO,wBAAA,GAA2B,MAAA;AAAA,UAC7C,UAAA,EAAY,OAAO,mBAAA,GAAsB;AAAA,SAC3C;AAAA,QACA,OAAA,EAAS,MAAM,OAAA,CAAQ,IAAI,CAAA;AAAA,QAE3B,QAAA,EAAA;AAAA,0BAAAA,eAAA;AAAA,YAAC,KAAA;AAAA,YAAA;AAAA,cACC,KAAA,EAAM,IAAA;AAAA,cACN,MAAA,EAAO,IAAA;AAAA,cACP,OAAA,EAAQ,WAAA;AAAA,cACR,IAAA,EAAK,MAAA;AAAA,cACL,aAAA,EAAY,MAAA;AAAA,cACZ,KAAA,EAAO,EAAE,KAAA,EAAO,iBAAA,EAAmB,MAAM,UAAA,EAAW;AAAA,cAEpD,QAAA,EAAA;AAAA,gCAAAC,cAAA,CAAC,QAAA,EAAA,EAAO,EAAA,EAAG,IAAA,EAAK,EAAA,EAAG,IAAA,EAAK,GAAE,GAAA,EAAI,MAAA,EAAO,cAAA,EAAe,WAAA,EAAY,GAAA,EAAI,CAAA;AAAA,gCACpEA,cAAA,CAAC,UAAK,CAAA,EAAE,aAAA,EAAc,QAAO,cAAA,EAAe,WAAA,EAAY,GAAA,EAAI,aAAA,EAAc,OAAA,EAAQ;AAAA;AAAA;AAAA,WACpF;AAAA,0BACAA,cAAA;AAAA,YAAC,OAAA;AAAA,YAAA;AAAA,cACC,EAAA,EAAI,IAAA;AAAA,cACJ,KAAA,EAAO,IAAA,GAAO,KAAA,GAAQ,QAAA,GAAW,SAAS,KAAA,GAAQ,EAAA;AAAA,cAClD,WAAA,EAAa,QAAA,IAAY,CAAC,IAAA,GAAO,SAAS,KAAA,GAAQ,WAAA;AAAA,cAClD,QAAA,EAAU,CAAC,CAAA,KAAM;AACf,gBAAA,QAAA,CAAS,CAAA,CAAE,OAAO,KAAK,CAAA;AACvB,gBAAA,OAAA,CAAQ,IAAI,CAAA;AACZ,gBAAA,SAAA,CAAU,CAAC,CAAA;AAAA,cACb,CAAA;AAAA,cACA,OAAA,EAAS,MAAM,OAAA,CAAQ,IAAI,CAAA;AAAA,cAC3B,SAAA,EAAW,KAAA;AAAA,cACX,KAAA,EAAO;AAAA,gBACL,IAAA,EAAM,CAAA;AAAA,gBACN,QAAA,EAAU,CAAA;AAAA,gBACV,MAAA,EAAQ,MAAA;AAAA,gBACR,OAAA,EAAS,MAAA;AAAA,gBACT,UAAA,EAAY,aAAA;AAAA,gBACZ,QAAA,EAAU,EAAA;AAAA,gBACV,UAAA,EAAY,SAAA;AAAA,gBACZ,KAAA,EAAO;AAAA;AACT;AAAA,WACF;AAAA,UACC,YAAY,CAAC,IAAA,mBACZA,cAAA,CAAC,MAAA,EAAA,EAAK,OAAO,EAAE,UAAA,EAAY,qBAAA,EAAuB,QAAA,EAAU,IAAI,KAAA,EAAO,iBAAA,EAAkB,EACtF,QAAA,EAAA,QAAA,CAAS,MACZ,CAAA,GACE;AAAA;AAAA;AAAA,KACN;AAAA,IAEC,IAAA,mBACCA,cAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAK,SAAA;AAAA,QACL,KAAA,EAAO;AAAA,UACL,QAAA,EAAU,UAAA;AAAA,UACV,GAAA,EAAK,kBAAA;AAAA,UACL,IAAA,EAAM,CAAA;AAAA,UACN,KAAA,EAAO,CAAA;AAAA,UACP,MAAA,EAAQ,EAAA;AAAA,UACR,UAAA,EAAY,mBAAA;AAAA,UACZ,MAAA,EAAQ,mCAAA;AAAA,UACR,YAAA,EAAc,qBAAA;AAAA,UACd,SAAA,EAAW,qBAAA;AAAA,UACX,SAAA,EAAW,GAAA;AAAA,UACX,SAAA,EAAW,MAAA;AAAA,UACX,OAAA,EAAS;AAAA,SACX;AAAA,QAEC,QAAA,EAAA,QAAA,CAAS,WAAW,CAAA,mBACnBA,cAAA,CAAC,SAAI,KAAA,EAAO,EAAE,SAAS,WAAA,EAAa,QAAA,EAAU,IAAI,KAAA,EAAO,iBAAA,IAAsB,QAAA,EAAA,SAAA,EAAU,CAAA,GAEzF,SAAS,GAAA,CAAI,CAAC,GAAG,CAAA,KAAM;AACrB,UAAA,MAAM,WAAW,CAAA,KAAM,MAAA;AACvB,UAAA,MAAM,KAAA,GAAQ,EAAE,KAAA,KAAU,KAAA;AAC1B,UAAA,uBACED,eAAA;AAAA,YAAC,KAAA;AAAA,YAAA;AAAA,cAEC,IAAA,EAAK,QAAA;AAAA,cACL,eAAA,EAAe,KAAA;AAAA,cACf,YAAA,EAAc,MAAM,SAAA,CAAU,CAAC,CAAA;AAAA,cAC/B,WAAA,EAAa,CAAC,CAAA,KAAkC;AAC9C,gBAAA,CAAA,CAAE,cAAA,EAAe;AACjB,gBAAA,MAAA,CAAO,CAAC,CAAA;AAAA,cACV,CAAA;AAAA,cACA,KAAA,EAAO;AAAA,gBACL,OAAA,EAAS,MAAA;AAAA,gBACT,UAAA,EAAY,QAAA;AAAA,gBACZ,GAAA,EAAK,EAAA;AAAA,gBACL,OAAA,EAAS,UAAA;AAAA,gBACT,YAAA,EAAc,qBAAA;AAAA,gBACd,MAAA,EAAQ,SAAA;AAAA,gBACR,UAAA,EAAY,WAAW,0BAAA,GAA6B;AAAA,eACtD;AAAA,cAEA,QAAA,EAAA;AAAA,gCAAAC,cAAA;AAAA,kBAAC,MAAA;AAAA,kBAAA;AAAA,oBACC,KAAA,EAAO;AAAA,sBACL,IAAA,EAAM,CAAA;AAAA,sBACN,QAAA,EAAU,CAAA;AAAA,sBACV,QAAA,EAAU,IAAA;AAAA,sBACV,UAAA,EAAY,QAAQ,GAAA,GAAM,GAAA;AAAA,sBAC1B,KAAA,EAAO,sBAAA;AAAA,sBACP,QAAA,EAAU,QAAA;AAAA,sBACV,YAAA,EAAc,UAAA;AAAA,sBACd,UAAA,EAAY;AAAA,qBACd;AAAA,oBAEC,QAAA,EAAA,CAAA,CAAE;AAAA;AAAA,iBACL;AAAA,gBACC,EAAE,IAAA,mBACDA,cAAA;AAAA,kBAAC,MAAA;AAAA,kBAAA;AAAA,oBACC,KAAA,EAAO;AAAA,sBACL,UAAA,EAAY,qBAAA;AAAA,sBACZ,QAAA,EAAU,EAAA;AAAA,sBACV,KAAA,EAAO,iBAAA;AAAA,sBACP,IAAA,EAAM;AAAA,qBACR;AAAA,oBAEC,QAAA,EAAA,CAAA,CAAE;AAAA;AAAA,iBACL,GACE,IAAA;AAAA,gBACH,KAAA,mBACCA,cAAA;AAAA,kBAAC,KAAA;AAAA,kBAAA;AAAA,oBACC,KAAA,EAAM,IAAA;AAAA,oBACN,MAAA,EAAO,IAAA;AAAA,oBACP,OAAA,EAAQ,WAAA;AAAA,oBACR,IAAA,EAAK,MAAA;AAAA,oBACL,aAAA,EAAY,MAAA;AAAA,oBACZ,KAAA,EAAO,EAAE,KAAA,EAAO,kBAAA,EAAoB,MAAM,UAAA,EAAW;AAAA,oBAErD,QAAA,kBAAAA,cAAA;AAAA,sBAAC,MAAA;AAAA,sBAAA;AAAA,wBACC,CAAA,EAAE,iBAAA;AAAA,wBACF,MAAA,EAAO,cAAA;AAAA,wBACP,WAAA,EAAY,KAAA;AAAA,wBACZ,aAAA,EAAc,OAAA;AAAA,wBACd,cAAA,EAAe;AAAA;AAAA;AACjB;AAAA,iBACF,GACE;AAAA;AAAA,aAAA;AAAA,YA7DC,CAAA,CAAE;AAAA,WA8DT;AAAA,QAEJ,CAAC;AAAA;AAAA,KAEL,GACE,IAAA;AAAA,IACH,IAAA,mBAAOA,cAAA,CAAC,MAAA,EAAA,EAAK,KAAA,EAAO,EAAE,QAAA,EAAU,EAAA,EAAI,KAAA,EAAO,iBAAA,EAAkB,EAAI,QAAA,EAAA,IAAA,EAAK,CAAA,GAAU;AAAA,GAAA,EACnF,CAAA;AAEJ","file":"Combobox.cjs","sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport type { CSSProperties, HTMLAttributes, KeyboardEvent, MouseEvent } from 'react';\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n meta?: string;\n}\n\nexport interface ComboboxProps extends Omit<HTMLAttributes<HTMLDivElement>, 'id' | 'onChange' | 'style'> {\n label?: string;\n hint?: string;\n options?: ComboboxOption[];\n value?: string;\n onChange?: (value: string) => void;\n placeholder?: string;\n emptyText?: string;\n id?: string;\n style?: CSSProperties;\n}\n\n/**\n * Searchable select for large option sets (the 230+ public-data API catalog).\n * Hairline field; the listbox is a bordered plane. Filters on label + meta.\n * @startingPoint section=\"Forms\" subtitle=\"Searchable select over a large catalog\" viewport=\"460x320\"\n */\nexport function Combobox({\n label,\n hint,\n options = [],\n value,\n onChange,\n placeholder = '검색…',\n emptyText = '결과 없음',\n id,\n style,\n}: ComboboxProps) {\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState('');\n const [active, setActive] = useState(0);\n const rootRef = useRef<HTMLDivElement>(null);\n const cbId = id || (label ? `cb-${label.replace(/\\s+/g, '-')}` : undefined);\n\n const selected = options.find((o) => o.value === value) || null;\n\n useEffect(() => {\n const onDoc = (e: globalThis.MouseEvent) => {\n if (rootRef.current && !rootRef.current.contains(e.target as Node)) setOpen(false);\n };\n document.addEventListener('mousedown', onDoc);\n return () => document.removeEventListener('mousedown', onDoc);\n }, []);\n\n const q = query.trim().toLowerCase();\n const filtered = q\n ? options.filter((o) => (o.label + ' ' + (o.meta || '')).toLowerCase().includes(q))\n : options;\n\n const commit = (o: ComboboxOption) => {\n onChange?.(o.value);\n setOpen(false);\n setQuery('');\n };\n\n const onKey = (e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n setOpen(true);\n setActive((i) => Math.min(i + 1, filtered.length - 1));\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n setActive((i) => Math.max(i - 1, 0));\n } else if (e.key === 'Enter' && open && filtered[active]) {\n e.preventDefault();\n commit(filtered[active]);\n } else if (e.key === 'Escape') {\n setOpen(false);\n }\n };\n\n return (\n <div ref={rootRef} style={{ display: 'grid', gap: 7, position: 'relative', ...style }}>\n {label ? (\n <label htmlFor={cbId} style={{ fontSize: 13, fontWeight: 600, color: 'var(--dt-muted-strong)' }}>\n {label}\n </label>\n ) : null}\n <div\n className=\"dt-field\"\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 9,\n height: 44,\n padding: '0 12px',\n boxShadow: open ? 'var(--dt-shadow-focus)' : undefined,\n background: open ? 'var(--dt-surface)' : 'var(--dt-surface-sunken)',\n }}\n onClick={() => setOpen(true)}\n >\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n aria-hidden=\"true\"\n style={{ color: 'var(--dt-muted)', flex: '0 0 auto' }}\n >\n <circle cx=\"11\" cy=\"11\" r=\"7\" stroke=\"currentColor\" strokeWidth=\"2\" />\n <path d=\"M21 21l-4-4\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" />\n </svg>\n <input\n id={cbId}\n value={open ? query : selected ? selected.label : ''}\n placeholder={selected && !open ? selected.label : placeholder}\n onChange={(e) => {\n setQuery(e.target.value);\n setOpen(true);\n setActive(0);\n }}\n onFocus={() => setOpen(true)}\n onKeyDown={onKey}\n style={{\n flex: 1,\n minWidth: 0,\n border: 'none',\n outline: 'none',\n background: 'transparent',\n fontSize: 14,\n fontFamily: 'inherit',\n color: 'var(--dt-ink-strong)',\n }}\n />\n {selected && !open ? (\n <span style={{ fontFamily: 'var(--dt-font-mono)', fontSize: 11, color: 'var(--dt-muted)' }}>\n {selected.meta}\n </span>\n ) : null}\n </div>\n\n {open ? (\n <div\n role=\"listbox\"\n style={{\n position: 'absolute',\n top: 'calc(100% + 6px)',\n left: 0,\n right: 0,\n zIndex: 20,\n background: 'var(--dt-surface)',\n border: '1px solid var(--dt-border-strong)',\n borderRadius: 'var(--dt-radius-lg)',\n boxShadow: 'var(--dt-shadow-md)',\n maxHeight: 240,\n overflowY: 'auto',\n padding: 4,\n }}\n >\n {filtered.length === 0 ? (\n <div style={{ padding: '12px 12px', fontSize: 13, color: 'var(--dt-muted)' }}>{emptyText}</div>\n ) : (\n filtered.map((o, i) => {\n const isActive = i === active;\n const isSel = o.value === value;\n return (\n <div\n key={o.value}\n role=\"option\"\n aria-selected={isSel}\n onMouseEnter={() => setActive(i)}\n onMouseDown={(e: MouseEvent<HTMLDivElement>) => {\n e.preventDefault();\n commit(o);\n }}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 10,\n padding: '9px 10px',\n borderRadius: 'var(--dt-radius-md)',\n cursor: 'pointer',\n background: isActive ? 'var(--dt-surface-sunken)' : 'transparent',\n }}\n >\n <span\n style={{\n flex: 1,\n minWidth: 0,\n fontSize: 13.5,\n fontWeight: isSel ? 600 : 500,\n color: 'var(--dt-ink-strong)',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {o.label}\n </span>\n {o.meta ? (\n <span\n style={{\n fontFamily: 'var(--dt-font-mono)',\n fontSize: 11,\n color: 'var(--dt-muted)',\n flex: '0 0 auto',\n }}\n >\n {o.meta}\n </span>\n ) : null}\n {isSel ? (\n <svg\n width=\"15\"\n height=\"15\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n aria-hidden=\"true\"\n style={{ color: 'var(--dt-accent)', flex: '0 0 auto' }}\n >\n <path\n d=\"M20 6L9 17l-5-5\"\n stroke=\"currentColor\"\n strokeWidth=\"2.2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n ) : null}\n </div>\n );\n })\n )}\n </div>\n ) : null}\n {hint ? <span style={{ fontSize: 12, color: 'var(--dt-muted)' }}>{hint}</span> : null}\n </div>\n );\n}\n"]}
@@ -0,0 +1,27 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, CSSProperties } from 'react';
3
+
4
+ interface ComboboxOption {
5
+ value: string;
6
+ label: string;
7
+ meta?: string;
8
+ }
9
+ interface ComboboxProps extends Omit<HTMLAttributes<HTMLDivElement>, 'id' | 'onChange' | 'style'> {
10
+ label?: string;
11
+ hint?: string;
12
+ options?: ComboboxOption[];
13
+ value?: string;
14
+ onChange?: (value: string) => void;
15
+ placeholder?: string;
16
+ emptyText?: string;
17
+ id?: string;
18
+ style?: CSSProperties;
19
+ }
20
+ /**
21
+ * Searchable select for large option sets (the 230+ public-data API catalog).
22
+ * Hairline field; the listbox is a bordered plane. Filters on label + meta.
23
+ * @startingPoint section="Forms" subtitle="Searchable select over a large catalog" viewport="460x320"
24
+ */
25
+ declare function Combobox({ label, hint, options, value, onChange, placeholder, emptyText, id, style, }: ComboboxProps): react.JSX.Element;
26
+
27
+ export { Combobox, type ComboboxOption, type ComboboxProps };
@@ -0,0 +1,27 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, CSSProperties } from 'react';
3
+
4
+ interface ComboboxOption {
5
+ value: string;
6
+ label: string;
7
+ meta?: string;
8
+ }
9
+ interface ComboboxProps extends Omit<HTMLAttributes<HTMLDivElement>, 'id' | 'onChange' | 'style'> {
10
+ label?: string;
11
+ hint?: string;
12
+ options?: ComboboxOption[];
13
+ value?: string;
14
+ onChange?: (value: string) => void;
15
+ placeholder?: string;
16
+ emptyText?: string;
17
+ id?: string;
18
+ style?: CSSProperties;
19
+ }
20
+ /**
21
+ * Searchable select for large option sets (the 230+ public-data API catalog).
22
+ * Hairline field; the listbox is a bordered plane. Filters on label + meta.
23
+ * @startingPoint section="Forms" subtitle="Searchable select over a large catalog" viewport="460x320"
24
+ */
25
+ declare function Combobox({ label, hint, options, value, onChange, placeholder, emptyText, id, style, }: ComboboxProps): react.JSX.Element;
26
+
27
+ export { Combobox, type ComboboxOption, type ComboboxProps };
@@ -0,0 +1,215 @@
1
+ import { useState, useRef, useEffect } from 'react';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+
4
+ // src/components/forms/Combobox.tsx
5
+ function Combobox({
6
+ label,
7
+ hint,
8
+ options = [],
9
+ value,
10
+ onChange,
11
+ placeholder = "\uAC80\uC0C9\u2026",
12
+ emptyText = "\uACB0\uACFC \uC5C6\uC74C",
13
+ id,
14
+ style
15
+ }) {
16
+ const [open, setOpen] = useState(false);
17
+ const [query, setQuery] = useState("");
18
+ const [active, setActive] = useState(0);
19
+ const rootRef = useRef(null);
20
+ const cbId = id || (label ? `cb-${label.replace(/\s+/g, "-")}` : void 0);
21
+ const selected = options.find((o) => o.value === value) || null;
22
+ useEffect(() => {
23
+ const onDoc = (e) => {
24
+ if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false);
25
+ };
26
+ document.addEventListener("mousedown", onDoc);
27
+ return () => document.removeEventListener("mousedown", onDoc);
28
+ }, []);
29
+ const q = query.trim().toLowerCase();
30
+ const filtered = q ? options.filter((o) => (o.label + " " + (o.meta || "")).toLowerCase().includes(q)) : options;
31
+ const commit = (o) => {
32
+ onChange?.(o.value);
33
+ setOpen(false);
34
+ setQuery("");
35
+ };
36
+ const onKey = (e) => {
37
+ if (e.key === "ArrowDown") {
38
+ e.preventDefault();
39
+ setOpen(true);
40
+ setActive((i) => Math.min(i + 1, filtered.length - 1));
41
+ } else if (e.key === "ArrowUp") {
42
+ e.preventDefault();
43
+ setActive((i) => Math.max(i - 1, 0));
44
+ } else if (e.key === "Enter" && open && filtered[active]) {
45
+ e.preventDefault();
46
+ commit(filtered[active]);
47
+ } else if (e.key === "Escape") {
48
+ setOpen(false);
49
+ }
50
+ };
51
+ return /* @__PURE__ */ jsxs("div", { ref: rootRef, style: { display: "grid", gap: 7, position: "relative", ...style }, children: [
52
+ label ? /* @__PURE__ */ jsx("label", { htmlFor: cbId, style: { fontSize: 13, fontWeight: 600, color: "var(--dt-muted-strong)" }, children: label }) : null,
53
+ /* @__PURE__ */ jsxs(
54
+ "div",
55
+ {
56
+ className: "dt-field",
57
+ style: {
58
+ display: "flex",
59
+ alignItems: "center",
60
+ gap: 9,
61
+ height: 44,
62
+ padding: "0 12px",
63
+ boxShadow: open ? "var(--dt-shadow-focus)" : void 0,
64
+ background: open ? "var(--dt-surface)" : "var(--dt-surface-sunken)"
65
+ },
66
+ onClick: () => setOpen(true),
67
+ children: [
68
+ /* @__PURE__ */ jsxs(
69
+ "svg",
70
+ {
71
+ width: "16",
72
+ height: "16",
73
+ viewBox: "0 0 24 24",
74
+ fill: "none",
75
+ "aria-hidden": "true",
76
+ style: { color: "var(--dt-muted)", flex: "0 0 auto" },
77
+ children: [
78
+ /* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "7", stroke: "currentColor", strokeWidth: "2" }),
79
+ /* @__PURE__ */ jsx("path", { d: "M21 21l-4-4", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
80
+ ]
81
+ }
82
+ ),
83
+ /* @__PURE__ */ jsx(
84
+ "input",
85
+ {
86
+ id: cbId,
87
+ value: open ? query : selected ? selected.label : "",
88
+ placeholder: selected && !open ? selected.label : placeholder,
89
+ onChange: (e) => {
90
+ setQuery(e.target.value);
91
+ setOpen(true);
92
+ setActive(0);
93
+ },
94
+ onFocus: () => setOpen(true),
95
+ onKeyDown: onKey,
96
+ style: {
97
+ flex: 1,
98
+ minWidth: 0,
99
+ border: "none",
100
+ outline: "none",
101
+ background: "transparent",
102
+ fontSize: 14,
103
+ fontFamily: "inherit",
104
+ color: "var(--dt-ink-strong)"
105
+ }
106
+ }
107
+ ),
108
+ selected && !open ? /* @__PURE__ */ jsx("span", { style: { fontFamily: "var(--dt-font-mono)", fontSize: 11, color: "var(--dt-muted)" }, children: selected.meta }) : null
109
+ ]
110
+ }
111
+ ),
112
+ open ? /* @__PURE__ */ jsx(
113
+ "div",
114
+ {
115
+ role: "listbox",
116
+ style: {
117
+ position: "absolute",
118
+ top: "calc(100% + 6px)",
119
+ left: 0,
120
+ right: 0,
121
+ zIndex: 20,
122
+ background: "var(--dt-surface)",
123
+ border: "1px solid var(--dt-border-strong)",
124
+ borderRadius: "var(--dt-radius-lg)",
125
+ boxShadow: "var(--dt-shadow-md)",
126
+ maxHeight: 240,
127
+ overflowY: "auto",
128
+ padding: 4
129
+ },
130
+ children: filtered.length === 0 ? /* @__PURE__ */ jsx("div", { style: { padding: "12px 12px", fontSize: 13, color: "var(--dt-muted)" }, children: emptyText }) : filtered.map((o, i) => {
131
+ const isActive = i === active;
132
+ const isSel = o.value === value;
133
+ return /* @__PURE__ */ jsxs(
134
+ "div",
135
+ {
136
+ role: "option",
137
+ "aria-selected": isSel,
138
+ onMouseEnter: () => setActive(i),
139
+ onMouseDown: (e) => {
140
+ e.preventDefault();
141
+ commit(o);
142
+ },
143
+ style: {
144
+ display: "flex",
145
+ alignItems: "center",
146
+ gap: 10,
147
+ padding: "9px 10px",
148
+ borderRadius: "var(--dt-radius-md)",
149
+ cursor: "pointer",
150
+ background: isActive ? "var(--dt-surface-sunken)" : "transparent"
151
+ },
152
+ children: [
153
+ /* @__PURE__ */ jsx(
154
+ "span",
155
+ {
156
+ style: {
157
+ flex: 1,
158
+ minWidth: 0,
159
+ fontSize: 13.5,
160
+ fontWeight: isSel ? 600 : 500,
161
+ color: "var(--dt-ink-strong)",
162
+ overflow: "hidden",
163
+ textOverflow: "ellipsis",
164
+ whiteSpace: "nowrap"
165
+ },
166
+ children: o.label
167
+ }
168
+ ),
169
+ o.meta ? /* @__PURE__ */ jsx(
170
+ "span",
171
+ {
172
+ style: {
173
+ fontFamily: "var(--dt-font-mono)",
174
+ fontSize: 11,
175
+ color: "var(--dt-muted)",
176
+ flex: "0 0 auto"
177
+ },
178
+ children: o.meta
179
+ }
180
+ ) : null,
181
+ isSel ? /* @__PURE__ */ jsx(
182
+ "svg",
183
+ {
184
+ width: "15",
185
+ height: "15",
186
+ viewBox: "0 0 24 24",
187
+ fill: "none",
188
+ "aria-hidden": "true",
189
+ style: { color: "var(--dt-accent)", flex: "0 0 auto" },
190
+ children: /* @__PURE__ */ jsx(
191
+ "path",
192
+ {
193
+ d: "M20 6L9 17l-5-5",
194
+ stroke: "currentColor",
195
+ strokeWidth: "2.2",
196
+ strokeLinecap: "round",
197
+ strokeLinejoin: "round"
198
+ }
199
+ )
200
+ }
201
+ ) : null
202
+ ]
203
+ },
204
+ o.value
205
+ );
206
+ })
207
+ }
208
+ ) : null,
209
+ hint ? /* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: "var(--dt-muted)" }, children: hint }) : null
210
+ ] });
211
+ }
212
+
213
+ export { Combobox };
214
+ //# sourceMappingURL=Combobox.mjs.map
215
+ //# sourceMappingURL=Combobox.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/forms/Combobox.tsx"],"names":[],"mappings":"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","file":"Combobox.mjs","sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport type { CSSProperties, HTMLAttributes, KeyboardEvent, MouseEvent } from 'react';\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n meta?: string;\n}\n\nexport interface ComboboxProps extends Omit<HTMLAttributes<HTMLDivElement>, 'id' | 'onChange' | 'style'> {\n label?: string;\n hint?: string;\n options?: ComboboxOption[];\n value?: string;\n onChange?: (value: string) => void;\n placeholder?: string;\n emptyText?: string;\n id?: string;\n style?: CSSProperties;\n}\n\n/**\n * Searchable select for large option sets (the 230+ public-data API catalog).\n * Hairline field; the listbox is a bordered plane. Filters on label + meta.\n * @startingPoint section=\"Forms\" subtitle=\"Searchable select over a large catalog\" viewport=\"460x320\"\n */\nexport function Combobox({\n label,\n hint,\n options = [],\n value,\n onChange,\n placeholder = '검색…',\n emptyText = '결과 없음',\n id,\n style,\n}: ComboboxProps) {\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState('');\n const [active, setActive] = useState(0);\n const rootRef = useRef<HTMLDivElement>(null);\n const cbId = id || (label ? `cb-${label.replace(/\\s+/g, '-')}` : undefined);\n\n const selected = options.find((o) => o.value === value) || null;\n\n useEffect(() => {\n const onDoc = (e: globalThis.MouseEvent) => {\n if (rootRef.current && !rootRef.current.contains(e.target as Node)) setOpen(false);\n };\n document.addEventListener('mousedown', onDoc);\n return () => document.removeEventListener('mousedown', onDoc);\n }, []);\n\n const q = query.trim().toLowerCase();\n const filtered = q\n ? options.filter((o) => (o.label + ' ' + (o.meta || '')).toLowerCase().includes(q))\n : options;\n\n const commit = (o: ComboboxOption) => {\n onChange?.(o.value);\n setOpen(false);\n setQuery('');\n };\n\n const onKey = (e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n setOpen(true);\n setActive((i) => Math.min(i + 1, filtered.length - 1));\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n setActive((i) => Math.max(i - 1, 0));\n } else if (e.key === 'Enter' && open && filtered[active]) {\n e.preventDefault();\n commit(filtered[active]);\n } else if (e.key === 'Escape') {\n setOpen(false);\n }\n };\n\n return (\n <div ref={rootRef} style={{ display: 'grid', gap: 7, position: 'relative', ...style }}>\n {label ? (\n <label htmlFor={cbId} style={{ fontSize: 13, fontWeight: 600, color: 'var(--dt-muted-strong)' }}>\n {label}\n </label>\n ) : null}\n <div\n className=\"dt-field\"\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 9,\n height: 44,\n padding: '0 12px',\n boxShadow: open ? 'var(--dt-shadow-focus)' : undefined,\n background: open ? 'var(--dt-surface)' : 'var(--dt-surface-sunken)',\n }}\n onClick={() => setOpen(true)}\n >\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n aria-hidden=\"true\"\n style={{ color: 'var(--dt-muted)', flex: '0 0 auto' }}\n >\n <circle cx=\"11\" cy=\"11\" r=\"7\" stroke=\"currentColor\" strokeWidth=\"2\" />\n <path d=\"M21 21l-4-4\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" />\n </svg>\n <input\n id={cbId}\n value={open ? query : selected ? selected.label : ''}\n placeholder={selected && !open ? selected.label : placeholder}\n onChange={(e) => {\n setQuery(e.target.value);\n setOpen(true);\n setActive(0);\n }}\n onFocus={() => setOpen(true)}\n onKeyDown={onKey}\n style={{\n flex: 1,\n minWidth: 0,\n border: 'none',\n outline: 'none',\n background: 'transparent',\n fontSize: 14,\n fontFamily: 'inherit',\n color: 'var(--dt-ink-strong)',\n }}\n />\n {selected && !open ? (\n <span style={{ fontFamily: 'var(--dt-font-mono)', fontSize: 11, color: 'var(--dt-muted)' }}>\n {selected.meta}\n </span>\n ) : null}\n </div>\n\n {open ? (\n <div\n role=\"listbox\"\n style={{\n position: 'absolute',\n top: 'calc(100% + 6px)',\n left: 0,\n right: 0,\n zIndex: 20,\n background: 'var(--dt-surface)',\n border: '1px solid var(--dt-border-strong)',\n borderRadius: 'var(--dt-radius-lg)',\n boxShadow: 'var(--dt-shadow-md)',\n maxHeight: 240,\n overflowY: 'auto',\n padding: 4,\n }}\n >\n {filtered.length === 0 ? (\n <div style={{ padding: '12px 12px', fontSize: 13, color: 'var(--dt-muted)' }}>{emptyText}</div>\n ) : (\n filtered.map((o, i) => {\n const isActive = i === active;\n const isSel = o.value === value;\n return (\n <div\n key={o.value}\n role=\"option\"\n aria-selected={isSel}\n onMouseEnter={() => setActive(i)}\n onMouseDown={(e: MouseEvent<HTMLDivElement>) => {\n e.preventDefault();\n commit(o);\n }}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 10,\n padding: '9px 10px',\n borderRadius: 'var(--dt-radius-md)',\n cursor: 'pointer',\n background: isActive ? 'var(--dt-surface-sunken)' : 'transparent',\n }}\n >\n <span\n style={{\n flex: 1,\n minWidth: 0,\n fontSize: 13.5,\n fontWeight: isSel ? 600 : 500,\n color: 'var(--dt-ink-strong)',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {o.label}\n </span>\n {o.meta ? (\n <span\n style={{\n fontFamily: 'var(--dt-font-mono)',\n fontSize: 11,\n color: 'var(--dt-muted)',\n flex: '0 0 auto',\n }}\n >\n {o.meta}\n </span>\n ) : null}\n {isSel ? (\n <svg\n width=\"15\"\n height=\"15\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n aria-hidden=\"true\"\n style={{ color: 'var(--dt-accent)', flex: '0 0 auto' }}\n >\n <path\n d=\"M20 6L9 17l-5-5\"\n stroke=\"currentColor\"\n strokeWidth=\"2.2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n ) : null}\n </div>\n );\n })\n )}\n </div>\n ) : null}\n {hint ? <span style={{ fontSize: 12, color: 'var(--dt-muted)' }}>{hint}</span> : null}\n </div>\n );\n}\n"]}