@hua-labs/ui 1.0.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 (153) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +363 -0
  3. package/dist/components/Accordion.d.ts +39 -0
  4. package/dist/components/Accordion.d.ts.map +1 -0
  5. package/dist/components/Accordion.js +84 -0
  6. package/dist/components/Alert.d.ts +17 -0
  7. package/dist/components/Alert.d.ts.map +1 -0
  8. package/dist/components/Alert.js +61 -0
  9. package/dist/components/Avatar.d.ts +13 -0
  10. package/dist/components/Avatar.d.ts.map +1 -0
  11. package/dist/components/Avatar.js +18 -0
  12. package/dist/components/Badge.d.ts +7 -0
  13. package/dist/components/Badge.d.ts.map +1 -0
  14. package/dist/components/Badge.js +15 -0
  15. package/dist/components/BottomSheet.d.ts +29 -0
  16. package/dist/components/BottomSheet.d.ts.map +1 -0
  17. package/dist/components/BottomSheet.js +96 -0
  18. package/dist/components/Breadcrumb.d.ts +27 -0
  19. package/dist/components/Breadcrumb.d.ts.map +1 -0
  20. package/dist/components/Breadcrumb.js +47 -0
  21. package/dist/components/Button.d.ts +9 -0
  22. package/dist/components/Button.d.ts.map +1 -0
  23. package/dist/components/Button.js +23 -0
  24. package/dist/components/Card.d.ts +21 -0
  25. package/dist/components/Card.d.ts.map +1 -0
  26. package/dist/components/Card.js +18 -0
  27. package/dist/components/ChatMessage.d.ts +35 -0
  28. package/dist/components/ChatMessage.d.ts.map +1 -0
  29. package/dist/components/ChatMessage.js +59 -0
  30. package/dist/components/Checkbox.d.ts +12 -0
  31. package/dist/components/Checkbox.d.ts.map +1 -0
  32. package/dist/components/Checkbox.js +30 -0
  33. package/dist/components/Command.d.ts +36 -0
  34. package/dist/components/Command.d.ts.map +1 -0
  35. package/dist/components/Command.js +119 -0
  36. package/dist/components/ConfirmModal.d.ts +26 -0
  37. package/dist/components/ConfirmModal.d.ts.map +1 -0
  38. package/dist/components/ConfirmModal.js +53 -0
  39. package/dist/components/Container.d.ts +10 -0
  40. package/dist/components/Container.d.ts.map +1 -0
  41. package/dist/components/Container.js +23 -0
  42. package/dist/components/ContextMenu.d.ts +26 -0
  43. package/dist/components/ContextMenu.d.ts.map +1 -0
  44. package/dist/components/ContextMenu.js +110 -0
  45. package/dist/components/Divider.d.ts +11 -0
  46. package/dist/components/Divider.d.ts.map +1 -0
  47. package/dist/components/Divider.js +39 -0
  48. package/dist/components/Drawer.d.ts +32 -0
  49. package/dist/components/Drawer.d.ts.map +1 -0
  50. package/dist/components/Drawer.js +79 -0
  51. package/dist/components/Dropdown.d.ts +28 -0
  52. package/dist/components/Dropdown.d.ts.map +1 -0
  53. package/dist/components/Dropdown.js +174 -0
  54. package/dist/components/EmotionAnalysis.d.ts +25 -0
  55. package/dist/components/EmotionAnalysis.d.ts.map +1 -0
  56. package/dist/components/EmotionAnalysis.js +40 -0
  57. package/dist/components/EmotionButton.d.ts +9 -0
  58. package/dist/components/EmotionButton.d.ts.map +1 -0
  59. package/dist/components/EmotionButton.js +16 -0
  60. package/dist/components/EmotionMeter.d.ts +10 -0
  61. package/dist/components/EmotionMeter.d.ts.map +1 -0
  62. package/dist/components/EmotionMeter.js +21 -0
  63. package/dist/components/EmotionSelector.d.ts +20 -0
  64. package/dist/components/EmotionSelector.d.ts.map +1 -0
  65. package/dist/components/EmotionSelector.js +46 -0
  66. package/dist/components/Grid.d.ts +11 -0
  67. package/dist/components/Grid.d.ts.map +1 -0
  68. package/dist/components/Grid.js +44 -0
  69. package/dist/components/Icon.d.ts +26 -0
  70. package/dist/components/Icon.d.ts.map +1 -0
  71. package/dist/components/Icon.js +48 -0
  72. package/dist/components/Input.d.ts +12 -0
  73. package/dist/components/Input.d.ts.map +1 -0
  74. package/dist/components/Input.js +25 -0
  75. package/dist/components/LanguageToggle.d.ts +17 -0
  76. package/dist/components/LanguageToggle.d.ts.map +1 -0
  77. package/dist/components/LanguageToggle.js +61 -0
  78. package/dist/components/LoadingSpinner.d.ts +10 -0
  79. package/dist/components/LoadingSpinner.d.ts.map +1 -0
  80. package/dist/components/LoadingSpinner.js +37 -0
  81. package/dist/components/Menu.d.ts +29 -0
  82. package/dist/components/Menu.d.ts.map +1 -0
  83. package/dist/components/Menu.js +122 -0
  84. package/dist/components/Modal.d.ts +15 -0
  85. package/dist/components/Modal.d.ts.map +1 -0
  86. package/dist/components/Modal.js +62 -0
  87. package/dist/components/PageTransition.d.ts +18 -0
  88. package/dist/components/PageTransition.d.ts.map +1 -0
  89. package/dist/components/PageTransition.js +39 -0
  90. package/dist/components/Pagination.d.ts +21 -0
  91. package/dist/components/Pagination.d.ts.map +1 -0
  92. package/dist/components/Pagination.js +87 -0
  93. package/dist/components/Popover.d.ts +16 -0
  94. package/dist/components/Popover.d.ts.map +1 -0
  95. package/dist/components/Popover.js +159 -0
  96. package/dist/components/Progress.d.ts +23 -0
  97. package/dist/components/Progress.d.ts.map +1 -0
  98. package/dist/components/Progress.js +51 -0
  99. package/dist/components/Radio.d.ts +12 -0
  100. package/dist/components/Radio.d.ts.map +1 -0
  101. package/dist/components/Radio.js +29 -0
  102. package/dist/components/ScrollArea.d.ts +16 -0
  103. package/dist/components/ScrollArea.d.ts.map +1 -0
  104. package/dist/components/ScrollArea.js +42 -0
  105. package/dist/components/ScrollIndicator.d.ts +17 -0
  106. package/dist/components/ScrollIndicator.d.ts.map +1 -0
  107. package/dist/components/ScrollIndicator.js +60 -0
  108. package/dist/components/ScrollProgress.d.ts +12 -0
  109. package/dist/components/ScrollProgress.d.ts.map +1 -0
  110. package/dist/components/ScrollProgress.js +39 -0
  111. package/dist/components/ScrollToTop.d.ts +15 -0
  112. package/dist/components/ScrollToTop.d.ts.map +1 -0
  113. package/dist/components/ScrollToTop.js +46 -0
  114. package/dist/components/Select.d.ts +17 -0
  115. package/dist/components/Select.d.ts.map +1 -0
  116. package/dist/components/Select.js +29 -0
  117. package/dist/components/Skeleton.d.ts +19 -0
  118. package/dist/components/Skeleton.d.ts.map +1 -0
  119. package/dist/components/Skeleton.js +71 -0
  120. package/dist/components/Stack.d.ts +11 -0
  121. package/dist/components/Stack.d.ts.map +1 -0
  122. package/dist/components/Stack.js +34 -0
  123. package/dist/components/Switch.d.ts +12 -0
  124. package/dist/components/Switch.d.ts.map +1 -0
  125. package/dist/components/Switch.js +29 -0
  126. package/dist/components/Tabs.d.ts +36 -0
  127. package/dist/components/Tabs.d.ts.map +1 -0
  128. package/dist/components/Tabs.js +117 -0
  129. package/dist/components/Textarea.d.ts +11 -0
  130. package/dist/components/Textarea.d.ts.map +1 -0
  131. package/dist/components/Textarea.js +31 -0
  132. package/dist/components/ThemeProvider.d.ts +19 -0
  133. package/dist/components/ThemeProvider.d.ts.map +1 -0
  134. package/dist/components/ThemeProvider.js +76 -0
  135. package/dist/components/ThemeToggle.d.ts +14 -0
  136. package/dist/components/ThemeToggle.d.ts.map +1 -0
  137. package/dist/components/ThemeToggle.js +49 -0
  138. package/dist/components/Toast.d.ts +32 -0
  139. package/dist/components/Toast.d.ts.map +1 -0
  140. package/dist/components/Toast.js +138 -0
  141. package/dist/components/Tooltip.d.ts +14 -0
  142. package/dist/components/Tooltip.d.ts.map +1 -0
  143. package/dist/components/Tooltip.js +102 -0
  144. package/dist/index.d.ts +50 -0
  145. package/dist/index.d.ts.map +1 -0
  146. package/dist/index.js +49 -0
  147. package/dist/lib/icons.d.ts +43 -0
  148. package/dist/lib/icons.d.ts.map +1 -0
  149. package/dist/lib/icons.js +321 -0
  150. package/dist/lib/utils.d.ts +3 -0
  151. package/dist/lib/utils.d.ts.map +1 -0
  152. package/dist/lib/utils.js +5 -0
  153. package/package.json +67 -0
