@uniai-fe/uds-templates 0.0.11 → 0.0.13
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/README.md +11 -0
- package/dist/styles.css +916 -1074
- package/package.json +3 -3
- package/src/auth/common/complete/Template.tsx +47 -0
- package/src/auth/common/complete/img/circle-check-complete.svg +4 -0
- package/src/auth/common/complete/index.scss +38 -0
- package/src/auth/common/complete/types.ts +15 -0
- package/src/auth/common/container/header/StageHeader.tsx +61 -0
- package/src/auth/common/container/header/index.tsx +5 -0
- package/src/auth/common/container/header/stage-header.scss +50 -0
- package/src/{components/auth → auth/common}/container/index.tsx +2 -0
- package/src/auth/common/find/hooks/useFindAccountForm.ts +79 -0
- package/src/auth/common/find/markup/CodeStep.tsx +166 -0
- package/src/auth/common/find/markup/Header.tsx +46 -0
- package/src/auth/common/find/markup/InfoStep.tsx +109 -0
- package/src/auth/common/find/styles/email.scss +55 -0
- package/src/auth/common/find/styles/find-account.scss +4 -0
- package/src/auth/common/find/styles/layout.scss +19 -0
- package/src/auth/common/find/styles/password.scss +39 -0
- package/src/auth/common/find/styles/result.scss +78 -0
- package/src/auth/common/find/types/forms.ts +30 -0
- package/src/auth/common/find/types/index.ts +121 -0
- package/src/auth/common/find/utils/composeFieldProps.ts +45 -0
- package/src/auth/common/password/constants.ts +19 -0
- package/src/auth/common/password/hooks/useCheckPassword.ts +133 -0
- package/src/auth/common/password/img/check-password.svg +3 -0
- package/src/auth/common/password/markup/PasswordSetField.tsx +250 -0
- package/src/auth/common/password/styles/password-set-field.scss +49 -0
- package/src/auth/common/password/types.ts +142 -0
- package/src/auth/common/password/utils/composePasswordFieldProps.ts +44 -0
- package/src/auth/find-account.ts +28 -0
- package/src/auth/find-id/hooks/index.ts +1 -0
- package/src/auth/find-id/index.scss +1 -0
- package/src/auth/find-id/index.ts +23 -0
- package/src/auth/find-id/markup/StepComplete.tsx +58 -0
- package/src/auth/find-id/markup/StepIdentify.tsx +46 -0
- package/src/auth/find-id/markup/StepVerifyCode.tsx +48 -0
- package/src/auth/find-id/types/index.ts +66 -0
- package/src/auth/find-password/index.scss +1 -0
- package/src/auth/find-password/index.ts +30 -0
- package/src/auth/find-password/markup/StepComplete.tsx +30 -0
- package/src/auth/find-password/markup/StepIdentify.tsx +45 -0
- package/src/auth/find-password/markup/StepResetPassword.tsx +150 -0
- package/src/auth/find-password/markup/StepVerifyCode.tsx +48 -0
- package/src/auth/index.tsx +41 -0
- package/src/{components/auth → auth}/login/markup/Container.tsx +1 -1
- package/src/{components/auth → auth}/login/types/props.ts +1 -1
- package/src/{components/auth → auth}/signup/hooks/useSignupAccountForm.ts +26 -2
- package/src/{components/auth → auth}/signup/hooks/useSignupUserInfoForm.ts +10 -3
- package/src/auth/signup/img/check-agree.svg +3 -0
- package/src/auth/signup/img/chevron-open-detail.svg +3 -0
- package/src/{components/auth → auth}/signup/index.ts +3 -0
- package/src/auth/signup/markup/AccountForm.tsx +113 -0
- package/src/auth/signup/markup/Complete.tsx +59 -0
- package/src/auth/signup/markup/Template.tsx +110 -0
- package/src/{components/auth → auth}/signup/markup/UserInfoForm.tsx +23 -13
- package/src/auth/signup/markup/VerificationForm.tsx +285 -0
- package/src/{components/auth → auth}/signup/markup/index.ts +1 -0
- package/src/auth/signup/styles/signup.scss +187 -0
- package/src/{components/auth → auth}/signup/types/hooks.ts +1 -0
- package/src/{components/auth → auth}/signup/types/props.ts +49 -9
- package/src/auth/signup/utils/getSignupFieldDefaultValue.ts +40 -0
- package/src/index.scss +5 -4
- package/src/index.tsx +3 -3
- package/src/page-frame/mobile/header/PageFrameMobileHeader.tsx +52 -0
- package/src/page-frame/mobile/header/index.ts +4 -0
- package/src/page-frame/mobile/header/page-frame-mobile-header.scss +48 -0
- package/src/page-frame/mobile/img/chevron-backward.svg +3 -0
- package/src/components/auth/index.tsx +0 -20
- package/src/components/auth/signup/markup/AccountForm.tsx +0 -124
- package/src/components/auth/signup/markup/Complete.tsx +0 -61
- package/src/components/auth/signup/markup/VerificationForm.tsx +0 -155
- package/src/components/auth/signup/styles/signup.scss +0 -135
- /package/src/{components/auth → auth/common}/container/AuthContainer.tsx +0 -0
- /package/src/{components/auth → auth/common}/container/index.scss +0 -0
- /package/src/{components/auth → auth/common}/container/types.ts +0 -0
- /package/src/{components/auth → auth}/login/data/valid-options.ts +0 -0
- /package/src/{components/auth → auth}/login/hooks/index.ts +0 -0
- /package/src/{components/auth → auth}/login/hooks/useAuthLoginForm.ts +0 -0
- /package/src/{components/auth → auth}/login/index.scss +0 -0
- /package/src/{components/auth → auth}/login/index.tsx +0 -0
- /package/src/{components/auth → auth}/login/markup/FormField.tsx +0 -0
- /package/src/{components/auth → auth}/login/markup/LinkButtons.tsx +0 -0
- /package/src/{components/auth → auth}/login/styles/login.scss +0 -0
- /package/src/{components/auth → auth}/login/types/form.ts +0 -0
- /package/src/{components/auth → auth}/login/types/hooks.ts +0 -0
- /package/src/{components/auth → auth}/login/types.ts +0 -0
- /package/src/{components/auth → auth}/signup/hooks/index.ts +0 -0
- /package/src/{components/auth → auth}/signup/hooks/useSignupVerificationForm.ts +0 -0
- /package/src/{components/auth → auth}/signup/types/index.ts +0 -0
- /package/src/{components/auth → auth}/signup/utils/composeFieldProps.ts +0 -0
- /package/src/{components/modal → modal}/core/components/Container.tsx +0 -0
- /package/src/{components/modal → modal}/core/components/FooterButtons.tsx +0 -0
- /package/src/{components/modal → modal}/core/components/Provider.tsx +0 -0
- /package/src/{components/modal → modal}/core/components/Root.tsx +0 -0
- /package/src/{components/modal → modal}/core/hooks/useModal.ts +0 -0
- /package/src/{components/modal → modal}/core/jotai/atoms.ts +0 -0
- /package/src/{components/modal → modal}/index.scss +0 -0
- /package/src/{components/modal → modal}/index.tsx +0 -0
- /package/src/{components/modal → modal}/styles/animations.scss +0 -0
- /package/src/{components/modal → modal}/styles/base.scss +0 -0
- /package/src/{components/modal → modal}/styles/container.scss +0 -0
- /package/src/{components/modal → modal}/styles/dimmer.scss +0 -0
- /package/src/{components/modal → modal}/templates/Alert.tsx +0 -0
- /package/src/{components/modal → modal}/templates/Dialog.tsx +0 -0
- /package/src/{components/modal → modal}/types/footer.ts +0 -0
- /package/src/{components/modal → modal}/types/index.ts +0 -0
- /package/src/{components/modal → modal}/types/options.ts +0 -0
- /package/src/{components/modal → modal}/types/state.ts +0 -0
- /package/src/{components/modal → modal}/types/templates.ts +0 -0
- /package/src/{components/page-frame → page-frame}/container/PageFrameContainer.tsx +0 -0
- /package/src/{components/page-frame → page-frame}/container/index.scss +0 -0
- /package/src/{components/page-frame → page-frame}/container/index.tsx +0 -0
- /package/src/{components/page-frame → page-frame}/container/types.ts +0 -0
- /package/src/{components/page-frame → page-frame}/index.tsx +0 -0
- /package/src/{components/page-frame → page-frame}/mobile/PageFrameMobile.tsx +0 -0
- /package/src/{components/page-frame → page-frame}/mobile/index.scss +0 -0
- /package/src/{components/page-frame → page-frame}/mobile/index.tsx +0 -0
- /package/src/{components/page-frame → page-frame}/mobile/types.ts +0 -0
- /package/src/{components/page-frame → page-frame}/navigation/PageFrameNavigation.tsx +0 -0
- /package/src/{components/page-frame → page-frame}/navigation/index.scss +0 -0
- /package/src/{components/page-frame → page-frame}/navigation/index.tsx +0 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
.auth-signup-form {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: var(--spacing-padding-8, 32px);
|
|
5
|
+
margin-top: var(--spacing-padding-9, 36px);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.auth-signup-fields {
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
gap: var(--spacing-padding-5, 20px);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.auth-signup-verification {
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: column;
|
|
17
|
+
gap: var(--spacing-padding-9, 36px);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.auth-signup-agreements {
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
gap: var(--spacing-padding-4, 16px);
|
|
24
|
+
background: var(--color-background-subtle);
|
|
25
|
+
border-radius: var(--shape-rounded-3, 16px);
|
|
26
|
+
padding: var(--spacing-padding-5, 20px);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.auth-signup-agreement-all {
|
|
30
|
+
background: var(--color-background-soft, #f2f2f3);
|
|
31
|
+
border-radius: var(--shape-rounded-2, 12px);
|
|
32
|
+
padding: var(--spacing-padding-4, 16px);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.auth-signup-agreements-list {
|
|
36
|
+
width: 100%;
|
|
37
|
+
background: var(--color-common-100, #ffffff);
|
|
38
|
+
border-radius: var(--shape-rounded-2, 12px);
|
|
39
|
+
padding: var(--spacing-padding-4, 16px);
|
|
40
|
+
display: flex;
|
|
41
|
+
flex-direction: column;
|
|
42
|
+
gap: var(--spacing-padding-3, 12px);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.auth-signup-agreement-row {
|
|
46
|
+
display: flex;
|
|
47
|
+
align-items: center;
|
|
48
|
+
justify-content: space-between;
|
|
49
|
+
gap: var(--spacing-padding-3, 12px);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 약관 토글 행은 SVG 아이콘/텍스트를 하나의 버튼으로 묶어 디자인 명세와 일치시킨다.
|
|
53
|
+
.auth-signup-agreement-toggle {
|
|
54
|
+
border: none;
|
|
55
|
+
background: none;
|
|
56
|
+
padding: 0;
|
|
57
|
+
margin: 0;
|
|
58
|
+
display: flex;
|
|
59
|
+
align-items: center;
|
|
60
|
+
gap: var(--spacing-padding-3, 12px);
|
|
61
|
+
cursor: pointer;
|
|
62
|
+
flex: 1;
|
|
63
|
+
text-align: left;
|
|
64
|
+
flex-wrap: nowrap;
|
|
65
|
+
font-size: 14px;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.auth-signup-agreement-icon {
|
|
69
|
+
width: 20px;
|
|
70
|
+
height: 20px;
|
|
71
|
+
color: var(--color-label-assistive, #94989e);
|
|
72
|
+
flex-shrink: 0;
|
|
73
|
+
|
|
74
|
+
svg {
|
|
75
|
+
display: block;
|
|
76
|
+
width: 100%;
|
|
77
|
+
height: 100%;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
svg path {
|
|
81
|
+
stroke: currentColor;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.auth-signup-agreement-toggle[data-checked="true"] .auth-signup-agreement-icon {
|
|
86
|
+
color: var(--color-primary-default);
|
|
87
|
+
border-color: var(--color-primary-default);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.auth-signup-agreement-label {
|
|
91
|
+
display: inline-flex;
|
|
92
|
+
gap: var(--spacing-padding-1, 4px);
|
|
93
|
+
align-items: baseline;
|
|
94
|
+
flex-wrap: nowrap;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.auth-signup-agreement-badge {
|
|
98
|
+
font-size: 14px;
|
|
99
|
+
// font-weight: 600;
|
|
100
|
+
color: var(--color-primary-default);
|
|
101
|
+
|
|
102
|
+
&[data-required="false"] {
|
|
103
|
+
color: var(--color-label-standard);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.auth-signup-agreement-title {
|
|
108
|
+
font-size: 14px;
|
|
109
|
+
color: var(--color-label-standard);
|
|
110
|
+
font-weight: 400;
|
|
111
|
+
line-height: 1.4;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.auth-signup-agreement-description {
|
|
115
|
+
font-size: 13px;
|
|
116
|
+
color: var(--color-label-assistive);
|
|
117
|
+
margin: 0;
|
|
118
|
+
padding-left: calc(20px + var(--spacing-padding-3, 12px));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.auth-signup-agreement-detail {
|
|
122
|
+
border: none;
|
|
123
|
+
background: none;
|
|
124
|
+
color: var(--color-label-assistive);
|
|
125
|
+
cursor: pointer;
|
|
126
|
+
padding: 0;
|
|
127
|
+
display: flex;
|
|
128
|
+
align-items: center;
|
|
129
|
+
justify-content: center;
|
|
130
|
+
width: 20px;
|
|
131
|
+
height: 20px;
|
|
132
|
+
|
|
133
|
+
svg {
|
|
134
|
+
display: block;
|
|
135
|
+
width: 100%;
|
|
136
|
+
height: 100%;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.auth-signup-agreement-detail:disabled {
|
|
141
|
+
opacity: 0.4;
|
|
142
|
+
cursor: default;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.auth-signup-agreement-detail:hover svg path {
|
|
146
|
+
fill: var(--color-label-standard);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.auth-signup-complete {
|
|
150
|
+
display: flex;
|
|
151
|
+
flex-direction: column;
|
|
152
|
+
gap: var(--spacing-padding-7, 28px);
|
|
153
|
+
text-align: center;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.auth-signup-complete-illustration {
|
|
157
|
+
display: flex;
|
|
158
|
+
justify-content: center;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.auth-signup-complete-title {
|
|
162
|
+
font-size: 24px;
|
|
163
|
+
font-weight: 700;
|
|
164
|
+
color: var(--color-label-standard);
|
|
165
|
+
margin: 0;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.auth-signup-complete-description {
|
|
169
|
+
margin: var(--spacing-padding-2, 8px) 0 0;
|
|
170
|
+
color: var(--color-label-assistive);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.auth-signup-complete-actions {
|
|
174
|
+
display: flex;
|
|
175
|
+
flex-direction: column;
|
|
176
|
+
gap: var(--spacing-padding-4, 16px);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.auth-signup-agreement-drawer-body {
|
|
180
|
+
p {
|
|
181
|
+
font-weight: 400;
|
|
182
|
+
color: var(--color-label-standard);
|
|
183
|
+
font-size: var(--font-body-xxsmall-size);
|
|
184
|
+
line-height: 1.5em;
|
|
185
|
+
letter-spacing: 0px;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -30,6 +30,7 @@ export interface UseSignupUserInfoFormOptions<
|
|
|
30
30
|
fields: TFields;
|
|
31
31
|
form: UseFormReturn<AuthSignupUserInfoValues>;
|
|
32
32
|
onSubmit: SubmitHandler<AuthSignupUserInfoValues>;
|
|
33
|
+
activeFields?: Array<keyof TFields>;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
export interface UseSignupUserInfoFormReturn<
|
|
@@ -2,12 +2,13 @@ import type { ReactNode } from "react";
|
|
|
2
2
|
import type React from "react";
|
|
3
3
|
import type { SubmitHandler } from "react-hook-form";
|
|
4
4
|
import type {
|
|
5
|
-
|
|
5
|
+
EmailInputProps,
|
|
6
6
|
InputFieldProps,
|
|
7
7
|
InputPasswordProps,
|
|
8
8
|
InputProps,
|
|
9
9
|
PhoneInputProps,
|
|
10
10
|
} from "@uniai-fe/uds-primitives";
|
|
11
|
+
import type { AuthPasswordRule } from "../../common/password/types";
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* 회원가입 공통 필드 props; primitives InputFieldProps를 그대로 사용한다.
|
|
@@ -25,6 +26,7 @@ export type AuthSignupUserInfoValues = Record<string, string>; // name/phone 값
|
|
|
25
26
|
|
|
26
27
|
export interface AuthSignupUserInfoProps {
|
|
27
28
|
fields: AuthSignupUserInfoFields;
|
|
29
|
+
visibleFields?: Array<keyof AuthSignupUserInfoFields>;
|
|
28
30
|
submitLabel?: ReactNode;
|
|
29
31
|
formAttr?: React.FormHTMLAttributes<HTMLFormElement>;
|
|
30
32
|
onSubmit: SubmitHandler<AuthSignupUserInfoValues>;
|
|
@@ -39,9 +41,12 @@ export interface AuthSignupAgreementOption {
|
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
export type AuthSignupAgreementState = Record<string, boolean>;
|
|
44
|
+
export interface AuthSignupAgreementToggleAllOptions {
|
|
45
|
+
requiredOnly?: boolean;
|
|
46
|
+
}
|
|
42
47
|
|
|
43
48
|
export type AuthSignupVerificationFields = {
|
|
44
|
-
email: AuthSignupFieldProps<
|
|
49
|
+
email: AuthSignupFieldProps<EmailInputProps>;
|
|
45
50
|
};
|
|
46
51
|
|
|
47
52
|
export type AuthSignupVerificationValues = Record<string, string>;
|
|
@@ -51,7 +56,7 @@ export interface AuthSignupVerificationProps {
|
|
|
51
56
|
agreements: AuthSignupAgreementOption[];
|
|
52
57
|
agreementState: AuthSignupAgreementState;
|
|
53
58
|
onToggleAgreement: (agreementId: string) => void;
|
|
54
|
-
onToggleAll: () => void;
|
|
59
|
+
onToggleAll: (options?: AuthSignupAgreementToggleAllOptions) => void;
|
|
55
60
|
onOpenAgreementDetail?: (agreementId: string) => void;
|
|
56
61
|
submitLabel?: ReactNode;
|
|
57
62
|
formAttr?: React.FormHTMLAttributes<HTMLFormElement>;
|
|
@@ -67,11 +72,7 @@ export type AuthSignupAccountFields = {
|
|
|
67
72
|
|
|
68
73
|
export type AuthSignupAccountValues = Record<string, string>;
|
|
69
74
|
|
|
70
|
-
export
|
|
71
|
-
id: string;
|
|
72
|
-
label: ReactNode;
|
|
73
|
-
fulfilled: boolean;
|
|
74
|
-
}
|
|
75
|
+
export type AuthSignupPasswordRule = AuthPasswordRule;
|
|
75
76
|
|
|
76
77
|
export interface AuthSignupAccountProps {
|
|
77
78
|
fields: AuthSignupAccountFields;
|
|
@@ -96,10 +97,49 @@ export interface AuthSignupCompleteProps {
|
|
|
96
97
|
secondaryAction?: AuthSignupCompleteAction;
|
|
97
98
|
}
|
|
98
99
|
|
|
99
|
-
/**
|
|
100
|
+
/** Step 식별자 */
|
|
101
|
+
export type AuthSignupStepId =
|
|
102
|
+
| "userInfo"
|
|
103
|
+
| "verifyAgreement"
|
|
104
|
+
| "generateAccount"
|
|
105
|
+
| "complete";
|
|
106
|
+
|
|
107
|
+
/** 헤더 구성 요소 */
|
|
108
|
+
export interface AuthSignupHeaderProps {
|
|
109
|
+
navigationTitle: ReactNode;
|
|
110
|
+
headline: ReactNode;
|
|
111
|
+
description?: ReactNode;
|
|
112
|
+
backIcon?: ReactNode;
|
|
113
|
+
onBack?: () => void;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Step indicator 항목 */
|
|
117
|
+
export interface AuthSignupStepIndicatorItem {
|
|
118
|
+
id: AuthSignupStepId;
|
|
119
|
+
label: ReactNode;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface AuthSignupStepIndicatorProps {
|
|
123
|
+
current?: AuthSignupStepId;
|
|
124
|
+
currentIndex?: number;
|
|
125
|
+
items?: AuthSignupStepIndicatorItem[];
|
|
126
|
+
total?: number;
|
|
127
|
+
showLabels?: boolean;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Step 구성 props 집합 */
|
|
100
131
|
export interface AuthSignupFlowProps {
|
|
101
132
|
userInfo: AuthSignupUserInfoProps;
|
|
102
133
|
verification: AuthSignupVerificationProps;
|
|
103
134
|
account: AuthSignupAccountProps;
|
|
104
135
|
complete: AuthSignupCompleteProps;
|
|
105
136
|
}
|
|
137
|
+
|
|
138
|
+
/** 회원가입 템플릿 props */
|
|
139
|
+
export interface AuthSignupTemplateProps extends AuthSignupFlowProps {
|
|
140
|
+
className?: string;
|
|
141
|
+
header: AuthSignupHeaderProps;
|
|
142
|
+
step: AuthSignupStepId;
|
|
143
|
+
stepIndicator?: AuthSignupStepIndicatorProps;
|
|
144
|
+
footer?: React.ReactNode;
|
|
145
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { AuthSignupFieldProps } from "../types";
|
|
2
|
+
|
|
3
|
+
const stringifyValue = (value: unknown): string =>
|
|
4
|
+
value === undefined || value === null ? "" : String(value);
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 필드 설정에서 defaultValue를 추출해 RHF 초기값으로 사용한다.
|
|
8
|
+
* props.defaultValue → attr.defaultValue → props.value 순으로 우선한다.
|
|
9
|
+
*/
|
|
10
|
+
export const getSignupFieldDefaultValue = (
|
|
11
|
+
config: AuthSignupFieldProps,
|
|
12
|
+
): string => {
|
|
13
|
+
if (config.props) {
|
|
14
|
+
if (
|
|
15
|
+
"defaultValue" in config.props &&
|
|
16
|
+
config.props.defaultValue !== undefined
|
|
17
|
+
) {
|
|
18
|
+
return stringifyValue(config.props.defaultValue);
|
|
19
|
+
}
|
|
20
|
+
if ("value" in config.props && config.props.value !== undefined) {
|
|
21
|
+
return stringifyValue(config.props.value);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (config.attr) {
|
|
26
|
+
if (
|
|
27
|
+
"defaultValue" in config.attr &&
|
|
28
|
+
config.attr.defaultValue !== undefined
|
|
29
|
+
) {
|
|
30
|
+
return stringifyValue(config.attr.defaultValue);
|
|
31
|
+
}
|
|
32
|
+
if ("value" in config.attr && config.attr.value !== undefined) {
|
|
33
|
+
return stringifyValue(config.attr.value);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return "";
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export default getSignupFieldDefaultValue;
|
package/src/index.scss
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
/* templates styles는 foundation/primitives 순으로 import된 이후를 가정한다. */
|
|
1
2
|
@use "@uniai-fe/uds-primitives/styles";
|
|
2
3
|
|
|
3
4
|
/* 템플릿 레벨 스타일을 통합해 서비스 앱이 단일 엔트리만 import하도록 구성한다. */
|
|
4
|
-
@use "./
|
|
5
|
-
@use "./
|
|
6
|
-
@use "./
|
|
7
|
-
@use "./
|
|
5
|
+
@use "./page-frame/container/index.scss" as *;
|
|
6
|
+
@use "./page-frame/mobile/index.scss" as *;
|
|
7
|
+
@use "./page-frame/navigation/index.scss" as *;
|
|
8
|
+
@use "./modal/index.scss" as *;
|
package/src/index.tsx
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from "./
|
|
2
|
-
export * from "./
|
|
3
|
-
export * from "./
|
|
1
|
+
export * from "./page-frame";
|
|
2
|
+
export * from "./auth";
|
|
3
|
+
export * from "./modal";
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import clsx from "clsx";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
import "./page-frame-mobile-header.scss";
|
|
5
|
+
|
|
6
|
+
export interface PageFrameMobileHeaderProps {
|
|
7
|
+
className?: string;
|
|
8
|
+
title: ReactNode;
|
|
9
|
+
backIcon?: ReactNode;
|
|
10
|
+
onBack?: () => void;
|
|
11
|
+
leftSlot?: ReactNode;
|
|
12
|
+
rightSlot?: ReactNode;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 모바일 서비스 공통 page header; 뒤로가기 버튼 + 타이틀을 노출한다.
|
|
17
|
+
* @component
|
|
18
|
+
*/
|
|
19
|
+
export function PageFrameMobileHeader({
|
|
20
|
+
className,
|
|
21
|
+
title,
|
|
22
|
+
backIcon,
|
|
23
|
+
onBack,
|
|
24
|
+
leftSlot,
|
|
25
|
+
rightSlot,
|
|
26
|
+
}: PageFrameMobileHeaderProps) {
|
|
27
|
+
return (
|
|
28
|
+
<div className={clsx("page-frame-mobile-header", className)}>
|
|
29
|
+
<span className="page-frame-mobile-header__left">
|
|
30
|
+
{leftSlot ??
|
|
31
|
+
(onBack ? (
|
|
32
|
+
<button
|
|
33
|
+
type="button"
|
|
34
|
+
className="page-frame-mobile-header__back"
|
|
35
|
+
onClick={onBack}
|
|
36
|
+
aria-label="뒤로가기"
|
|
37
|
+
>
|
|
38
|
+
<span className="page-frame-mobile-header__back-icon">
|
|
39
|
+
{backIcon ?? "←"}
|
|
40
|
+
</span>
|
|
41
|
+
</button>
|
|
42
|
+
) : (
|
|
43
|
+
<span className="page-frame-mobile-header__back-placeholder" />
|
|
44
|
+
))}
|
|
45
|
+
</span>
|
|
46
|
+
<p className="page-frame-mobile-header__title">{title}</p>
|
|
47
|
+
<span className="page-frame-mobile-header__right">
|
|
48
|
+
{rightSlot ?? null}
|
|
49
|
+
</span>
|
|
50
|
+
</div>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
.page-frame-mobile-header {
|
|
2
|
+
position: relative;
|
|
3
|
+
display: flex;
|
|
4
|
+
align-items: center;
|
|
5
|
+
justify-content: space-between;
|
|
6
|
+
gap: var(--spacing-padding-2, 8px);
|
|
7
|
+
min-height: 40px;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.page-frame-mobile-header__left,
|
|
11
|
+
.page-frame-mobile-header__right {
|
|
12
|
+
width: 64px;
|
|
13
|
+
height: 40px;
|
|
14
|
+
display: inline-flex;
|
|
15
|
+
align-items: center;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.page-frame-mobile-header__back {
|
|
19
|
+
border: none;
|
|
20
|
+
background: none;
|
|
21
|
+
display: inline-flex;
|
|
22
|
+
align-items: center;
|
|
23
|
+
gap: var(--spacing-padding-1, 4px);
|
|
24
|
+
padding: var(--spacing-padding-1, 4px);
|
|
25
|
+
font-size: 14px;
|
|
26
|
+
color: var(--color-label-standard);
|
|
27
|
+
cursor: pointer;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.page-frame-mobile-header__back-icon {
|
|
31
|
+
font-size: 18px;
|
|
32
|
+
line-height: 1;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.page-frame-mobile-header__back-placeholder {
|
|
36
|
+
width: 100%;
|
|
37
|
+
height: 100%;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.page-frame-mobile-header__title {
|
|
41
|
+
position: absolute;
|
|
42
|
+
left: 50%;
|
|
43
|
+
transform: translateX(-50%);
|
|
44
|
+
margin: 0;
|
|
45
|
+
font-size: 16px;
|
|
46
|
+
font-weight: 600;
|
|
47
|
+
color: var(--color-label-standard);
|
|
48
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M15.5655 19.4341L8.13126 11.9998L15.5655 4.56554C15.878 4.25312 15.878 3.74686 15.5655 3.43444C15.2531 3.12202 14.7469 3.12202 14.4345 3.43444L6.43462 11.4343C6.1222 11.7467 6.1222 12.253 6.43461 12.5654L14.4345 20.5652C14.7469 20.8776 15.2531 20.8776 15.5655 20.5652C15.8778 20.2528 15.8779 19.7465 15.5655 19.4341Z" fill="#313235"/>
|
|
3
|
+
</svg>
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import "./login/index.scss";
|
|
2
|
-
|
|
3
|
-
import { AuthContainer } from "./container";
|
|
4
|
-
import { AuthLogin } from "./login";
|
|
5
|
-
import { AuthSignup } from "./signup";
|
|
6
|
-
|
|
7
|
-
export const Auth = {
|
|
8
|
-
Container: AuthContainer,
|
|
9
|
-
Login: AuthLogin,
|
|
10
|
-
Signup: AuthSignup,
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export {
|
|
14
|
-
useSignupUserInfoForm,
|
|
15
|
-
useSignupVerificationForm,
|
|
16
|
-
useSignupAccountForm,
|
|
17
|
-
} from "./signup";
|
|
18
|
-
|
|
19
|
-
export type * from "./login";
|
|
20
|
-
export type * from "./signup";
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import { useMemo } from "react";
|
|
2
|
-
import { useForm } from "react-hook-form";
|
|
3
|
-
import {
|
|
4
|
-
Button,
|
|
5
|
-
Input,
|
|
6
|
-
PasswordInput,
|
|
7
|
-
type InputPasswordProps,
|
|
8
|
-
type InputProps,
|
|
9
|
-
} from "@uniai-fe/uds-primitives";
|
|
10
|
-
import type { AuthSignupAccountProps, AuthSignupAccountValues } from "../types";
|
|
11
|
-
import { useSignupAccountForm } from "../hooks";
|
|
12
|
-
import { composeSignupFieldProps } from "../utils/composeFieldProps";
|
|
13
|
-
|
|
14
|
-
const DEFAULT_SUBMIT_LABEL = "가입 요청";
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* 회원가입 Step3; 계정 정보 입력(아이디/비밀번호)
|
|
18
|
-
* @component
|
|
19
|
-
* @param {AuthSignupAccountProps} props account props
|
|
20
|
-
* @param {AuthSignupAccountProps["fields"]} props.fields 아이디/비밀번호 필드
|
|
21
|
-
* @param {AuthSignupPasswordRule[]} [props.passwordRules] 비밀번호 규칙 상태
|
|
22
|
-
* @param {React.FormHTMLAttributes<HTMLFormElement>} [props.formAttr] form attr
|
|
23
|
-
* @param {import("react").ReactNode} [props.submitLabel] CTA 라벨
|
|
24
|
-
*/
|
|
25
|
-
export function AuthSignupAccountForm({
|
|
26
|
-
fields,
|
|
27
|
-
passwordRules,
|
|
28
|
-
formAttr,
|
|
29
|
-
submitLabel,
|
|
30
|
-
onSubmit,
|
|
31
|
-
}: AuthSignupAccountProps) {
|
|
32
|
-
// 필드 구성에서 name attr을 추출해 RHF default 값을 정의한다.
|
|
33
|
-
const defaultValues = useMemo(
|
|
34
|
-
() =>
|
|
35
|
-
(Object.keys(fields) as Array<keyof typeof fields>).reduce(
|
|
36
|
-
(acc, fieldKey) => {
|
|
37
|
-
const config = fields[fieldKey];
|
|
38
|
-
const fieldName = config.attr?.name ?? String(fieldKey);
|
|
39
|
-
acc[fieldName] = "";
|
|
40
|
-
return acc;
|
|
41
|
-
},
|
|
42
|
-
{} as AuthSignupAccountValues,
|
|
43
|
-
),
|
|
44
|
-
[fields],
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
const form = useForm<AuthSignupAccountValues>({
|
|
48
|
-
mode: "onChange",
|
|
49
|
-
defaultValues,
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const {
|
|
53
|
-
register,
|
|
54
|
-
helpers,
|
|
55
|
-
disabled,
|
|
56
|
-
onSubmit: handleSubmit,
|
|
57
|
-
} = useSignupAccountForm({
|
|
58
|
-
fields,
|
|
59
|
-
form,
|
|
60
|
-
onSubmit,
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
return (
|
|
64
|
-
<form
|
|
65
|
-
className="auth-signup-form auth-signup-form--account"
|
|
66
|
-
{...formAttr}
|
|
67
|
-
onSubmit={handleSubmit}
|
|
68
|
-
>
|
|
69
|
-
<div className="auth-signup-fields">
|
|
70
|
-
<Input
|
|
71
|
-
{...composeSignupFieldProps<InputProps>(
|
|
72
|
-
fields.accountId,
|
|
73
|
-
helpers.accountId,
|
|
74
|
-
)}
|
|
75
|
-
register={register.accountId}
|
|
76
|
-
/>
|
|
77
|
-
<PasswordInput
|
|
78
|
-
{...composeSignupFieldProps<InputPasswordProps>(
|
|
79
|
-
fields.password,
|
|
80
|
-
helpers.password,
|
|
81
|
-
)}
|
|
82
|
-
register={register.password}
|
|
83
|
-
/>
|
|
84
|
-
<PasswordInput
|
|
85
|
-
{...composeSignupFieldProps<InputPasswordProps>(
|
|
86
|
-
fields.confirmPassword,
|
|
87
|
-
helpers.confirmPassword,
|
|
88
|
-
)}
|
|
89
|
-
register={register.confirmPassword}
|
|
90
|
-
/>
|
|
91
|
-
</div>
|
|
92
|
-
{passwordRules?.length ? (
|
|
93
|
-
<ul className="auth-signup-password-rules">
|
|
94
|
-
{passwordRules.map(rule => (
|
|
95
|
-
<li
|
|
96
|
-
key={rule.id}
|
|
97
|
-
className="auth-signup-password-rule"
|
|
98
|
-
data-fulfilled={rule.fulfilled ? "true" : undefined}
|
|
99
|
-
>
|
|
100
|
-
<span
|
|
101
|
-
className="auth-signup-password-rule-indicator"
|
|
102
|
-
aria-hidden="true"
|
|
103
|
-
/>
|
|
104
|
-
<span className="auth-signup-password-rule-text">
|
|
105
|
-
{rule.label}
|
|
106
|
-
</span>
|
|
107
|
-
</li>
|
|
108
|
-
))}
|
|
109
|
-
</ul>
|
|
110
|
-
) : null}
|
|
111
|
-
<Button.Default
|
|
112
|
-
type="submit"
|
|
113
|
-
scale="solid-xlarge"
|
|
114
|
-
priority="primary"
|
|
115
|
-
block
|
|
116
|
-
disabled={disabled}
|
|
117
|
-
>
|
|
118
|
-
{submitLabel ?? DEFAULT_SUBMIT_LABEL}
|
|
119
|
-
</Button.Default>
|
|
120
|
-
</form>
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export default AuthSignupAccountForm;
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { Button } from "@uniai-fe/uds-primitives";
|
|
2
|
-
import type { AuthSignupCompleteProps } from "../types";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* 회원가입 Step4; 가입 완료/승인 대기 안내
|
|
6
|
-
* @component
|
|
7
|
-
* @param {AuthSignupCompleteProps} props complete props
|
|
8
|
-
* @param {React.ReactNode} props.title 완료 메시지
|
|
9
|
-
* @param {React.ReactNode} [props.description] 부가 설명
|
|
10
|
-
* @param {React.ReactNode} [props.illustration] 일러스트 영역
|
|
11
|
-
* @param {AuthSignupCompleteAction} props.primaryAction 주요 CTA
|
|
12
|
-
* @param {AuthSignupCompleteAction} [props.secondaryAction] 보조 CTA
|
|
13
|
-
*/
|
|
14
|
-
export function AuthSignupComplete({
|
|
15
|
-
title,
|
|
16
|
-
description,
|
|
17
|
-
illustration,
|
|
18
|
-
primaryAction,
|
|
19
|
-
secondaryAction,
|
|
20
|
-
}: AuthSignupCompleteProps) {
|
|
21
|
-
// CTA 섹션은 primary/secondary 순으로 렌더해 사용자가 자연스럽게 진행하도록 안내한다.
|
|
22
|
-
return (
|
|
23
|
-
<section className="auth-signup-complete">
|
|
24
|
-
{illustration ? (
|
|
25
|
-
<div className="auth-signup-complete-illustration">{illustration}</div>
|
|
26
|
-
) : null}
|
|
27
|
-
<div className="auth-signup-complete-text">
|
|
28
|
-
<h2 className="auth-signup-complete-title">{title}</h2>
|
|
29
|
-
{description ? (
|
|
30
|
-
<p className="auth-signup-complete-description">{description}</p>
|
|
31
|
-
) : null}
|
|
32
|
-
</div>
|
|
33
|
-
<div className="auth-signup-complete-actions">
|
|
34
|
-
<Button.Default
|
|
35
|
-
type="button"
|
|
36
|
-
scale="solid-xlarge"
|
|
37
|
-
priority="primary"
|
|
38
|
-
block
|
|
39
|
-
disabled={primaryAction.disabled}
|
|
40
|
-
onClick={primaryAction.onClick}
|
|
41
|
-
>
|
|
42
|
-
{primaryAction.label}
|
|
43
|
-
</Button.Default>
|
|
44
|
-
{secondaryAction ? (
|
|
45
|
-
<Button.Default
|
|
46
|
-
type="button"
|
|
47
|
-
scale="solid-xlarge"
|
|
48
|
-
priority="secondary"
|
|
49
|
-
block
|
|
50
|
-
disabled={secondaryAction.disabled}
|
|
51
|
-
onClick={secondaryAction.onClick}
|
|
52
|
-
>
|
|
53
|
-
{secondaryAction.label}
|
|
54
|
-
</Button.Default>
|
|
55
|
-
) : null}
|
|
56
|
-
</div>
|
|
57
|
-
</section>
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export default AuthSignupComplete;
|