@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.
Files changed (122) hide show
  1. package/README.md +11 -0
  2. package/dist/styles.css +916 -1074
  3. package/package.json +3 -3
  4. package/src/auth/common/complete/Template.tsx +47 -0
  5. package/src/auth/common/complete/img/circle-check-complete.svg +4 -0
  6. package/src/auth/common/complete/index.scss +38 -0
  7. package/src/auth/common/complete/types.ts +15 -0
  8. package/src/auth/common/container/header/StageHeader.tsx +61 -0
  9. package/src/auth/common/container/header/index.tsx +5 -0
  10. package/src/auth/common/container/header/stage-header.scss +50 -0
  11. package/src/{components/auth → auth/common}/container/index.tsx +2 -0
  12. package/src/auth/common/find/hooks/useFindAccountForm.ts +79 -0
  13. package/src/auth/common/find/markup/CodeStep.tsx +166 -0
  14. package/src/auth/common/find/markup/Header.tsx +46 -0
  15. package/src/auth/common/find/markup/InfoStep.tsx +109 -0
  16. package/src/auth/common/find/styles/email.scss +55 -0
  17. package/src/auth/common/find/styles/find-account.scss +4 -0
  18. package/src/auth/common/find/styles/layout.scss +19 -0
  19. package/src/auth/common/find/styles/password.scss +39 -0
  20. package/src/auth/common/find/styles/result.scss +78 -0
  21. package/src/auth/common/find/types/forms.ts +30 -0
  22. package/src/auth/common/find/types/index.ts +121 -0
  23. package/src/auth/common/find/utils/composeFieldProps.ts +45 -0
  24. package/src/auth/common/password/constants.ts +19 -0
  25. package/src/auth/common/password/hooks/useCheckPassword.ts +133 -0
  26. package/src/auth/common/password/img/check-password.svg +3 -0
  27. package/src/auth/common/password/markup/PasswordSetField.tsx +250 -0
  28. package/src/auth/common/password/styles/password-set-field.scss +49 -0
  29. package/src/auth/common/password/types.ts +142 -0
  30. package/src/auth/common/password/utils/composePasswordFieldProps.ts +44 -0
  31. package/src/auth/find-account.ts +28 -0
  32. package/src/auth/find-id/hooks/index.ts +1 -0
  33. package/src/auth/find-id/index.scss +1 -0
  34. package/src/auth/find-id/index.ts +23 -0
  35. package/src/auth/find-id/markup/StepComplete.tsx +58 -0
  36. package/src/auth/find-id/markup/StepIdentify.tsx +46 -0
  37. package/src/auth/find-id/markup/StepVerifyCode.tsx +48 -0
  38. package/src/auth/find-id/types/index.ts +66 -0
  39. package/src/auth/find-password/index.scss +1 -0
  40. package/src/auth/find-password/index.ts +30 -0
  41. package/src/auth/find-password/markup/StepComplete.tsx +30 -0
  42. package/src/auth/find-password/markup/StepIdentify.tsx +45 -0
  43. package/src/auth/find-password/markup/StepResetPassword.tsx +150 -0
  44. package/src/auth/find-password/markup/StepVerifyCode.tsx +48 -0
  45. package/src/auth/index.tsx +41 -0
  46. package/src/{components/auth → auth}/login/markup/Container.tsx +1 -1
  47. package/src/{components/auth → auth}/login/types/props.ts +1 -1
  48. package/src/{components/auth → auth}/signup/hooks/useSignupAccountForm.ts +26 -2
  49. package/src/{components/auth → auth}/signup/hooks/useSignupUserInfoForm.ts +10 -3
  50. package/src/auth/signup/img/check-agree.svg +3 -0
  51. package/src/auth/signup/img/chevron-open-detail.svg +3 -0
  52. package/src/{components/auth → auth}/signup/index.ts +3 -0
  53. package/src/auth/signup/markup/AccountForm.tsx +113 -0
  54. package/src/auth/signup/markup/Complete.tsx +59 -0
  55. package/src/auth/signup/markup/Template.tsx +110 -0
  56. package/src/{components/auth → auth}/signup/markup/UserInfoForm.tsx +23 -13
  57. package/src/auth/signup/markup/VerificationForm.tsx +285 -0
  58. package/src/{components/auth → auth}/signup/markup/index.ts +1 -0
  59. package/src/auth/signup/styles/signup.scss +187 -0
  60. package/src/{components/auth → auth}/signup/types/hooks.ts +1 -0
  61. package/src/{components/auth → auth}/signup/types/props.ts +49 -9
  62. package/src/auth/signup/utils/getSignupFieldDefaultValue.ts +40 -0
  63. package/src/index.scss +5 -4
  64. package/src/index.tsx +3 -3
  65. package/src/page-frame/mobile/header/PageFrameMobileHeader.tsx +52 -0
  66. package/src/page-frame/mobile/header/index.ts +4 -0
  67. package/src/page-frame/mobile/header/page-frame-mobile-header.scss +48 -0
  68. package/src/page-frame/mobile/img/chevron-backward.svg +3 -0
  69. package/src/components/auth/index.tsx +0 -20
  70. package/src/components/auth/signup/markup/AccountForm.tsx +0 -124
  71. package/src/components/auth/signup/markup/Complete.tsx +0 -61
  72. package/src/components/auth/signup/markup/VerificationForm.tsx +0 -155
  73. package/src/components/auth/signup/styles/signup.scss +0 -135
  74. /package/src/{components/auth → auth/common}/container/AuthContainer.tsx +0 -0
  75. /package/src/{components/auth → auth/common}/container/index.scss +0 -0
  76. /package/src/{components/auth → auth/common}/container/types.ts +0 -0
  77. /package/src/{components/auth → auth}/login/data/valid-options.ts +0 -0
  78. /package/src/{components/auth → auth}/login/hooks/index.ts +0 -0
  79. /package/src/{components/auth → auth}/login/hooks/useAuthLoginForm.ts +0 -0
  80. /package/src/{components/auth → auth}/login/index.scss +0 -0
  81. /package/src/{components/auth → auth}/login/index.tsx +0 -0
  82. /package/src/{components/auth → auth}/login/markup/FormField.tsx +0 -0
  83. /package/src/{components/auth → auth}/login/markup/LinkButtons.tsx +0 -0
  84. /package/src/{components/auth → auth}/login/styles/login.scss +0 -0
  85. /package/src/{components/auth → auth}/login/types/form.ts +0 -0
  86. /package/src/{components/auth → auth}/login/types/hooks.ts +0 -0
  87. /package/src/{components/auth → auth}/login/types.ts +0 -0
  88. /package/src/{components/auth → auth}/signup/hooks/index.ts +0 -0
  89. /package/src/{components/auth → auth}/signup/hooks/useSignupVerificationForm.ts +0 -0
  90. /package/src/{components/auth → auth}/signup/types/index.ts +0 -0
  91. /package/src/{components/auth → auth}/signup/utils/composeFieldProps.ts +0 -0
  92. /package/src/{components/modal → modal}/core/components/Container.tsx +0 -0
  93. /package/src/{components/modal → modal}/core/components/FooterButtons.tsx +0 -0
  94. /package/src/{components/modal → modal}/core/components/Provider.tsx +0 -0
  95. /package/src/{components/modal → modal}/core/components/Root.tsx +0 -0
  96. /package/src/{components/modal → modal}/core/hooks/useModal.ts +0 -0
  97. /package/src/{components/modal → modal}/core/jotai/atoms.ts +0 -0
  98. /package/src/{components/modal → modal}/index.scss +0 -0
  99. /package/src/{components/modal → modal}/index.tsx +0 -0
  100. /package/src/{components/modal → modal}/styles/animations.scss +0 -0
  101. /package/src/{components/modal → modal}/styles/base.scss +0 -0
  102. /package/src/{components/modal → modal}/styles/container.scss +0 -0
  103. /package/src/{components/modal → modal}/styles/dimmer.scss +0 -0
  104. /package/src/{components/modal → modal}/templates/Alert.tsx +0 -0
  105. /package/src/{components/modal → modal}/templates/Dialog.tsx +0 -0
  106. /package/src/{components/modal → modal}/types/footer.ts +0 -0
  107. /package/src/{components/modal → modal}/types/index.ts +0 -0
  108. /package/src/{components/modal → modal}/types/options.ts +0 -0
  109. /package/src/{components/modal → modal}/types/state.ts +0 -0
  110. /package/src/{components/modal → modal}/types/templates.ts +0 -0
  111. /package/src/{components/page-frame → page-frame}/container/PageFrameContainer.tsx +0 -0
  112. /package/src/{components/page-frame → page-frame}/container/index.scss +0 -0
  113. /package/src/{components/page-frame → page-frame}/container/index.tsx +0 -0
  114. /package/src/{components/page-frame → page-frame}/container/types.ts +0 -0
  115. /package/src/{components/page-frame → page-frame}/index.tsx +0 -0
  116. /package/src/{components/page-frame → page-frame}/mobile/PageFrameMobile.tsx +0 -0
  117. /package/src/{components/page-frame → page-frame}/mobile/index.scss +0 -0
  118. /package/src/{components/page-frame → page-frame}/mobile/index.tsx +0 -0
  119. /package/src/{components/page-frame → page-frame}/mobile/types.ts +0 -0
  120. /package/src/{components/page-frame → page-frame}/navigation/PageFrameNavigation.tsx +0 -0
  121. /package/src/{components/page-frame → page-frame}/navigation/index.scss +0 -0
  122. /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
- EmailVerificationInputProps,
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<EmailVerificationInputProps>;
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 interface AuthSignupPasswordRule {
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 "./components/page-frame/container/index.scss" as *;
5
- @use "./components/page-frame/mobile/index.scss" as *;
6
- @use "./components/page-frame/navigation/index.scss" as *;
7
- @use "./components/modal/index.scss" as *;
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 "./components/page-frame";
2
- export * from "./components/auth";
3
- export * from "./components/modal";
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,4 @@
1
+ export {
2
+ PageFrameMobileHeader as PageFrameMobileHeader,
3
+ type PageFrameMobileHeaderProps,
4
+ } from "./PageFrameMobileHeader";
@@ -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;