@@ -0,0 +1,61 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "../lib/utils";
5
+ const LanguageToggle = React.forwardRef(({ className, size = "md", variant = "button", showLabel = false, languages = [
6
+ { code: "ko", name: "한국어", flag: "🇰🇷" },
7
+ { code: "en", name: "English", flag: "🇺🇸" },
8
+ { code: "ja", name: "日本語", flag: "🇯🇵" },
9
+ { code: "zh", name: "中文", flag: "🇨🇳" }
10
+ ], currentLanguage = "ko", onLanguageChange, ...props }, ref) => {
11
+ const [isOpen, setIsOpen] = React.useState(false);
12
+ const dropdownRef = React.useRef(null);
13
+ const currentLang = languages.find(lang => lang.code === currentLanguage) || languages[0];
14
+ const sizeClasses = {
15
+ sm: "h-10 w-10", // 40px - 더 넉넉한 크기
16
+ md: "h-12 w-12", // 48px - 더 넉넉한 크기
17
+ lg: "h-14 w-14" // 56px - 더 넉넉한 크기
18
+ };
19
+ const iconSizes = {
20
+ sm: 16,
21
+ md: 20,
22
+ lg: 24
23
+ };
24
+ // 외부 클릭 시 드롭다운 닫기
25
+ React.useEffect(() => {
26
+ const handleClickOutside = (event) => {
27
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
28
+ setIsOpen(false);
29
+ }
30
+ };
31
+ if (isOpen) {
32
+ document.addEventListener("mousedown", handleClickOutside);
33
+ }
34
+ return () => {
35
+ document.removeEventListener("mousedown", handleClickOutside);
36
+ };
37
+ }, [isOpen]);
38
+ const handleLanguageChange = (languageCode) => {
39
+ onLanguageChange?.(languageCode);
40
+ setIsOpen(false);
41
+ };
42
+ const renderIcon = () => (_jsx("div", { className: "flex items-center justify-center", children: _jsx("span", { className: "text-lg", children: currentLang.flag }) }));
43
+ if (variant === "icon") {
44
+ return (_jsxs("div", { ref: dropdownRef, className: "relative", children: [_jsx("button", { onClick: () => setIsOpen(!isOpen), className: cn("inline-flex items-center justify-center rounded-lg transition-all duration-300 hover:bg-gray-100 dark:hover:bg-gray-800 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/50 focus-visible:ring-offset-2", sizeClasses[size], className), ...props, children: renderIcon() }), isOpen && (_jsx("div", { className: "absolute top-full right-0 mt-2 w-48 bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 py-2 z-50", children: languages.map((language) => (_jsxs("button", { onClick: () => handleLanguageChange(language.code), className: cn("w-full px-4 py-3 text-left hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors duration-200 flex items-center gap-3", // 16px, 12px 패딩, 12px 간격
45
+ currentLanguage === language.code && "bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400"), children: [_jsx("span", { className: "text-lg", children: language.flag }), _jsx("span", { className: "text-sm font-medium", children: language.name })] }, language.code))) }))] }));
46
+ }
47
+ if (variant === "dropdown") {
48
+ return (_jsxs("div", { ref: dropdownRef, className: "relative", children: [_jsxs("button", { onClick: () => setIsOpen(!isOpen), className: cn("inline-flex items-center gap-3 rounded-lg px-4 py-3 text-sm font-medium transition-all duration-300 hover:bg-gray-100 dark:hover:bg-gray-800 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/50 focus-visible:ring-offset-2", // 12px 간격, 16px, 12px 패딩
49
+ className), ...props, children: [_jsx("span", { className: "text-lg", children: currentLang.flag }), showLabel && _jsx("span", { children: currentLang.name }), _jsx("svg", { className: cn("w-4 h-4 transition-transform duration-200", isOpen && "rotate-180"), fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), isOpen && (_jsx("div", { className: "absolute top-full right-0 mt-2 w-48 bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 py-2 z-50", children: languages.map((language) => (_jsxs("button", { onClick: () => handleLanguageChange(language.code), className: cn("w-full px-4 py-3 text-left hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors duration-200 flex items-center gap-3", // 16px, 12px 패딩, 12px 간격
50
+ currentLanguage === language.code && "bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400"), children: [_jsx("span", { className: "text-lg", children: language.flag }), _jsx("span", { className: "text-sm font-medium", children: language.name })] }, language.code))) }))] }));
51
+ }
52
+ // 기본 버튼 형태
53
+ return (_jsxs("button", { onClick: () => {
54
+ const currentIndex = languages.findIndex(lang => lang.code === currentLanguage);
55
+ const nextIndex = (currentIndex + 1) % languages.length;
56
+ onLanguageChange?.(languages[nextIndex].code);
57
+ }, className: cn("inline-flex items-center gap-3 rounded-lg px-4 py-3 text-sm font-medium transition-all duration-300 hover:bg-gray-100 dark:hover:bg-gray-800 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/50 focus-visible:ring-offset-2", // 12px 간격, 16px, 12px 패딩
58
+ className), ...props, children: [_jsx("span", { className: "text-lg", children: currentLang.flag }), showLabel && _jsx("span", { children: currentLang.name })] }));
59
+ });
60
+ LanguageToggle.displayName = "LanguageToggle";
61
+ export { LanguageToggle };
@@ -0,0 +1,10 @@
1
+ import * as React from "react";
2
+ export interface LoadingSpinnerProps extends React.HTMLAttributes<HTMLDivElement> {
3
+ size?: "sm" | "md" | "lg" | "xl";
4
+ variant?: "default" | "dots" | "bars" | "ring" | "ripple";
5
+ text?: string;
6
+ color?: "default" | "primary" | "secondary" | "success" | "warning" | "error";
7
+ }
8
+ declare const LoadingSpinner: React.ForwardRefExoticComponent<LoadingSpinnerProps & React.RefAttributes<HTMLDivElement>>;
9
+ export { LoadingSpinner };
10
+ //# sourceMappingURL=LoadingSpinner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LoadingSpinner.d.ts","sourceRoot":"","sources":["../../src/components/LoadingSpinner.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,MAAM,WAAW,mBAAoB,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IAC/E,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IAChC,OAAO,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAA;IACzD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAA;CAC9E;AAED,QAAA,MAAM,cAAc,4FAsFnB,CAAA;AAGD,OAAO,EAAE,cAAc,EAAE,CAAA"}
@@ -0,0 +1,37 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "../lib/utils";
5
+ const LoadingSpinner = React.forwardRef(({ className, size = "md", variant = "default", text, color = "default", ...props }, ref) => {
6
+ const sizeClasses = {
7
+ sm: "w-6 h-6", // 24px - 더 넉넉한 크기
8
+ md: "w-8 h-8", // 32px - 더 넉넉한 크기
9
+ lg: "w-12 h-12", // 48px - 더 넉넉한 크기
10
+ xl: "w-16 h-16" // 64px - 더 넉넉한 크기
11
+ };
12
+ const colorClasses = {
13
+ default: "border-gray-300 border-t-gray-600 dark:border-gray-600 dark:border-t-gray-300",
14
+ primary: "border-blue-300 border-t-blue-600 dark:border-blue-600 dark:border-t-blue-300",
15
+ secondary: "border-gray-300 border-t-gray-600 dark:border-gray-600 dark:border-t-gray-300",
16
+ success: "border-green-300 border-t-green-600 dark:border-green-600 dark:border-t-green-300",
17
+ warning: "border-yellow-300 border-t-yellow-600 dark:border-yellow-600 dark:border-t-yellow-300",
18
+ error: "border-red-300 border-t-red-600 dark:border-red-600 dark:border-t-red-300"
19
+ };
20
+ const renderSpinner = () => {
21
+ switch (variant) {
22
+ case "dots":
23
+ return (_jsxs("div", { className: "flex space-x-1", children: [" ", _jsx("div", { className: "w-2 h-2 bg-current rounded-full animate-bounce" }), _jsx("div", { className: "w-2 h-2 bg-current rounded-full animate-bounce delay-100" }), _jsx("div", { className: "w-2 h-2 bg-current rounded-full animate-bounce delay-200" })] }));
24
+ case "bars":
25
+ return (_jsxs("div", { className: "flex space-x-1", children: [" ", _jsx("div", { className: "w-1 h-full bg-current animate-pulse" }), _jsx("div", { className: "w-1 h-full bg-current animate-pulse delay-100" }), _jsx("div", { className: "w-1 h-full bg-current animate-pulse delay-200" })] }));
26
+ case "ring":
27
+ return (_jsx("div", { className: cn("animate-spin rounded-full border-2", colorClasses[color]) }));
28
+ case "ripple":
29
+ return (_jsxs("div", { className: "relative", children: [_jsx("div", { className: cn("absolute inset-0 rounded-full border-2 animate-ping", colorClasses[color]) }), _jsx("div", { className: cn("rounded-full border-2", colorClasses[color]) })] }));
30
+ default:
31
+ return (_jsx("div", { className: cn("animate-spin rounded-full border-2", colorClasses[color]) }));
32
+ }
33
+ };
34
+ return (_jsxs("div", { ref: ref, className: cn("flex flex-col items-center justify-center", className), ...props, children: [_jsx("div", { className: cn(sizeClasses[size], "text-gray-600 dark:text-gray-400"), children: renderSpinner() }), text && (_jsxs("p", { className: "mt-3 text-sm text-gray-600 dark:text-gray-400 text-center", children: [" ", text] }))] }));
35
+ });
36
+ LoadingSpinner.displayName = "LoadingSpinner";
37
+ export { LoadingSpinner };
@@ -0,0 +1,29 @@
1
+ import * as React from "react";
2
+ export interface MenuProps extends React.HTMLAttributes<HTMLDivElement> {
3
+ children: React.ReactNode;
4
+ variant?: "default" | "horizontal" | "vertical" | "compact";
5
+ size?: "sm" | "md" | "lg";
6
+ }
7
+ declare const Menu: React.ForwardRefExoticComponent<MenuProps & React.RefAttributes<HTMLDivElement>>;
8
+ export interface MenuItemProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
9
+ icon?: React.ReactNode;
10
+ variant?: "default" | "horizontal" | "vertical" | "compact";
11
+ size?: "sm" | "md" | "lg";
12
+ active?: boolean;
13
+ disabled?: boolean;
14
+ }
15
+ declare const MenuItem: React.ForwardRefExoticComponent<MenuItemProps & React.RefAttributes<HTMLButtonElement>>;
16
+ export interface MenuSeparatorProps extends React.HTMLAttributes<HTMLDivElement> {
17
+ variant?: "default" | "horizontal" | "vertical" | "compact";
18
+ }
19
+ declare const MenuSeparator: React.ForwardRefExoticComponent<MenuSeparatorProps & React.RefAttributes<HTMLDivElement>>;
20
+ export interface MenuLabelProps extends React.HTMLAttributes<HTMLDivElement> {
21
+ variant?: "default" | "horizontal" | "vertical" | "compact";
22
+ size?: "sm" | "md" | "lg";
23
+ }
24
+ declare const MenuLabel: React.ForwardRefExoticComponent<MenuLabelProps & React.RefAttributes<HTMLDivElement>>;
25
+ export declare const MenuHorizontal: React.ForwardRefExoticComponent<Omit<MenuProps, "variant"> & React.RefAttributes<HTMLDivElement>>;
26
+ export declare const MenuVertical: React.ForwardRefExoticComponent<Omit<MenuProps, "variant"> & React.RefAttributes<HTMLDivElement>>;
27
+ export declare const MenuCompact: React.ForwardRefExoticComponent<Omit<MenuProps, "variant"> & React.RefAttributes<HTMLDivElement>>;
28
+ export { Menu, MenuItem, MenuSeparator, MenuLabel };
29
+ //# sourceMappingURL=Menu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Menu.d.ts","sourceRoot":"","sources":["../../src/components/Menu.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,MAAM,WAAW,SAAU,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IACrE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,OAAO,CAAC,EAAE,SAAS,GAAG,YAAY,GAAG,UAAU,GAAG,SAAS,CAAA;IAC3D,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;CAC1B;AAED,QAAA,MAAM,IAAI,kFAsDT,CAAA;AAGD,MAAM,WAAW,aAAc,SAAQ,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC;IAClF,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACtB,OAAO,CAAC,EAAE,SAAS,GAAG,YAAY,GAAG,UAAU,GAAG,SAAS,CAAA;IAC3D,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IACzB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,QAAA,MAAM,QAAQ,yFA4Eb,CAAA;AAGD,MAAM,WAAW,kBAAmB,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IAC9E,OAAO,CAAC,EAAE,SAAS,GAAG,YAAY,GAAG,UAAU,GAAG,SAAS,CAAA;CAC5D;AAED,QAAA,MAAM,aAAa,2FAqBlB,CAAA;AAGD,MAAM,WAAW,cAAe,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IAC1E,OAAO,CAAC,EAAE,SAAS,GAAG,YAAY,GAAG,UAAU,GAAG,SAAS,CAAA;IAC3D,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;CAC1B;AAED,QAAA,MAAM,SAAS,uFAsCd,CAAA;AAID,eAAO,MAAM,cAAc,mGAI1B,CAAA;AAGD,eAAO,MAAM,YAAY,mGAIxB,CAAA;AAGD,eAAO,MAAM,WAAW,mGAIvB,CAAA;AAGD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,aAAa,EAAE,SAAS,EAAE,CAAA"}
@@ -0,0 +1,122 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "../lib/utils";
5
+ const Menu = React.forwardRef(({ className, children, variant = "default", size = "md", ...props }, ref) => {
6
+ const getVariantClasses = () => {
7
+ switch (variant) {
8
+ case "horizontal":
9
+ return "flex items-center space-x-1"; // 4px 간격
10
+ case "vertical":
11
+ return "flex flex-col space-y-1"; // 4px 간격
12
+ case "compact":
13
+ return "flex flex-col space-y-0.5"; // 2px 간격
14
+ default:
15
+ return "flex flex-col space-y-1"; // 4px 간격
16
+ }
17
+ };
18
+ const getSizeClasses = () => {
19
+ switch (size) {
20
+ case "sm":
21
+ return "text-sm";
22
+ case "lg":
23
+ return "text-base";
24
+ default:
25
+ return "text-sm";
26
+ }
27
+ };
28
+ return (_jsx("div", { ref: ref, className: cn(getVariantClasses(), getSizeClasses(), className), ...props, children: React.Children.map(children, (child) => {
29
+ if (React.isValidElement(child)) {
30
+ return React.cloneElement(child, {
31
+ variant,
32
+ size
33
+ });
34
+ }
35
+ return child;
36
+ }) }));
37
+ });
38
+ Menu.displayName = "Menu";
39
+ const MenuItem = React.forwardRef(({ className, icon, variant = "default", size = "md", active = false, disabled = false, children, ...props }, ref) => {
40
+ const getVariantClasses = () => {
41
+ switch (variant) {
42
+ case "horizontal":
43
+ return cn("flex items-center gap-2 px-3 py-2 rounded-md text-sm font-medium transition-colors", // 12px, 8px 패딩
44
+ active
45
+ ? "bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300"
46
+ : "text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-700");
47
+ case "vertical":
48
+ return cn("flex items-center gap-3 px-4 py-3 rounded-md text-sm font-medium transition-colors", // 16px, 12px 패딩
49
+ active
50
+ ? "bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300"
51
+ : "text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-700");
52
+ case "compact":
53
+ return cn("flex items-center gap-2 px-2 py-1.5 rounded text-sm font-medium transition-colors", // 8px, 6px 패딩
54
+ active
55
+ ? "bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300"
56
+ : "text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-700");
57
+ default:
58
+ return cn("flex items-center gap-3 px-4 py-3 rounded-md text-sm font-medium transition-colors", // 16px, 12px 패딩
59
+ active
60
+ ? "bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300"
61
+ : "text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-700");
62
+ }
63
+ };
64
+ const getSizeClasses = () => {
65
+ switch (size) {
66
+ case "sm":
67
+ return "text-xs";
68
+ case "lg":
69
+ return "text-base";
70
+ default:
71
+ return "text-sm";
72
+ }
73
+ };
74
+ return (_jsxs("button", { ref: ref, className: cn(getVariantClasses(), getSizeClasses(), disabled && "opacity-50 cursor-not-allowed", className), disabled: disabled, ...props, children: [icon && (_jsx("div", { className: "flex-shrink-0 w-4 h-4", children: icon })), _jsx("span", { className: "flex-1 text-left", children: children })] }));
75
+ });
76
+ MenuItem.displayName = "MenuItem";
77
+ const MenuSeparator = React.forwardRef(({ className, variant = "default", ...props }, ref) => {
78
+ const getVariantClasses = () => {
79
+ switch (variant) {
80
+ case "horizontal":
81
+ return "w-px h-4 bg-gray-200 dark:bg-gray-700 mx-1"; // 4px 여백
82
+ case "vertical":
83
+ case "compact":
84
+ default:
85
+ return "h-px bg-gray-200 dark:bg-gray-700 my-2"; // 8px 여백
86
+ }
87
+ };
88
+ return (_jsx("div", { ref: ref, className: cn(getVariantClasses(), className), ...props }));
89
+ });
90
+ MenuSeparator.displayName = "MenuSeparator";
91
+ const MenuLabel = React.forwardRef(({ className, variant = "default", size = "md", children, ...props }, ref) => {
92
+ const getVariantClasses = () => {
93
+ switch (variant) {
94
+ case "horizontal":
95
+ return "px-3 py-1 text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide"; // 12px, 4px 패딩
96
+ case "vertical":
97
+ case "compact":
98
+ default:
99
+ return "px-4 py-2 text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide"; // 16px, 8px 패딩
100
+ }
101
+ };
102
+ const getSizeClasses = () => {
103
+ switch (size) {
104
+ case "sm":
105
+ return "text-xs";
106
+ case "lg":
107
+ return "text-sm";
108
+ default:
109
+ return "text-xs";
110
+ }
111
+ };
112
+ return (_jsx("div", { ref: ref, className: cn(getVariantClasses(), getSizeClasses(), className), ...props, children: children }));
113
+ });
114
+ MenuLabel.displayName = "MenuLabel";
115
+ // 편의 컴포넌트들
116
+ export const MenuHorizontal = React.forwardRef(({ className, ...props }, ref) => (_jsx(Menu, { ref: ref, variant: "horizontal", className: className, ...props })));
117
+ MenuHorizontal.displayName = "MenuHorizontal";
118
+ export const MenuVertical = React.forwardRef(({ className, ...props }, ref) => (_jsx(Menu, { ref: ref, variant: "vertical", className: className, ...props })));
119
+ MenuVertical.displayName = "MenuVertical";
120
+ export const MenuCompact = React.forwardRef(({ className, ...props }, ref) => (_jsx(Menu, { ref: ref, variant: "compact", className: className, ...props })));
121
+ MenuCompact.displayName = "MenuCompact";
122
+ export { Menu, MenuItem, MenuSeparator, MenuLabel };
@@ -0,0 +1,15 @@
1
+ import * as React from "react";
2
+ export interface ModalProps extends React.HTMLAttributes<HTMLDivElement> {
3
+ isOpen: boolean;
4
+ onClose: () => void;
5
+ children: React.ReactNode;
6
+ size?: "sm" | "md" | "lg" | "xl" | "2xl";
7
+ showCloseButton?: boolean;
8
+ closeOnOverlayClick?: boolean;
9
+ title?: string;
10
+ showBackdrop?: boolean;
11
+ centered?: boolean;
12
+ }
13
+ declare const Modal: React.ForwardRefExoticComponent<ModalProps & React.RefAttributes<HTMLDivElement>>;
14
+ export { Modal };
15
+ //# sourceMappingURL=Modal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Modal.d.ts","sourceRoot":"","sources":["../../src/components/Modal.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,MAAM,WAAW,UAAW,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IACtE,MAAM,EAAE,OAAO,CAAA;IACf,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,CAAA;IACxC,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,QAAA,MAAM,KAAK,mFAoIV,CAAA;AAGD,OAAO,EAAE,KAAK,EAAE,CAAA"}
@@ -0,0 +1,62 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { createPortal } from "react-dom";
5
+ import { cn } from "../lib/utils";
6
+ const Modal = React.forwardRef(({ className, isOpen, onClose, children, size = "md", showCloseButton = true, closeOnOverlayClick = true, title, showBackdrop = true, centered = true, ...props }, ref) => {
7
+ const modalRef = React.useRef(null);
8
+ // ESC 키로 모달 닫기
9
+ React.useEffect(() => {
10
+ const handleEscape = (e) => {
11
+ if (e.key === "Escape") {
12
+ onClose();
13
+ }
14
+ };
15
+ if (isOpen) {
16
+ document.addEventListener("keydown", handleEscape);
17
+ document.body.style.overflow = "hidden";
18
+ }
19
+ return () => {
20
+ document.removeEventListener("keydown", handleEscape);
21
+ document.body.style.overflow = "unset";
22
+ };
23
+ }, [isOpen, onClose]);
24
+ // 모달 외부 클릭으로 닫기
25
+ const handleOverlayClick = (e) => {
26
+ if (closeOnOverlayClick && e.target === e.currentTarget) {
27
+ onClose();
28
+ }
29
+ };
30
+ // 모달 크기 클래스 (4의 배수, 8의 배수 스페이싱)
31
+ const sizeClasses = {
32
+ sm: "w-80 max-w-sm", // 320px
33
+ md: "w-96 max-w-md", // 384px
34
+ lg: "w-[500px] max-w-lg", // 500px
35
+ xl: "w-[600px] max-w-xl", // 600px
36
+ "2xl": "w-[800px] max-w-2xl" // 800px
37
+ };
38
+ if (!isOpen)
39
+ return null;
40
+ return createPortal(_jsxs("div", { ref: ref, className: cn("fixed inset-0 z-50 flex items-center justify-center p-4", // 16px 패딩
41
+ centered ? "items-center" : "items-start pt-16", // 64px 상단 여백
42
+ className), onClick: handleOverlayClick, ...props, children: [showBackdrop && (_jsx("div", { className: "absolute inset-0 bg-black/60 backdrop-blur-md transition-opacity duration-300" })), _jsxs("div", { ref: modalRef, className: cn("relative bg-white dark:bg-gray-800 rounded-2xl shadow-2xl border border-gray-200/50 dark:border-gray-700/50 mx-4 transform transition-all duration-300 ease-out overflow-hidden", sizeClasses[size]), style: {
43
+ animation: "modalSlideIn 0.3s cubic-bezier(0.16, 1, 0.3, 1)"
44
+ }, children: [_jsx("div", { className: "absolute top-0 left-0 right-0 h-1 bg-gradient-to-r from-blue-500 via-purple-500 to-pink-500" }), _jsx("div", { className: "absolute top-0 right-0 w-32 h-32 bg-gradient-to-bl from-blue-400/10 to-transparent rounded-full -translate-y-16 translate-x-16" }), _jsx("div", { className: "absolute bottom-0 left-0 w-24 h-24 bg-gradient-to-tr from-purple-400/10 to-transparent rounded-full translate-y-12 -translate-x-12" }), title && (_jsxs("div", { className: "relative z-10 px-8 pt-8 pb-6 border-b border-gray-200/50 dark:border-gray-700/50", children: [" ", _jsx("h2", { className: "text-xl font-semibold text-gray-900 dark:text-white", children: title })] })), showCloseButton && (_jsx("button", { onClick: onClose, className: "absolute top-6 right-6 p-2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-all duration-200 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 hover:scale-110 z-20" // 24px 여백
45
+ , "aria-label": "\uB2EB\uAE30", children: _jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })), _jsx("div", { className: cn("relative z-10", title ? "px-8 py-8" : "p-8" // 32px 여백
46
+ ), children: children })] }), _jsx("style", { dangerouslySetInnerHTML: {
47
+ __html: `
48
+ @keyframes modalSlideIn {
49
+ from {
50
+ opacity: 0;
51
+ transform: scale(0.95) translateY(20px);
52
+ }
53
+ to {
54
+ opacity: 1;
55
+ transform: scale(1) translateY(0);
56
+ }
57
+ }
58
+ `
59
+ } })] }), document.body);
60
+ });
61
+ Modal.displayName = "Modal";
62
+ export { Modal };
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ export interface PageTransitionProps {
3
+ children: React.ReactNode;
4
+ className?: string;
5
+ duration?: number;
6
+ variant?: 'fade' | 'slide' | 'scale' | 'flip';
7
+ loadingVariant?: 'default' | 'rhythm' | 'flower' | 'heart' | 'star' | 'butterfly' | 'dots' | 'bars' | 'ring' | 'ripple';
8
+ loadingText?: string;
9
+ showLoading?: boolean;
10
+ onTransitionStart?: () => void;
11
+ onTransitionEnd?: () => void;
12
+ }
13
+ export declare const PageTransition: React.ForwardRefExoticComponent<PageTransitionProps & React.RefAttributes<HTMLDivElement>>;
14
+ export declare const FadeTransition: React.ForwardRefExoticComponent<Omit<PageTransitionProps, "variant"> & React.RefAttributes<HTMLDivElement>>;
15
+ export declare const SlideTransition: React.ForwardRefExoticComponent<Omit<PageTransitionProps, "variant"> & React.RefAttributes<HTMLDivElement>>;
16
+ export declare const ScaleTransition: React.ForwardRefExoticComponent<Omit<PageTransitionProps, "variant"> & React.RefAttributes<HTMLDivElement>>;
17
+ export declare const FlipTransition: React.ForwardRefExoticComponent<Omit<PageTransitionProps, "variant"> & React.RefAttributes<HTMLDivElement>>;
18
+ //# sourceMappingURL=PageTransition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PageTransition.d.ts","sourceRoot":"","sources":["../../src/components/PageTransition.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA8B,MAAM,OAAO,CAAA;AAIlD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,CAAA;IAC7C,cAAc,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAA;IACvH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAA;IAC9B,eAAe,CAAC,EAAE,MAAM,IAAI,CAAA;CAC7B;AAED,eAAO,MAAM,cAAc,4FAoEzB,CAAA;AAKF,eAAO,MAAM,cAAc,6GAEzB,CAAA;AAEF,eAAO,MAAM,eAAe,6GAE1B,CAAA;AAEF,eAAO,MAAM,eAAe,6GAE1B,CAAA;AAEF,eAAO,MAAM,cAAc,6GAEzB,CAAA"}
@@ -0,0 +1,39 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import React, { useState, useEffect } from 'react';
4
+ import { cn } from '../lib/utils';
5
+ import { LoadingSpinner } from './LoadingSpinner';
6
+ export const PageTransition = React.forwardRef(({ children, className, duration = 300, variant = 'fade', loadingVariant = 'butterfly', loadingText = '페이지 로딩 중...', showLoading = true, onTransitionStart, onTransitionEnd }, ref) => {
7
+ const [isLoading, setIsLoading] = useState(true);
8
+ const [isVisible, setIsVisible] = useState(false);
9
+ useEffect(() => {
10
+ const timer = setTimeout(() => {
11
+ setIsLoading(false);
12
+ setIsVisible(true);
13
+ onTransitionEnd?.();
14
+ }, duration);
15
+ onTransitionStart?.();
16
+ return () => clearTimeout(timer);
17
+ }, [duration, onTransitionStart, onTransitionEnd]);
18
+ const transitionClasses = {
19
+ fade: cn('transition-opacity duration-300 ease-in-out', isVisible ? 'opacity-100' : 'opacity-0'),
20
+ slide: cn('transition-transform duration-300 ease-in-out', isVisible ? 'translate-x-0' : 'translate-x-full'),
21
+ scale: cn('transition-all duration-300 ease-in-out', isVisible ? 'scale-100 opacity-100' : 'scale-95 opacity-0'),
22
+ flip: cn('transition-all duration-500 ease-in-out', isVisible ? 'rotate-y-0 opacity-100' : 'rotate-y-90 opacity-0')
23
+ };
24
+ if (isLoading && showLoading) {
25
+ return (_jsx("div", { className: cn('flex items-center justify-center min-h-screen', className), children: _jsx(LoadingSpinner, { size: "lg" }) }));
26
+ }
27
+ return (_jsx("div", { ref: ref, className: cn('w-full', transitionClasses[variant], className), style: { transitionDuration: `${duration}ms` }, children: children }));
28
+ });
29
+ PageTransition.displayName = 'PageTransition';
30
+ // Convenience components for different transition types
31
+ export const FadeTransition = React.forwardRef((props, ref) => (_jsx(PageTransition, { ref: ref, variant: "fade", ...props })));
32
+ export const SlideTransition = React.forwardRef((props, ref) => (_jsx(PageTransition, { ref: ref, variant: "slide", ...props })));
33
+ export const ScaleTransition = React.forwardRef((props, ref) => (_jsx(PageTransition, { ref: ref, variant: "scale", ...props })));
34
+ export const FlipTransition = React.forwardRef((props, ref) => (_jsx(PageTransition, { ref: ref, variant: "flip", ...props })));
35
+ // Add displayName for convenience components
36
+ FadeTransition.displayName = 'FadeTransition';
37
+ SlideTransition.displayName = 'SlideTransition';
38
+ ScaleTransition.displayName = 'ScaleTransition';
39
+ FlipTransition.displayName = 'FlipTransition';
@@ -0,0 +1,21 @@
1
+ import * as React from "react";
2
+ export interface PaginationProps extends React.HTMLAttributes<HTMLDivElement> {
3
+ currentPage: number;
4
+ totalPages: number;
5
+ onPageChange: (page: number) => void;
6
+ showFirstLast?: boolean;
7
+ showPrevNext?: boolean;
8
+ maxVisiblePages?: number;
9
+ size?: "sm" | "md" | "lg";
10
+ variant?: "default" | "outlined" | "minimal";
11
+ }
12
+ declare const Pagination: React.ForwardRefExoticComponent<PaginationProps & React.RefAttributes<HTMLDivElement>>;
13
+ export declare const PaginationOutlined: React.ForwardRefExoticComponent<Omit<PaginationProps, "variant"> & React.RefAttributes<HTMLDivElement>>;
14
+ export declare const PaginationMinimal: React.ForwardRefExoticComponent<Omit<PaginationProps, "variant"> & React.RefAttributes<HTMLDivElement>>;
15
+ export declare const PaginationWithInfo: React.ForwardRefExoticComponent<PaginationProps & {
16
+ totalItems?: number;
17
+ itemsPerPage?: number;
18
+ showInfo?: boolean;
19
+ } & React.RefAttributes<HTMLDivElement>>;
20
+ export { Pagination };
21
+ //# sourceMappingURL=Pagination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Pagination.d.ts","sourceRoot":"","sources":["../../src/components/Pagination.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,MAAM,WAAW,eAAgB,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IAC3E,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IACzB,OAAO,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS,CAAA;CAC7C;AAED,QAAA,MAAM,UAAU,wFAyMf,CAAA;AAID,eAAO,MAAM,kBAAkB,yGAI9B,CAAA;AAGD,eAAO,MAAM,iBAAiB,yGAI7B,CAAA;AAID,eAAO,MAAM,kBAAkB;iBAChB,MAAM;mBACJ,MAAM;eACV,OAAO;wCAuBnB,CAAA;AAGD,OAAO,EAAE,UAAU,EAAE,CAAA"}
@@ -0,0 +1,87 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "../lib/utils";
5
+ const Pagination = React.forwardRef(({ className, currentPage, totalPages, onPageChange, showFirstLast = true, showPrevNext = true, maxVisiblePages = 5, size = "md", variant = "default", ...props }, ref) => {
6
+ const getVisiblePages = () => {
7
+ const pages = [];
8
+ const halfVisible = Math.floor(maxVisiblePages / 2);
9
+ let start = Math.max(1, currentPage - halfVisible);
10
+ let end = Math.min(totalPages, currentPage + halfVisible);
11
+ // 조정
12
+ if (end - start + 1 < maxVisiblePages) {
13
+ if (start === 1) {
14
+ end = Math.min(totalPages, start + maxVisiblePages - 1);
15
+ }
16
+ else {
17
+ start = Math.max(1, end - maxVisiblePages + 1);
18
+ }
19
+ }
20
+ // 첫 페이지
21
+ if (start > 1) {
22
+ pages.push(1);
23
+ if (start > 2) {
24
+ pages.push("...");
25
+ }
26
+ }
27
+ // 중간 페이지들
28
+ for (let i = start; i <= end; i++) {
29
+ pages.push(i);
30
+ }
31
+ // 마지막 페이지
32
+ if (end < totalPages) {
33
+ if (end < totalPages - 1) {
34
+ pages.push("...");
35
+ }
36
+ pages.push(totalPages);
37
+ }
38
+ return pages;
39
+ };
40
+ const getSizeClasses = () => {
41
+ switch (size) {
42
+ case "sm":
43
+ return "h-8 px-2 text-sm"; // 32px 높이, 8px 패딩
44
+ case "lg":
45
+ return "h-12 px-4 text-base"; // 48px 높이, 16px 패딩
46
+ default:
47
+ return "h-10 px-3 text-sm"; // 40px 높이, 12px 패딩
48
+ }
49
+ };
50
+ const getVariantClasses = (isActive = false) => {
51
+ switch (variant) {
52
+ case "outlined":
53
+ return cn("border border-gray-300 dark:border-gray-600", isActive
54
+ ? "bg-blue-500 border-blue-500 text-white"
55
+ : "bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700");
56
+ case "minimal":
57
+ return cn("border-0", isActive
58
+ ? "bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300"
59
+ : "bg-transparent text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700");
60
+ default:
61
+ return cn("border-0", isActive
62
+ ? "bg-blue-500 text-white"
63
+ : "bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700");
64
+ }
65
+ };
66
+ const handlePageClick = (page) => {
67
+ if (page >= 1 && page <= totalPages && page !== currentPage) {
68
+ onPageChange(page);
69
+ }
70
+ };
71
+ const visiblePages = getVisiblePages();
72
+ return (_jsxs("div", { ref: ref, className: cn("flex items-center justify-center space-x-1", className), ...props, children: [showFirstLast && currentPage > 1 && (_jsx("button", { onClick: () => handlePageClick(1), className: cn("inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", getSizeClasses(), getVariantClasses()), "aria-label": "\uCCAB \uD398\uC774\uC9C0\uB85C \uC774\uB3D9", children: _jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M11 19l-7-7 7-7m8 14l-7-7 7-7" }) }) })), showPrevNext && currentPage > 1 && (_jsx("button", { onClick: () => handlePageClick(currentPage - 1), className: cn("inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", getSizeClasses(), getVariantClasses()), "aria-label": "\uC774\uC804 \uD398\uC774\uC9C0\uB85C \uC774\uB3D9", children: _jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) }) })), visiblePages.map((page, index) => (_jsx(React.Fragment, { children: page === "..." ? (_jsx("span", { className: cn("inline-flex items-center justify-center px-3 py-2 text-sm font-medium text-gray-500 dark:text-gray-400", getSizeClasses()), children: "..." })) : (_jsx("button", { onClick: () => handlePageClick(page), className: cn("inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", getSizeClasses(), getVariantClasses(page === currentPage)), "aria-label": `${page}페이지로 이동`, "aria-current": page === currentPage ? "page" : undefined, children: page })) }, index))), showPrevNext && currentPage < totalPages && (_jsx("button", { onClick: () => handlePageClick(currentPage + 1), className: cn("inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", getSizeClasses(), getVariantClasses()), "aria-label": "\uB2E4\uC74C \uD398\uC774\uC9C0\uB85C \uC774\uB3D9", children: _jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) }) })), showFirstLast && currentPage < totalPages && (_jsx("button", { onClick: () => handlePageClick(totalPages), className: cn("inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", getSizeClasses(), getVariantClasses()), "aria-label": "\uB9C8\uC9C0\uB9C9 \uD398\uC774\uC9C0\uB85C \uC774\uB3D9", children: _jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M13 5l7 7-7 7M5 5l7 7-7 7" }) }) }))] }));
73
+ });
74
+ Pagination.displayName = "Pagination";
75
+ // 편의 컴포넌트들
76
+ export const PaginationOutlined = React.forwardRef(({ className, ...props }, ref) => (_jsx(Pagination, { ref: ref, variant: "outlined", className: className, ...props })));
77
+ PaginationOutlined.displayName = "PaginationOutlined";
78
+ export const PaginationMinimal = React.forwardRef(({ className, ...props }, ref) => (_jsx(Pagination, { ref: ref, variant: "minimal", className: className, ...props })));
79
+ PaginationMinimal.displayName = "PaginationMinimal";
80
+ // 복합 컴포넌트들
81
+ export const PaginationWithInfo = React.forwardRef(({ totalItems = 0, itemsPerPage = 10, showInfo = true, className, ...props }, ref) => {
82
+ const startItem = (props.currentPage - 1) * itemsPerPage + 1;
83
+ const endItem = Math.min(props.currentPage * itemsPerPage, totalItems);
84
+ return (_jsxs("div", { ref: ref, className: cn("flex flex-col sm:flex-row items-center justify-between gap-4", className), children: [showInfo && totalItems > 0 && (_jsxs("div", { className: "text-sm text-gray-700 dark:text-gray-300", children: [startItem, "-", endItem, " / ", totalItems, "\uAC1C \uD56D\uBAA9"] })), _jsx(Pagination, { ...props })] }));
85
+ });
86
+ PaginationWithInfo.displayName = "PaginationWithInfo";
87
+ export { Pagination };
@@ -0,0 +1,16 @@
1
+ import * as React from "react";
2
+ export interface PopoverProps extends React.HTMLAttributes<HTMLDivElement> {
3
+ children: React.ReactNode;
4
+ trigger: React.ReactNode;
5
+ open?: boolean;
6
+ onOpenChange?: (open: boolean) => void;
7
+ position?: "top" | "bottom" | "left" | "right";
8
+ align?: "start" | "center" | "end";
9
+ offset?: number;
10
+ disabled?: boolean;
11
+ }
12
+ declare const Popover: React.ForwardRefExoticComponent<PopoverProps & React.RefAttributes<HTMLDivElement>>;
13
+ export declare const PopoverTrigger: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
14
+ export declare const PopoverContent: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
15
+ export { Popover };
16
+ //# sourceMappingURL=Popover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Popover.d.ts","sourceRoot":"","sources":["../../src/components/Popover.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,MAAM,WAAW,YAAa,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IACxE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,OAAO,EAAE,KAAK,CAAC,SAAS,CAAA;IACxB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACtC,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAA;IAC9C,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAA;IAClC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,QAAA,MAAM,OAAO,qFA+MZ,CAAA;AAID,eAAO,MAAM,cAAc,6GAU1B,CAAA;AAGD,eAAO,MAAM,cAAc,6GAU1B,CAAA;AAGD,OAAO,EAAE,OAAO,EAAE,CAAA"}