@sonhoseong/mfa-lib 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 (98) hide show
  1. package/README.md +172 -0
  2. package/dist/components/button/ScrollTopButton.d.ts +5 -0
  3. package/dist/components/button/ScrollTopButton.d.ts.map +1 -0
  4. package/dist/components/button/ScrollTopButton.js +28 -0
  5. package/dist/components/button/index.d.ts +3 -0
  6. package/dist/components/button/index.d.ts.map +1 -0
  7. package/dist/components/button/index.js +2 -0
  8. package/dist/components/button/types/index.d.ts +10 -0
  9. package/dist/components/button/types/index.d.ts.map +1 -0
  10. package/dist/components/button/types/index.js +1 -0
  11. package/dist/components/error/ErrorBoundary.d.ts +25 -0
  12. package/dist/components/error/ErrorBoundary.d.ts.map +1 -0
  13. package/dist/components/error/ErrorBoundary.js +130 -0
  14. package/dist/components/error/index.d.ts +2 -0
  15. package/dist/components/error/index.d.ts.map +1 -0
  16. package/dist/components/error/index.js +1 -0
  17. package/dist/components/index.d.ts +6 -0
  18. package/dist/components/index.d.ts.map +1 -0
  19. package/dist/components/index.js +10 -0
  20. package/dist/components/loading/GlobalLoading.d.ts +12 -0
  21. package/dist/components/loading/GlobalLoading.d.ts.map +1 -0
  22. package/dist/components/loading/GlobalLoading.js +84 -0
  23. package/dist/components/loading/index.d.ts +2 -0
  24. package/dist/components/loading/index.d.ts.map +1 -0
  25. package/dist/components/loading/index.js +1 -0
  26. package/dist/components/modal/ModalContainer.d.ts +8 -0
  27. package/dist/components/modal/ModalContainer.d.ts.map +1 -0
  28. package/dist/components/modal/ModalContainer.js +161 -0
  29. package/dist/components/modal/ModalContext.d.ts +43 -0
  30. package/dist/components/modal/ModalContext.d.ts.map +1 -0
  31. package/dist/components/modal/ModalContext.js +78 -0
  32. package/dist/components/modal/index.d.ts +4 -0
  33. package/dist/components/modal/index.d.ts.map +1 -0
  34. package/dist/components/modal/index.js +2 -0
  35. package/dist/components/toast/ToastContainer.d.ts +12 -0
  36. package/dist/components/toast/ToastContainer.d.ts.map +1 -0
  37. package/dist/components/toast/ToastContainer.js +125 -0
  38. package/dist/components/toast/ToastContext.d.ts +40 -0
  39. package/dist/components/toast/ToastContext.d.ts.map +1 -0
  40. package/dist/components/toast/ToastContext.js +56 -0
  41. package/dist/components/toast/index.d.ts +4 -0
  42. package/dist/components/toast/index.d.ts.map +1 -0
  43. package/dist/components/toast/index.js +2 -0
  44. package/dist/hooks/index.d.ts +11 -0
  45. package/dist/hooks/index.d.ts.map +1 -0
  46. package/dist/hooks/index.js +17 -0
  47. package/dist/hooks/use-auth.d.ts +38 -0
  48. package/dist/hooks/use-auth.d.ts.map +1 -0
  49. package/dist/hooks/use-auth.js +117 -0
  50. package/dist/hooks/use-error-notification.d.ts +29 -0
  51. package/dist/hooks/use-error-notification.d.ts.map +1 -0
  52. package/dist/hooks/use-error-notification.js +91 -0
  53. package/dist/hooks/use-global-loading.d.ts +16 -0
  54. package/dist/hooks/use-global-loading.d.ts.map +1 -0
  55. package/dist/hooks/use-global-loading.js +72 -0
  56. package/dist/hooks/use-initialize.d.ts +26 -0
  57. package/dist/hooks/use-initialize.d.ts.map +1 -0
  58. package/dist/hooks/use-initialize.js +104 -0
  59. package/dist/hooks/use-modal.d.ts +39 -0
  60. package/dist/hooks/use-modal.d.ts.map +1 -0
  61. package/dist/hooks/use-modal.js +83 -0
  62. package/dist/hooks/use-navigate.d.ts +34 -0
  63. package/dist/hooks/use-navigate.d.ts.map +1 -0
  64. package/dist/hooks/use-navigate.js +89 -0
  65. package/dist/hooks/use-track-history.d.ts +33 -0
  66. package/dist/hooks/use-track-history.d.ts.map +1 -0
  67. package/dist/hooks/use-track-history.js +150 -0
  68. package/dist/index.d.ts +12 -0
  69. package/dist/index.d.ts.map +1 -0
  70. package/dist/index.js +17 -0
  71. package/dist/network/axios-factory.d.ts +61 -0
  72. package/dist/network/axios-factory.d.ts.map +1 -0
  73. package/dist/network/axios-factory.js +119 -0
  74. package/dist/network/index.d.ts +5 -0
  75. package/dist/network/index.d.ts.map +1 -0
  76. package/dist/network/index.js +4 -0
  77. package/dist/network/supabase-axios.d.ts +62 -0
  78. package/dist/network/supabase-axios.d.ts.map +1 -0
  79. package/dist/network/supabase-axios.js +74 -0
  80. package/dist/store/index.d.ts +2 -0
  81. package/dist/store/index.d.ts.map +1 -0
  82. package/dist/store/index.js +1 -0
  83. package/dist/store/store-access.d.ts +48 -0
  84. package/dist/store/store-access.d.ts.map +1 -0
  85. package/dist/store/store-access.js +131 -0
  86. package/dist/types/index.d.ts +58 -0
  87. package/dist/types/index.d.ts.map +1 -0
  88. package/dist/types/index.js +5 -0
  89. package/dist/types/service.d.ts +17 -0
  90. package/dist/types/service.d.ts.map +1 -0
  91. package/dist/types/service.js +31 -0
  92. package/dist/utils/index.d.ts +2 -0
  93. package/dist/utils/index.d.ts.map +1 -0
  94. package/dist/utils/index.js +1 -0
  95. package/dist/utils/storage.d.ts +27 -0
  96. package/dist/utils/storage.d.ts.map +1 -0
  97. package/dist/utils/storage.js +78 -0
  98. package/package.json +27 -0
