@jho951/ui-components 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 jho951
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # @jho951/ui-components
2
+
3
+ React UI component library.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm i @jho951/ui-components
9
+ # or
10
+ pnpm add @jho951/ui-components
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```tsx
16
+ import { Modal, BaseButton } from '@jho951/ui-components';
17
+
18
+ export function Example() {
19
+ return (
20
+ <Modal isOpen title="Hello">
21
+ <BaseButton>OK</BaseButton>
22
+ </Modal>
23
+ );
24
+ }
25
+ ```
26
+
27
+ ## Styles (CSS)
28
+
29
+ ```ts
30
+ import '@jho951/ui-components/theme.css';
31
+ import '@jho951/ui-components/reset.css';
32
+ import '@jho951/ui-components/utils.css';
33
+ ```
34
+
35
+ ## License
36
+
37
+ MIT
@@ -0,0 +1,11 @@
1
+ <svg
2
+ xmlns="http://www.w3.org/2000/svg"
3
+ viewBox="0 0 24 24"
4
+ fill="none"
5
+ stroke="currentColor"
6
+ stroke-width="2"
7
+ stroke-linecap="round"
8
+ stroke-linejoin="round"
9
+ >
10
+ <polyline points="6 9 12 15 18 9" />
11
+ </svg>
@@ -0,0 +1,3 @@
1
+ declare const SVG_ASSETS: Record<string, any>;
2
+
3
+ export { SVG_ASSETS };
@@ -0,0 +1,7 @@
1
+ import "../chunk-DZ3NPY6X.js";
2
+ import {
3
+ SVG_ASSETS
4
+ } from "../chunk-PLGHTHAJ.js";
5
+ export {
6
+ SVG_ASSETS
7
+ };
File without changes
@@ -0,0 +1,23 @@
1
+ // assert/svg/arrow.svg
2
+ var arrow_default = "./arrow-2URCOGMX.svg";
3
+
4
+ // assert/svg/close.svg
5
+ var close_default = "./close-JPE5H3ZO.svg";
6
+
7
+ // assert/svg/spinner.svg
8
+ var spinner_default = "./spinner-GIDMICZO.svg";
9
+
10
+ // assert/svg/required.svg
11
+ var required_default = "./required-TPNMLA3A.svg";
12
+
13
+ // assert/svg/index.ts
14
+ var SVG_ASSETS = {
15
+ arrow: arrow_default,
16
+ close: close_default,
17
+ spinner: spinner_default,
18
+ required: required_default
19
+ };
20
+
21
+ export {
22
+ SVG_ASSETS
23
+ };
@@ -0,0 +1,81 @@
1
+ // lib/deep-freeze.ts
2
+ function deepFreeze(obj, seen = /* @__PURE__ */ new WeakSet()) {
3
+ if (obj === null || typeof obj !== "object" || seen.has(obj)) {
4
+ return obj;
5
+ }
6
+ seen.add(obj);
7
+ const keys = [...Object.getOwnPropertyNames(obj), ...Object.getOwnPropertySymbols(obj)];
8
+ for (const k of keys) {
9
+ const v = obj[k];
10
+ if (v && typeof v === "object") {
11
+ deepFreeze(v, seen);
12
+ }
13
+ }
14
+ return Object.freeze(obj);
15
+ }
16
+
17
+ // lib/cn.ts
18
+ function cn(...inputs) {
19
+ const frozenInputs = deepFreeze(inputs);
20
+ const classList = frozenInputs.filter((item) => typeof item === "string" && item.trim() !== "");
21
+ return classList.join(" ");
22
+ }
23
+
24
+ // lib/dom.ts
25
+ function ensurePortalRoot(id) {
26
+ if (typeof document === "undefined") {
27
+ return {};
28
+ }
29
+ let el = document.getElementById(id);
30
+ if (!el) {
31
+ el = document.createElement("div");
32
+ el.id = id;
33
+ document.body.appendChild(el);
34
+ }
35
+ return el;
36
+ }
37
+
38
+ // lib/id.ts
39
+ function generateId() {
40
+ const timestamp = Date.now().toString(36);
41
+ const randomPart = Math.random().toString(36).slice(2, 9);
42
+ if (typeof window !== "undefined" && window.crypto?.randomUUID) {
43
+ return window.crypto.randomUUID();
44
+ }
45
+ if (typeof window !== "undefined" && window.crypto?.getRandomValues) {
46
+ return ("10000000-1000-4000-8000" + -1e11).replace(
47
+ /[018]/g,
48
+ (c) => (c ^ window.crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
49
+ );
50
+ }
51
+ return `${timestamp}-${randomPart}`;
52
+ }
53
+
54
+ // lib/os.ts
55
+ function getUAData() {
56
+ const nav = navigator;
57
+ const uad = nav.userAgentData;
58
+ if (!uad || typeof uad !== "object") return null;
59
+ return uad;
60
+ }
61
+ function isMac() {
62
+ if (typeof window === "undefined") return false;
63
+ const uaData = getUAData();
64
+ const platforms = uaData?.platforms;
65
+ if (Array.isArray(platforms)) {
66
+ const hit = platforms.some((p2) => /mac|ios|iphone|ipad|ipados/i.test(p2.platform));
67
+ if (hit) return true;
68
+ }
69
+ const p = (navigator.platform || "").toLowerCase();
70
+ const ua = (navigator.userAgent || "").toLowerCase();
71
+ return /mac|iphone|ipad|ipod/.test(p) || /mac os x|iphone|ipad|ipod/.test(ua);
72
+ }
73
+ var MOD = isMac() ? "Meta" : "Control";
74
+
75
+ export {
76
+ deepFreeze,
77
+ cn,
78
+ ensurePortalRoot,
79
+ generateId,
80
+ MOD
81
+ };
@@ -0,0 +1,493 @@
1
+ import {
2
+ SVG_ASSETS
3
+ } from "./chunk-PLGHTHAJ.js";
4
+ import {
5
+ useScrollLock
6
+ } from "./chunk-VRIA2QTM.js";
7
+ import {
8
+ cn,
9
+ ensurePortalRoot,
10
+ generateId
11
+ } from "./chunk-Q55QXUNN.js";
12
+
13
+ // ui/icon/Icon.tsx
14
+ import React2 from "react";
15
+
16
+ // ui/icon/Icon.module.css
17
+ var Icon_default = {};
18
+
19
+ // ui/icon/Icon.util.ts
20
+ import React from "react";
21
+ var svgCache = /* @__PURE__ */ new Map();
22
+ function getRegistryIcon(name, registry) {
23
+ if (!registry) return void 0;
24
+ return registry[name];
25
+ }
26
+ function isExternalSvgPath(p) {
27
+ return /^https?:\/\//.test(p) || p.startsWith("/");
28
+ }
29
+ function resolveIconSrc(name, src, basePath = "/assert/icons", ext = "svg") {
30
+ if (src) return src;
31
+ if (isExternalSvgPath(name)) return name;
32
+ return `${basePath}/${name}.${ext}`;
33
+ }
34
+ function getAriaProps(title) {
35
+ return title ? { role: "img", "aria-label": title } : { "aria-hidden": true };
36
+ }
37
+ function useInlineSvg(src) {
38
+ const [svgText, setSvgText] = React.useState(null);
39
+ React.useEffect(() => {
40
+ let alive = true;
41
+ if (!src) {
42
+ setSvgText(null);
43
+ return;
44
+ }
45
+ const cached = svgCache.get(src);
46
+ if (cached) {
47
+ setSvgText(cached);
48
+ return;
49
+ }
50
+ fetch(src).then((r) => r.ok ? r.text() : Promise.reject()).then((txt) => {
51
+ if (!alive) return;
52
+ svgCache.set(src, txt);
53
+ setSvgText(txt);
54
+ }).catch(() => {
55
+ if (!alive) return;
56
+ setSvgText(null);
57
+ });
58
+ return () => {
59
+ alive = false;
60
+ };
61
+ }, [src]);
62
+ return svgText;
63
+ }
64
+ function extractSvgInner(svgText) {
65
+ const m = svgText.match(/<svg[^>]*>([\s\S]*?)<\/svg>/i);
66
+ return m ? m[1] : svgText;
67
+ }
68
+ function extractViewBox(svgText, fallback = "0 0 24 24") {
69
+ const m = svgText.match(/viewBox="([^"]+)"/i);
70
+ return m ? m[1] : fallback;
71
+ }
72
+
73
+ // ui/icon/Icon.tsx
74
+ import { jsx, jsxs } from "react/jsx-runtime";
75
+ var DEFAULT_ICONS = Object.entries(SVG_ASSETS).reduce(
76
+ (acc, [name, content]) => {
77
+ const isString = typeof content === "string";
78
+ const isSvgContent = isString && content.includes("<svg");
79
+ acc[name] = {
80
+ vb: isSvgContent ? extractViewBox(content) : "0 0 24 24",
81
+ raw: isSvgContent ? extractSvgInner(content) : void 0,
82
+ src: !isSvgContent ? content : void 0
83
+ // 문자열이 아니면 경로로 간주
84
+ };
85
+ return acc;
86
+ },
87
+ {}
88
+ );
89
+ var Icon = ({
90
+ name,
91
+ size = 24,
92
+ title,
93
+ color,
94
+ source = "auto",
95
+ src,
96
+ basePath = "/assert/svg",
97
+ ext = "svg",
98
+ icons,
99
+ className,
100
+ style,
101
+ ...rest
102
+ }) => {
103
+ const registry = icons ?? DEFAULT_ICONS;
104
+ const regData = getRegistryIcon(String(name), registry);
105
+ const shouldUseRegistry = source === "registry" || source === "auto" && !!regData;
106
+ const ariaProps = getAriaProps(title);
107
+ const svgCommonProps = {
108
+ width: size,
109
+ height: size,
110
+ focusable: "false",
111
+ style: { color, ...style },
112
+ ...ariaProps,
113
+ ...rest
114
+ };
115
+ if (shouldUseRegistry && regData) {
116
+ if (regData.raw || regData.g) {
117
+ return /* @__PURE__ */ jsxs(
118
+ "svg",
119
+ {
120
+ viewBox: regData.vb,
121
+ className: cn(Icon_default.icon, Icon_default.registry, className),
122
+ ...svgCommonProps,
123
+ children: [
124
+ regData.g?.map(
125
+ ({ el, ...attrs }, i) => React2.createElement(el, { key: i, ...attrs })
126
+ ),
127
+ regData.raw && /* @__PURE__ */ jsx("g", { dangerouslySetInnerHTML: { __html: regData.raw } })
128
+ ]
129
+ }
130
+ );
131
+ }
132
+ }
133
+ const finalSrc = regData?.src || resolveIconSrc(String(name), src, basePath, ext);
134
+ const svgText = useInlineSvg(finalSrc);
135
+ if (!svgText) {
136
+ return /* @__PURE__ */ jsx(
137
+ "span",
138
+ {
139
+ className: cn(Icon_default.icon, Icon_default.placeholder, className),
140
+ style: { width: size, height: size, ...style },
141
+ "aria-hidden": true
142
+ }
143
+ );
144
+ }
145
+ return /* @__PURE__ */ jsx(
146
+ "svg",
147
+ {
148
+ viewBox: extractViewBox(svgText),
149
+ className: cn(Icon_default.icon, Icon_default.remote, className),
150
+ ...svgCommonProps,
151
+ dangerouslySetInnerHTML: { __html: extractSvgInner(svgText) }
152
+ }
153
+ );
154
+ };
155
+
156
+ // ui/arrow/Arrow.constant.ts
157
+ var DIRECTION_MAP = {
158
+ down: "0deg",
159
+ right: "90deg",
160
+ up: "180deg",
161
+ left: "-90deg"
162
+ };
163
+
164
+ // ui/arrow/Arrow.module.css
165
+ var Arrow_default = {};
166
+
167
+ // ui/arrow/Arrow.tsx
168
+ import { jsx as jsx2 } from "react/jsx-runtime";
169
+ var Arrow = ({ size = 24, direction = "down", className }) => {
170
+ return /* @__PURE__ */ jsx2("span", { className: cn(Arrow_default.container, className), style: { "--arrow-rotation": DIRECTION_MAP[direction] }, children: /* @__PURE__ */ jsx2(Icon, { name: "arrow", size }) });
171
+ };
172
+
173
+ // ui/backdrop/BackDrop.tsx
174
+ import { useEffect, useState } from "react";
175
+
176
+ // ui/backdrop/BackDrop.module.css
177
+ var BackDrop_default = {};
178
+
179
+ // ui/backdrop/BackDrop.tsx
180
+ import { jsx as jsx3 } from "react/jsx-runtime";
181
+ var BackDrop = ({
182
+ visible = false,
183
+ onClick,
184
+ className,
185
+ variant = "blur"
186
+ }) => {
187
+ const [shouldRender, setRender] = useState(visible);
188
+ useEffect(() => {
189
+ if (visible) {
190
+ setRender(true);
191
+ document.body.style.overflow = "hidden";
192
+ } else {
193
+ const timer = setTimeout(() => {
194
+ setRender(false);
195
+ document.body.style.overflow = "auto";
196
+ }, 300);
197
+ return () => {
198
+ clearTimeout(timer);
199
+ document.body.style.overflow = "auto";
200
+ };
201
+ }
202
+ }, [visible]);
203
+ if (!shouldRender) return null;
204
+ return /* @__PURE__ */ jsx3(
205
+ "div",
206
+ {
207
+ className: cn(
208
+ BackDrop_default.backdrop,
209
+ BackDrop_default[variant],
210
+ visible && BackDrop_default.isActive,
211
+ className
212
+ ),
213
+ onClick,
214
+ "aria-hidden": !visible
215
+ }
216
+ );
217
+ };
218
+
219
+ // ui/button/Button.tsx
220
+ import { forwardRef } from "react";
221
+
222
+ // ui/spinner/Spinner.module.css
223
+ var Spinner_default = {};
224
+
225
+ // ui/spinner/Spinner.tsx
226
+ import { jsx as jsx4 } from "react/jsx-runtime";
227
+ var Spinner = ({ size = 24, className, label = "Loading" }) => {
228
+ return /* @__PURE__ */ jsx4("span", { className: cn(Spinner_default.spinner, className), role: "status", "aria-label": label, "aria-live": "polite", children: /* @__PURE__ */ jsx4(Icon, { name: "spinner", size }) });
229
+ };
230
+
231
+ // ui/button/Button.module.css
232
+ var Button_default = {};
233
+
234
+ // ui/button/Button.tsx
235
+ import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
236
+ var BaseButton = forwardRef(
237
+ ({ className, children, variant = "primary", size = "m", disabled = false, ...rest }, ref) => {
238
+ return /* @__PURE__ */ jsx5(
239
+ "button",
240
+ {
241
+ className: cn(Button_default.button, Button_default[variant], Button_default[size], disabled && Button_default.disabled, className),
242
+ disabled,
243
+ ...rest,
244
+ ref,
245
+ children: /* @__PURE__ */ jsx5("span", { className: Button_default.label, children })
246
+ }
247
+ );
248
+ }
249
+ );
250
+ BaseButton.displayName = "BaseButton";
251
+ var Button = forwardRef(
252
+ ({ children, isLoading, leftIcon, rightIcon, ...rest }, ref) => {
253
+ if (isLoading) return /* @__PURE__ */ jsx5(Spinner, {});
254
+ return /* @__PURE__ */ jsxs2(BaseButton, { ref, ...rest, children: [
255
+ leftIcon && /* @__PURE__ */ jsx5("span", { className: Button_default.icon, children: leftIcon }),
256
+ children,
257
+ rightIcon && /* @__PURE__ */ jsx5("span", { className: Button_default.icon, children: rightIcon })
258
+ ] });
259
+ }
260
+ );
261
+ Button.displayName = "Button";
262
+
263
+ // ui/button/Button.constant.ts
264
+ var BUTTON_SIZES = ["s", "m", "l"];
265
+ var BUTTON_VARIANTS = ["primary", "secondary", "ghost", "text"];
266
+
267
+ // ui/card/Card.module.css
268
+ var Card_default = {};
269
+
270
+ // ui/card/Card.utils.tsx
271
+ import { jsx as jsx6 } from "react/jsx-runtime";
272
+ var createCardSection = (name) => {
273
+ const Section = ({ className, children, ...rest }) => {
274
+ return /* @__PURE__ */ jsx6("div", { className: cn(Card_default[name.toLowerCase()], className), ...rest, children });
275
+ };
276
+ Section.displayName = `Card.${name}`;
277
+ return Section;
278
+ };
279
+
280
+ // ui/card/Card.tsx
281
+ import { forwardRef as forwardRef2 } from "react";
282
+ import { jsx as jsx7 } from "react/jsx-runtime";
283
+ var CardRoot = forwardRef2(
284
+ ({ children, className, ...rest }, ref) => {
285
+ return /* @__PURE__ */ jsx7("section", { className: cn(Card_default.card, className), ...rest, ref, children });
286
+ }
287
+ );
288
+ var Card = CardRoot;
289
+ Card.Header = createCardSection("Header");
290
+ Card.Body = createCardSection("Body");
291
+ Card.Footer = createCardSection("Footer");
292
+ CardRoot.displayName = "Card";
293
+
294
+ // ui/checkbox/CheckBox.tsx
295
+ import { forwardRef as forwardRef3, useEffect as useEffect2, useImperativeHandle, useRef } from "react";
296
+
297
+ // ui/label/Label.module.css
298
+ var Label_default = {};
299
+
300
+ // ui/label/Label.tsx
301
+ import { jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
302
+ var Label = ({ className, children, htmlFor, required = false, variant = "default", ...rest }) => {
303
+ return /* @__PURE__ */ jsxs3("label", { htmlFor, className: cn(Label_default.label, Label_default[variant], className), ...rest, children: [
304
+ children,
305
+ required && /* @__PURE__ */ jsx8(Icon, { className: Label_default.required, name: "required" })
306
+ ] });
307
+ };
308
+
309
+ // ui/checkbox/CheckBox.module.css
310
+ var CheckBox_default = {};
311
+
312
+ // ui/checkbox/CheckBox.tsx
313
+ import { jsx as jsx9, jsxs as jsxs4 } from "react/jsx-runtime";
314
+ var Checkbox = forwardRef3(
315
+ ({ label, error, indeterminate, className, ...rest }, ref) => {
316
+ const id = rest.id || generateId();
317
+ const inputRef = useRef(null);
318
+ useImperativeHandle(ref, () => inputRef.current);
319
+ useEffect2(() => {
320
+ if (inputRef.current) inputRef.current.indeterminate = !!indeterminate;
321
+ }, [indeterminate]);
322
+ return /* @__PURE__ */ jsxs4(Label, { htmlFor: id, className: cn(CheckBox_default.container, rest.disabled && CheckBox_default.disabled, className), children: [
323
+ /* @__PURE__ */ jsxs4("div", { className: CheckBox_default.wrapper, children: [
324
+ /* @__PURE__ */ jsx9("input", { id, className: "sr-only", type: "checkbox", ref: inputRef, ...rest }),
325
+ /* @__PURE__ */ jsx9("div", { className: cn(CheckBox_default.styledBox, error && CheckBox_default.error, rest.checked && CheckBox_default.checked, indeterminate && CheckBox_default.indeterminate), children: (rest.checked || indeterminate) && /* @__PURE__ */ jsx9(Icon, { name: indeterminate ? "indeterminate" : "check" }) })
326
+ ] }),
327
+ label && /* @__PURE__ */ jsx9("span", { className: CheckBox_default.labelText, children: label })
328
+ ] });
329
+ }
330
+ );
331
+ Checkbox.displayName = "Checkbox";
332
+
333
+ // ui/divider/Divider.module.css
334
+ var Divider_default = {};
335
+
336
+ // ui/divider/Divider.tsx
337
+ import { jsx as jsx10 } from "react/jsx-runtime";
338
+ var Divider = () => {
339
+ return /* @__PURE__ */ jsx10("section", { className: Divider_default.divider });
340
+ };
341
+
342
+ // ui/form/Form.module.css
343
+ var Form_default = {};
344
+
345
+ // ui/form/Form.tsx
346
+ import { jsx as jsx11 } from "react/jsx-runtime";
347
+ var Form = ({ children, onSubmit, className, ...rest }) => {
348
+ return /* @__PURE__ */ jsx11("form", { onSubmit, className: cn(Form_default.container, className), noValidate: true, ...rest, children });
349
+ };
350
+
351
+ // ui/modal/Modal.tsx
352
+ import { useCallback, useEffect as useEffect3, useRef as useRef2, useState as useState2 } from "react";
353
+ import { createPortal } from "react-dom";
354
+
355
+ // ui/modal/Modal.module.css
356
+ var Modal_default = {};
357
+
358
+ // ui/modal/Modal.tsx
359
+ import { jsx as jsx12, jsxs as jsxs5 } from "react/jsx-runtime";
360
+ var Modal = ({ isOpen, content, onClose, title = "\uBBF8\uB9AC\uBCF4\uAE30", size = "medium" }) => {
361
+ const [mounted, setMounted] = useState2(false);
362
+ const [visible, setVisible] = useState2(false);
363
+ const [portalElement, setPortalElement] = useState2(null);
364
+ const modalRef = useRef2(null);
365
+ useEffect3(() => {
366
+ if (!isOpen) {
367
+ setVisible(false);
368
+ const t = window.setTimeout(() => {
369
+ setMounted(false);
370
+ }, 300);
371
+ return () => window.clearTimeout(t);
372
+ }
373
+ setMounted(true);
374
+ setPortalElement(ensurePortalRoot("modal-root"));
375
+ const raf = requestAnimationFrame(() => setVisible(true));
376
+ return () => cancelAnimationFrame(raf);
377
+ }, [isOpen]);
378
+ useScrollLock(mounted);
379
+ const handleClose = useCallback(() => {
380
+ setVisible(false);
381
+ setTimeout(onClose, 300);
382
+ }, [onClose]);
383
+ useEffect3(() => {
384
+ if (!visible) return;
385
+ const handleKeyDown = (e) => {
386
+ if (e.key === "Escape") handleClose();
387
+ };
388
+ window.addEventListener("keydown", handleKeyDown);
389
+ modalRef.current?.focus();
390
+ return () => window.removeEventListener("keydown", handleKeyDown);
391
+ }, [visible, handleClose]);
392
+ if (!mounted || !portalElement) return null;
393
+ return createPortal(
394
+ /* @__PURE__ */ jsxs5("div", { className: Modal_default.container, role: "presentation", children: [
395
+ /* @__PURE__ */ jsx12(BackDrop, { className: Modal_default.overlay, visible, onClick: handleClose }),
396
+ /* @__PURE__ */ jsxs5(
397
+ "div",
398
+ {
399
+ className: cn(Modal_default.modal, Modal_default[size], visible && Modal_default.show),
400
+ ref: modalRef,
401
+ tabIndex: -1,
402
+ role: "dialog",
403
+ "aria-modal": "true",
404
+ "aria-labelledby": "modal-title",
405
+ onClick: (e) => e.stopPropagation(),
406
+ children: [
407
+ /* @__PURE__ */ jsxs5("section", { className: Modal_default.header, children: [
408
+ /* @__PURE__ */ jsx12("h2", { id: "modal-title", className: Modal_default.title, children: title }),
409
+ /* @__PURE__ */ jsx12(
410
+ "button",
411
+ {
412
+ onClick: handleClose,
413
+ className: Modal_default.closeBtn,
414
+ "aria-label": "\uBAA8\uB2EC \uB2EB\uAE30",
415
+ children: /* @__PURE__ */ jsx12("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx12("path", { d: "M18 6L6 18M6 6l12 12" }) })
416
+ }
417
+ )
418
+ ] }),
419
+ /* @__PURE__ */ jsx12("article", { className: Modal_default.content, children: /* @__PURE__ */ jsx12("div", { dangerouslySetInnerHTML: { __html: content } }) })
420
+ ]
421
+ }
422
+ )
423
+ ] }),
424
+ portalElement
425
+ );
426
+ };
427
+
428
+ // ui/tag/Tag.module.css
429
+ var Tag_default = {};
430
+
431
+ // ui/tag/Tag.tsx
432
+ import { jsx as jsx13 } from "react/jsx-runtime";
433
+ var Tag = ({ active, color = "default", children }) => {
434
+ return /* @__PURE__ */ jsx13("span", { className: cn(Tag_default.tag, Tag_default[color], active && Tag_default.active), children });
435
+ };
436
+
437
+ // ui/textarea/Textarea.tsx
438
+ import { forwardRef as forwardRef4, useEffect as useEffect4, useImperativeHandle as useImperativeHandle2, useRef as useRef3 } from "react";
439
+
440
+ // ui/textarea/Textarea.module.css
441
+ var Textarea_default = {};
442
+
443
+ // ui/textarea/Textarea.tsx
444
+ import { jsx as jsx14 } from "react/jsx-runtime";
445
+ var Textarea = forwardRef4(
446
+ ({ autoResize = true, className, value, ...rest }, ref) => {
447
+ const innerRef = useRef3(null);
448
+ useImperativeHandle2(ref, () => innerRef.current, []);
449
+ useEffect4(() => {
450
+ if (!autoResize || !innerRef.current) return;
451
+ const el = innerRef.current;
452
+ el.style.height = "auto";
453
+ el.style.height = `${el.scrollHeight}px`;
454
+ }, [value, autoResize]);
455
+ return /* @__PURE__ */ jsx14(
456
+ "textarea",
457
+ {
458
+ className: cn(Textarea_default.textarea, autoResize && Textarea_default.autoResize, className),
459
+ ref: innerRef,
460
+ value,
461
+ ...rest
462
+ }
463
+ );
464
+ }
465
+ );
466
+ Textarea.displayName = "Textarea";
467
+
468
+ export {
469
+ getRegistryIcon,
470
+ isExternalSvgPath,
471
+ resolveIconSrc,
472
+ getAriaProps,
473
+ useInlineSvg,
474
+ extractSvgInner,
475
+ extractViewBox,
476
+ Icon,
477
+ DIRECTION_MAP,
478
+ Arrow,
479
+ BackDrop,
480
+ Spinner,
481
+ Button,
482
+ BUTTON_SIZES,
483
+ BUTTON_VARIANTS,
484
+ createCardSection,
485
+ Card,
486
+ Label,
487
+ Checkbox,
488
+ Divider,
489
+ Form,
490
+ Modal,
491
+ Tag,
492
+ Textarea
493
+ };