@@ -0,0 +1,161 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /**
3
+ * Modal Container - KOMCA 패턴
4
+ * 모달 UI 렌더링
5
+ */
6
+ import { useEffect } from 'react';
7
+ import { useModalContext } from './ModalContext';
8
+ const ModalContainer = () => {
9
+ const { modals, closeModal } = useModalContext();
10
+ // ESC 키로 모달 닫기
11
+ useEffect(() => {
12
+ const handleKeyDown = (e) => {
13
+ if (e.key === 'Escape' && modals.length > 0) {
14
+ const lastModal = modals[modals.length - 1];
15
+ closeModal(lastModal.id, false);
16
+ }
17
+ };
18
+ document.addEventListener('keydown', handleKeyDown);
19
+ return () => document.removeEventListener('keydown', handleKeyDown);
20
+ }, [modals, closeModal]);
21
+ // 모달 열렸을 때 body 스크롤 방지
22
+ useEffect(() => {
23
+ if (modals.length > 0) {
24
+ document.body.style.overflow = 'hidden';
25
+ }
26
+ else {
27
+ document.body.style.overflow = '';
28
+ }
29
+ return () => {
30
+ document.body.style.overflow = '';
31
+ };
32
+ }, [modals.length]);
33
+ if (modals.length === 0)
34
+ return null;
35
+ const handleOverlayClick = (modal) => {
36
+ if (modal.closeOnOverlayClick !== false) {
37
+ closeModal(modal.id, false);
38
+ }
39
+ };
40
+ const handleConfirm = async (modal) => {
41
+ if (modal.onConfirm) {
42
+ await modal.onConfirm();
43
+ }
44
+ closeModal(modal.id, true);
45
+ };
46
+ const handleCancel = (modal) => {
47
+ modal.onCancel?.();
48
+ closeModal(modal.id, false);
49
+ };
50
+ return (_jsxs(_Fragment, { children: [modals.map((modal, index) => (_jsx("div", { className: "modal-overlay", style: { zIndex: 10000 + index }, onClick: () => handleOverlayClick(modal), children: _jsx("div", { className: "modal-container", onClick: (e) => e.stopPropagation(), children: modal.content ? (
51
+ // 커스텀 컨텐츠
52
+ modal.content) : (
53
+ // 기본 Alert/Confirm UI
54
+ _jsxs(_Fragment, { children: [modal.title && (_jsx("div", { className: "modal-header", children: _jsx("h3", { className: "modal-title", children: modal.title }) })), _jsx("div", { className: "modal-body", children: _jsx("p", { className: "modal-message", children: modal.message }) }), _jsxs("div", { className: "modal-footer", children: [modal.type === 'confirm' && modal.cancelText && (_jsx("button", { className: "modal-button secondary", onClick: () => handleCancel(modal), children: modal.cancelText })), _jsx("button", { className: "modal-button primary", onClick: () => handleConfirm(modal), children: modal.confirmText || '확인' })] })] })) }) }, modal.id))), _jsx("style", { children: `
55
+ .modal-overlay {
56
+ position: fixed;
57
+ top: 0;
58
+ left: 0;
59
+ right: 0;
60
+ bottom: 0;
61
+ background: rgba(0, 0, 0, 0.5);
62
+ display: flex;
63
+ align-items: center;
64
+ justify-content: center;
65
+ animation: modal-fade-in 0.2s ease-out;
66
+ }
67
+
68
+ @keyframes modal-fade-in {
69
+ from {
70
+ opacity: 0;
71
+ }
72
+ to {
73
+ opacity: 1;
74
+ }
75
+ }
76
+
77
+ .modal-container {
78
+ background: white;
79
+ border-radius: 12px;
80
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
81
+ min-width: 320px;
82
+ max-width: 480px;
83
+ max-height: 90vh;
84
+ overflow: hidden;
85
+ animation: modal-slide-up 0.2s ease-out;
86
+ }
87
+
88
+ @keyframes modal-slide-up {
89
+ from {
90
+ opacity: 0;
91
+ transform: translateY(20px);
92
+ }
93
+ to {
94
+ opacity: 1;
95
+ transform: translateY(0);
96
+ }
97
+ }
98
+
99
+ .modal-header {
100
+ padding: 20px 24px 0;
101
+ }
102
+
103
+ .modal-title {
104
+ margin: 0;
105
+ font-size: 18px;
106
+ font-weight: 600;
107
+ color: #111827;
108
+ }
109
+
110
+ .modal-body {
111
+ padding: 16px 24px 24px;
112
+ }
113
+
114
+ .modal-message {
115
+ margin: 0;
116
+ font-size: 14px;
117
+ color: #4b5563;
118
+ line-height: 1.6;
119
+ white-space: pre-wrap;
120
+ }
121
+
122
+ .modal-footer {
123
+ display: flex;
124
+ justify-content: flex-end;
125
+ gap: 8px;
126
+ padding: 16px 24px;
127
+ background: #f9fafb;
128
+ border-top: 1px solid #e5e7eb;
129
+ }
130
+
131
+ .modal-button {
132
+ padding: 10px 20px;
133
+ font-size: 14px;
134
+ font-weight: 500;
135
+ border-radius: 8px;
136
+ cursor: pointer;
137
+ transition: all 0.15s;
138
+ }
139
+
140
+ .modal-button.primary {
141
+ background: #3b82f6;
142
+ color: white;
143
+ border: none;
144
+ }
145
+
146
+ .modal-button.primary:hover {
147
+ background: #2563eb;
148
+ }
149
+
150
+ .modal-button.secondary {
151
+ background: white;
152
+ color: #374151;
153
+ border: 1px solid #d1d5db;
154
+ }
155
+
156
+ .modal-button.secondary:hover {
157
+ background: #f3f4f6;
158
+ }
159
+ ` })] }));
160
+ };
161
+ export default ModalContainer;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Modal Context - KOMCA 패턴
3
+ * 전역 모달 상태 관리
4
+ */
5
+ import React, { ReactNode } from 'react';
6
+ export type ModalType = 'alert' | 'confirm' | 'custom';
7
+ export interface ModalOptions {
8
+ type: ModalType;
9
+ title?: string;
10
+ message?: string;
11
+ confirmText?: string;
12
+ cancelText?: string;
13
+ content?: ReactNode;
14
+ onConfirm?: () => void | Promise<void>;
15
+ onCancel?: () => void;
16
+ closeOnOverlayClick?: boolean;
17
+ }
18
+ export interface ModalItem extends ModalOptions {
19
+ id: string;
20
+ resolve?: (value: boolean) => void;
21
+ }
22
+ interface ModalContextType {
23
+ modals: ModalItem[];
24
+ alert: (message: string, title?: string) => Promise<void>;
25
+ confirm: (message: string, title?: string) => Promise<boolean>;
26
+ openModal: (options: ModalOptions) => string;
27
+ closeModal: (id: string, result?: boolean) => void;
28
+ closeAll: () => void;
29
+ }
30
+ declare const ModalContext: React.Context<ModalContextType | null>;
31
+ interface ModalProviderProps {
32
+ children: ReactNode;
33
+ }
34
+ /**
35
+ * Modal Provider
36
+ */
37
+ export declare const ModalProvider: React.FC<ModalProviderProps>;
38
+ /**
39
+ * useModal Hook
40
+ */
41
+ export declare const useModalContext: () => ModalContextType;
42
+ export default ModalContext;
43
+ //# sourceMappingURL=ModalContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ModalContext.d.ts","sourceRoot":"","sources":["../../../src/components/modal/ModalContext.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,EAAoD,SAAS,EAAE,MAAM,OAAO,CAAC;AAG3F,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAC;AAGvD,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CACjC;AAGD,MAAM,WAAW,SAAU,SAAQ,YAAY;IAC3C,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACtC;AAGD,UAAU,gBAAgB;IACtB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/D,SAAS,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,MAAM,CAAC;IAC7C,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACnD,QAAQ,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,QAAA,MAAM,YAAY,wCAA+C,CAAC;AAElE,UAAU,kBAAkB;IACxB,QAAQ,EAAE,SAAS,CAAC;CACvB;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAkEtD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,QAAO,gBAMlC,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -0,0 +1,78 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * Modal Context - KOMCA 패턴
4
+ * 전역 모달 상태 관리
5
+ */
6
+ import { createContext, useContext, useCallback, useState } from 'react';
7
+ const ModalContext = createContext(null);
8
+ /**
9
+ * Modal Provider
10
+ */
11
+ export const ModalProvider = ({ children }) => {
12
+ const [modals, setModals] = useState([]);
13
+ const openModal = useCallback((options) => {
14
+ const id = `modal-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
15
+ setModals((prev) => [...prev, { ...options, id }]);
16
+ return id;
17
+ }, []);
18
+ const closeModal = useCallback((id, result = false) => {
19
+ setModals((prev) => {
20
+ const modal = prev.find((m) => m.id === id);
21
+ if (modal?.resolve) {
22
+ modal.resolve(result);
23
+ }
24
+ return prev.filter((m) => m.id !== id);
25
+ });
26
+ }, []);
27
+ const closeAll = useCallback(() => {
28
+ setModals((prev) => {
29
+ prev.forEach((modal) => modal.resolve?.(false));
30
+ return [];
31
+ });
32
+ }, []);
33
+ const alert = useCallback((message, title) => {
34
+ return new Promise((resolve) => {
35
+ const id = `modal-${Date.now()}`;
36
+ setModals((prev) => [
37
+ ...prev,
38
+ {
39
+ id,
40
+ type: 'alert',
41
+ title: title || '알림',
42
+ message,
43
+ confirmText: '확인',
44
+ resolve: () => resolve(),
45
+ },
46
+ ]);
47
+ });
48
+ }, []);
49
+ const confirm = useCallback((message, title) => {
50
+ return new Promise((resolve) => {
51
+ const id = `modal-${Date.now()}`;
52
+ setModals((prev) => [
53
+ ...prev,
54
+ {
55
+ id,
56
+ type: 'confirm',
57
+ title: title || '확인',
58
+ message,
59
+ confirmText: '확인',
60
+ cancelText: '취소',
61
+ resolve,
62
+ },
63
+ ]);
64
+ });
65
+ }, []);
66
+ return (_jsx(ModalContext.Provider, { value: { modals, alert, confirm, openModal, closeModal, closeAll }, children: children }));
67
+ };
68
+ /**
69
+ * useModal Hook
70
+ */
71
+ export const useModalContext = () => {
72
+ const context = useContext(ModalContext);
73
+ if (!context) {
74
+ throw new Error('useModalContext must be used within a ModalProvider');
75
+ }
76
+ return context;
77
+ };
78
+ export default ModalContext;
@@ -0,0 +1,4 @@
1
+ export { ModalProvider, useModalContext } from './ModalContext';
2
+ export type { ModalItem, ModalOptions, ModalType } from './ModalContext';
3
+ export { default as ModalContainer } from './ModalContainer';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/modal/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAChE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACzE,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { ModalProvider, useModalContext } from './ModalContext';
2
+ export { default as ModalContainer } from './ModalContainer';
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Toast Container - KOMCA 패턴
3
+ * 토스트 알림 UI 렌더링
4
+ */
5
+ import React from 'react';
6
+ interface ToastContainerProps {
7
+ /** 위치 */
8
+ position?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left' | 'top-center' | 'bottom-center';
9
+ }
10
+ declare const ToastContainer: React.FC<ToastContainerProps>;
11
+ export default ToastContainer;
12
+ //# sourceMappingURL=ToastContainer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToastContainer.d.ts","sourceRoot":"","sources":["../../../src/components/toast/ToastContainer.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAmB1B,UAAU,mBAAmB;IACzB,SAAS;IACT,QAAQ,CAAC,EAAE,WAAW,GAAG,UAAU,GAAG,cAAc,GAAG,aAAa,GAAG,YAAY,GAAG,eAAe,CAAC;CACzG;AAED,QAAA,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CA8IjD,CAAC;AAEF,eAAe,cAAc,CAAC"}
@@ -0,0 +1,125 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useToast } from './ToastContext';
3
+ // 타입별 아이콘
4
+ const icons = {
5
+ success: '✓',
6
+ error: '✕',
7
+ warning: '⚠',
8
+ info: 'ℹ',
9
+ };
10
+ // 타입별 색상
11
+ const colors = {
12
+ success: { bg: '#ecfdf5', border: '#10b981', icon: '#059669' },
13
+ error: { bg: '#fef2f2', border: '#ef4444', icon: '#dc2626' },
14
+ warning: { bg: '#fffbeb', border: '#f59e0b', icon: '#d97706' },
15
+ info: { bg: '#eff6ff', border: '#3b82f6', icon: '#2563eb' },
16
+ };
17
+ const ToastContainer = ({ position = 'top-right' }) => {
18
+ const { toasts, removeToast } = useToast();
19
+ const getPositionStyles = () => {
20
+ const base = { position: 'fixed', zIndex: 9998 };
21
+ switch (position) {
22
+ case 'top-left':
23
+ return { ...base, top: 20, left: 20 };
24
+ case 'top-center':
25
+ return { ...base, top: 20, left: '50%', transform: 'translateX(-50%)' };
26
+ case 'bottom-right':
27
+ return { ...base, bottom: 20, right: 20 };
28
+ case 'bottom-left':
29
+ return { ...base, bottom: 20, left: 20 };
30
+ case 'bottom-center':
31
+ return { ...base, bottom: 20, left: '50%', transform: 'translateX(-50%)' };
32
+ case 'top-right':
33
+ default:
34
+ return { ...base, top: 20, right: 20 };
35
+ }
36
+ };
37
+ if (toasts.length === 0)
38
+ return null;
39
+ return (_jsxs("div", { style: getPositionStyles(), children: [_jsx("div", { className: "toast-list", children: toasts.map((toast) => (_jsxs("div", { className: `toast-item toast-${toast.type}`, style: {
40
+ backgroundColor: colors[toast.type].bg,
41
+ borderLeftColor: colors[toast.type].border,
42
+ }, children: [_jsx("div", { className: "toast-icon", style: { color: colors[toast.type].icon }, children: icons[toast.type] }), _jsxs("div", { className: "toast-content", children: [toast.title && _jsx("div", { className: "toast-title", children: toast.title }), _jsx("div", { className: "toast-message", children: toast.message })] }), _jsx("button", { className: "toast-close", onClick: () => removeToast(toast.id), "aria-label": "\uB2EB\uAE30", children: "\u2715" })] }, toast.id))) }), _jsx("style", { children: `
43
+ .toast-list {
44
+ display: flex;
45
+ flex-direction: column;
46
+ gap: 8px;
47
+ max-width: 380px;
48
+ width: 100%;
49
+ }
50
+
51
+ .toast-item {
52
+ display: flex;
53
+ align-items: flex-start;
54
+ gap: 12px;
55
+ padding: 14px 16px;
56
+ border-radius: 8px;
57
+ border-left: 4px solid;
58
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
59
+ animation: toast-slide-in 0.3s ease-out;
60
+ }
61
+
62
+ @keyframes toast-slide-in {
63
+ from {
64
+ opacity: 0;
65
+ transform: translateX(100%);
66
+ }
67
+ to {
68
+ opacity: 1;
69
+ transform: translateX(0);
70
+ }
71
+ }
72
+
73
+ .toast-icon {
74
+ flex-shrink: 0;
75
+ width: 20px;
76
+ height: 20px;
77
+ display: flex;
78
+ align-items: center;
79
+ justify-content: center;
80
+ font-size: 14px;
81
+ font-weight: bold;
82
+ }
83
+
84
+ .toast-content {
85
+ flex: 1;
86
+ min-width: 0;
87
+ }
88
+
89
+ .toast-title {
90
+ font-size: 14px;
91
+ font-weight: 600;
92
+ color: #111827;
93
+ margin-bottom: 2px;
94
+ }
95
+
96
+ .toast-message {
97
+ font-size: 13px;
98
+ color: #4b5563;
99
+ line-height: 1.4;
100
+ word-break: break-word;
101
+ }
102
+
103
+ .toast-close {
104
+ flex-shrink: 0;
105
+ width: 20px;
106
+ height: 20px;
107
+ display: flex;
108
+ align-items: center;
109
+ justify-content: center;
110
+ background: transparent;
111
+ border: none;
112
+ cursor: pointer;
113
+ color: #9ca3af;
114
+ font-size: 12px;
115
+ border-radius: 4px;
116
+ transition: all 0.15s;
117
+ }
118
+
119
+ .toast-close:hover {
120
+ background: rgba(0, 0, 0, 0.1);
121
+ color: #374151;
122
+ }
123
+ ` })] }));
124
+ };
125
+ export default ToastContainer;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Toast Context - KOMCA 패턴
3
+ * 전역 토스트 알림 상태 관리
4
+ */
5
+ import React, { ReactNode } from 'react';
6
+ export type ToastType = 'success' | 'error' | 'warning' | 'info';
7
+ export interface ToastItem {
8
+ id: string;
9
+ type: ToastType;
10
+ message: string;
11
+ title?: string;
12
+ duration?: number;
13
+ }
14
+ interface ToastContextType {
15
+ toasts: ToastItem[];
16
+ addToast: (toast: Omit<ToastItem, 'id'>) => void;
17
+ removeToast: (id: string) => void;
18
+ success: (message: string, title?: string) => void;
19
+ error: (message: string, title?: string) => void;
20
+ warning: (message: string, title?: string) => void;
21
+ info: (message: string, title?: string) => void;
22
+ }
23
+ declare const ToastContext: React.Context<ToastContextType | null>;
24
+ interface ToastProviderProps {
25
+ children: ReactNode;
26
+ /** 기본 표시 시간 (ms) */
27
+ defaultDuration?: number;
28
+ /** 최대 표시 개수 */
29
+ maxToasts?: number;
30
+ }
31
+ /**
32
+ * Toast Provider
33
+ */
34
+ export declare const ToastProvider: React.FC<ToastProviderProps>;
35
+ /**
36
+ * useToast Hook
37
+ */
38
+ export declare const useToast: () => ToastContextType;
39
+ export default ToastContext;
40
+ //# sourceMappingURL=ToastContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToastContext.d.ts","sourceRoot":"","sources":["../../../src/components/toast/ToastContext.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,EAAoD,SAAS,EAAE,MAAM,OAAO,CAAC;AAG3F,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAGjE,MAAM,WAAW,SAAS;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAGD,UAAU,gBAAgB;IACtB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,IAAI,CAAC;IACjD,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACnD;AAED,QAAA,MAAM,YAAY,wCAA+C,CAAC;AAGlE,UAAU,kBAAkB;IACxB,QAAQ,EAAE,SAAS,CAAC;IACpB,oBAAoB;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAmDtD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,QAAQ,QAAO,gBAM3B,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -0,0 +1,56 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * Toast Context - KOMCA 패턴
4
+ * 전역 토스트 알림 상태 관리
5
+ */
6
+ import { createContext, useContext, useCallback, useState } from 'react';
7
+ const ToastContext = createContext(null);
8
+ /**
9
+ * Toast Provider
10
+ */
11
+ export const ToastProvider = ({ children, defaultDuration = 3000, maxToasts = 5, }) => {
12
+ const [toasts, setToasts] = useState([]);
13
+ const addToast = useCallback((toast) => {
14
+ const id = `toast-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
15
+ const duration = toast.duration ?? defaultDuration;
16
+ setToasts((prev) => {
17
+ const newToasts = [...prev, { ...toast, id }];
18
+ // 최대 개수 초과 시 오래된 것 제거
19
+ return newToasts.slice(-maxToasts);
20
+ });
21
+ // 자동 제거
22
+ if (duration > 0) {
23
+ setTimeout(() => {
24
+ removeToast(id);
25
+ }, duration);
26
+ }
27
+ }, [defaultDuration, maxToasts]);
28
+ const removeToast = useCallback((id) => {
29
+ setToasts((prev) => prev.filter((t) => t.id !== id));
30
+ }, []);
31
+ // 편의 메서드
32
+ const success = useCallback((message, title) => {
33
+ addToast({ type: 'success', message, title });
34
+ }, [addToast]);
35
+ const error = useCallback((message, title) => {
36
+ addToast({ type: 'error', message, title, duration: 5000 });
37
+ }, [addToast]);
38
+ const warning = useCallback((message, title) => {
39
+ addToast({ type: 'warning', message, title });
40
+ }, [addToast]);
41
+ const info = useCallback((message, title) => {
42
+ addToast({ type: 'info', message, title });
43
+ }, [addToast]);
44
+ return (_jsx(ToastContext.Provider, { value: { toasts, addToast, removeToast, success, error, warning, info }, children: children }));
45
+ };
46
+ /**
47
+ * useToast Hook
48
+ */
49
+ export const useToast = () => {
50
+ const context = useContext(ToastContext);
51
+ if (!context) {
52
+ throw new Error('useToast must be used within a ToastProvider');
53
+ }
54
+ return context;
55
+ };
56
+ export default ToastContext;
@@ -0,0 +1,4 @@
1
+ export { ToastProvider, useToast } from './ToastContext';
2
+ export type { ToastItem, ToastType } from './ToastContext';
3
+ export { default as ToastContainer } from './ToastContainer';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/toast/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACzD,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { ToastProvider, useToast } from './ToastContext';
2
+ export { default as ToastContainer } from './ToastContainer';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Hooks 모듈 - KOMCA 패턴
3
+ */
4
+ export * from './use-auth';
5
+ export * from './use-initialize';
6
+ export * from './use-track-history';
7
+ export * from './use-navigate';
8
+ export * from './use-global-loading';
9
+ export * from './use-modal';
10
+ export * from './use-error-notification';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,cAAc,YAAY,CAAC;AAG3B,cAAc,kBAAkB,CAAC;AAGjC,cAAc,qBAAqB,CAAC;AAGpC,cAAc,gBAAgB,CAAC;AAG/B,cAAc,sBAAsB,CAAC;AAGrC,cAAc,aAAa,CAAC;AAG5B,cAAc,0BAA0B,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Hooks 모듈 - KOMCA 패턴
3
+ */
4
+ // Auth Hooks
5
+ export * from './use-auth';
6
+ // Initialize Hook
7
+ export * from './use-initialize';
8
+ // Track History Hook
9
+ export * from './use-track-history';
10
+ // Navigate Hook
11
+ export * from './use-navigate';
12
+ // Global Loading Hook
13
+ export * from './use-global-loading';
14
+ // Modal Hooks
15
+ export * from './use-modal';
16
+ // Error Notification Hook
17
+ export * from './use-error-notification';
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Auth Hooks - KOMCA 패턴
3
+ * 로그인, 로그아웃, 토큰 갱신
4
+ */
5
+ import { User } from '../types';
6
+ export interface LoginResponse {
7
+ accessToken: string;
8
+ refreshToken?: string;
9
+ user: User;
10
+ }
11
+ export interface LoginRequest {
12
+ username: string;
13
+ password: string;
14
+ }
15
+ export type LoginFn = (request: LoginRequest) => Promise<LoginResponse>;
16
+ export type LogoutFn = () => Promise<void>;
17
+ export type RefreshFn = () => Promise<string | null>;
18
+ /**
19
+ * 로그인 Hook
20
+ */
21
+ export declare function useLogin(loginApi?: LoginFn): (request: LoginRequest) => Promise<LoginResponse | null>;
22
+ /**
23
+ * 로그아웃 Hook
24
+ */
25
+ export declare function useLogout(logoutApi?: LogoutFn): () => Promise<void>;
26
+ /**
27
+ * 토큰 갱신 Hook
28
+ */
29
+ export declare function useTokenRefresh(refreshApi?: RefreshFn): () => Promise<string | null>;
30
+ /**
31
+ * 인증 상태 확인 Hook
32
+ */
33
+ export declare function useAuthState(): {
34
+ isAuthenticated: boolean;
35
+ user: User | null;
36
+ accessToken: string;
37
+ };
38
+ //# sourceMappingURL=use-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-auth.d.ts","sourceRoot":"","sources":["../../src/hooks/use-auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAGhC,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,IAAI,CAAC;CACZ;AAGD,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAGD,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,EAAE,YAAY,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;AAGxE,MAAM,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;AAG3C,MAAM,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAErD;;GAEG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,aACN,YAAY,KAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAuChF;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,SAAS,CAAC,EAAE,QAAQ,SACf,OAAO,CAAC,IAAI,CAAC,CAuB3C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,UAAU,CAAC,EAAE,SAAS,SACvB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA0BpD;AAED;;GAEG;AACH,wBAAgB,YAAY;;;;EAS3B"}