@uniai-fe/uds-templates 0.4.32 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/styles.css CHANGED
@@ -539,8 +539,10 @@
539
539
  }
540
540
 
541
541
  .uds-modal-container {
542
- display: flex;
543
- flex-direction: column;
542
+ display: grid;
543
+ grid-template-rows: auto minmax(0, 1fr) auto;
544
+ flex: 1 1 auto;
545
+ min-height: 0;
544
546
  width: 100%;
545
547
  }
546
548
 
@@ -550,11 +552,22 @@
550
552
  margin: 0;
551
553
  }
552
554
 
555
+ .uds-modal-header {
556
+ grid-row: 1;
557
+ }
558
+
559
+ .uds-modal-body {
560
+ grid-row: 2;
561
+ min-height: 0;
562
+ overflow: auto;
563
+ }
564
+
553
565
  .uds-modal-header-close-button {
554
566
  font-size: 0;
555
567
  }
556
568
 
557
569
  .uds-modal-footer {
570
+ grid-row: 3;
558
571
  padding: 0;
559
572
  border-top: none;
560
573
  }
@@ -1216,6 +1229,62 @@
1216
1229
  gap: var(--spacing-padding-5);
1217
1230
  }
1218
1231
 
1232
+ .auth-signup-type-options {
1233
+ display: flex;
1234
+ flex-direction: column;
1235
+ gap: var(--spacing-padding-6);
1236
+ }
1237
+
1238
+ .auth-signup-type-option-group {
1239
+ display: flex;
1240
+ flex-direction: column;
1241
+ gap: var(--spacing-padding-3);
1242
+ }
1243
+
1244
+ .auth-signup-type-question {
1245
+ margin: 0;
1246
+ color: var(--color-label-standard);
1247
+ font-size: var(--font-heading-xxsmall-size);
1248
+ line-height: 1.4;
1249
+ }
1250
+
1251
+ .auth-signup-type-option {
1252
+ display: flex;
1253
+ justify-content: flex-start;
1254
+ text-align: left;
1255
+ --button-default-padding-inline-large: 16px;
1256
+ --button-default-tertiary-outline-foreground: var(--color-label-alternative);
1257
+ }
1258
+ .auth-signup-type-option .button-left {
1259
+ display: flex;
1260
+ width: 24px;
1261
+ height: 24px;
1262
+ align-items: center;
1263
+ justify-content: center;
1264
+ flex-shrink: 0;
1265
+ }
1266
+ .auth-signup-type-option .button-left svg {
1267
+ display: block;
1268
+ width: 24px;
1269
+ height: 24px;
1270
+ }
1271
+ .auth-signup-type-option .button-label {
1272
+ flex: 1;
1273
+ text-align: left;
1274
+ }
1275
+
1276
+ .auth-signup-type-option-label {
1277
+ font-size: var(--font-body-large-size);
1278
+ line-height: 1.5;
1279
+ }
1280
+
1281
+ .auth-signup-type-description {
1282
+ margin: 0;
1283
+ color: var(--color-label-assistive);
1284
+ font-size: var(--font-body-xxsmall-size);
1285
+ line-height: 1.5;
1286
+ }
1287
+
1219
1288
  .auth-signup-verification {
1220
1289
  display: flex;
1221
1290
  flex-direction: column;
@@ -1225,34 +1294,47 @@
1225
1294
  .auth-signup-agreements {
1226
1295
  display: flex;
1227
1296
  flex-direction: column;
1228
- gap: var(--spacing-padding-4);
1297
+ gap: 12px;
1298
+ }
1299
+
1300
+ .auth-signup-agreement-all-field {
1301
+ gap: 0;
1229
1302
  }
1230
1303
 
1231
1304
  .auth-signup-agreement-all {
1232
- height: var(--theme-size-medium-3);
1233
- display: flex;
1234
- align-items: center;
1305
+ width: 100%;
1306
+ min-height: 56px;
1235
1307
  background: var(--color-bg-alternative-cool-gray, #f2f2f3);
1236
- border-radius: var(--theme-radius-large-2, 16px);
1237
- border-radius: var(--theme-radius-medium-3, 8px);
1238
- padding: 0 var(--spacing-padding-5);
1308
+ border-radius: 12px;
1309
+ padding: 8px 12px;
1310
+ gap: 6px;
1311
+ align-items: center;
1312
+ }
1313
+
1314
+ .auth-signup-agreement-all-checkbox {
1315
+ flex-shrink: 0;
1316
+ }
1317
+
1318
+ .auth-signup-agreement-all-label {
1319
+ color: var(--color-label-standard);
1320
+ font-size: var(--font-body-large-size);
1321
+ font-weight: 500;
1322
+ line-height: 1.5;
1239
1323
  }
1240
1324
 
1241
1325
  .auth-signup-agreements-list {
1242
1326
  width: 100%;
1243
- background: var(--color-common-100);
1244
- border-radius: var(--theme-radius-medium-3, 8px);
1245
- padding: var(--spacing-padding-3);
1246
1327
  display: flex;
1247
1328
  flex-direction: column;
1248
- gap: var(--spacing-padding-3);
1329
+ gap: 12px;
1330
+ padding: 4px 12px;
1249
1331
  }
1250
1332
 
1251
1333
  .auth-signup-agreement-row {
1252
1334
  display: flex;
1253
1335
  align-items: center;
1254
1336
  justify-content: space-between;
1255
- gap: var(--spacing-padding-3);
1337
+ gap: 8px;
1256
1338
  }
1257
1339
 
1258
1340
  .auth-signup-agreement-toggle {
@@ -1262,12 +1344,13 @@
1262
1344
  margin: 0;
1263
1345
  display: flex;
1264
1346
  align-items: center;
1265
- gap: var(--spacing-padding-3);
1347
+ gap: 8px;
1266
1348
  cursor: pointer;
1267
1349
  flex: 1;
1268
1350
  text-align: left;
1269
1351
  flex-wrap: nowrap;
1270
1352
  font-size: 14px;
1353
+ min-width: 0;
1271
1354
  }
1272
1355
 
1273
1356
  .auth-signup-agreement-icon {
@@ -1287,18 +1370,19 @@
1287
1370
 
1288
1371
  .auth-signup-agreement-toggle[data-checked=true] .auth-signup-agreement-icon {
1289
1372
  color: var(--color-primary-default);
1290
- border-color: var(--color-primary-default);
1291
1373
  }
1292
1374
 
1293
1375
  .auth-signup-agreement-label {
1294
- display: inline-flex;
1295
- gap: var(--spacing-padding-1);
1376
+ display: flex;
1377
+ gap: 0;
1296
1378
  align-items: baseline;
1297
1379
  flex-wrap: nowrap;
1380
+ min-width: 0;
1298
1381
  }
1299
1382
 
1300
1383
  .auth-signup-agreement-badge {
1301
1384
  font-size: 14px;
1385
+ font-weight: 400;
1302
1386
  color: var(--color-primary-default);
1303
1387
  }
1304
1388
  .auth-signup-agreement-badge[data-required=false] {
@@ -1309,7 +1393,9 @@
1309
1393
  font-size: 14px;
1310
1394
  color: var(--color-label-standard);
1311
1395
  font-weight: 400;
1312
- line-height: 1.4;
1396
+ line-height: 1.5;
1397
+ overflow: hidden;
1398
+ text-overflow: ellipsis;
1313
1399
  }
1314
1400
 
1315
1401
  .auth-signup-agreement-description {
@@ -1330,11 +1416,12 @@
1330
1416
  justify-content: center;
1331
1417
  width: 20px;
1332
1418
  height: 20px;
1419
+ flex-shrink: 0;
1333
1420
  }
1334
1421
  .auth-signup-agreement-detail svg {
1335
1422
  display: block;
1336
- width: 100%;
1337
- height: 100%;
1423
+ width: 16px;
1424
+ height: 16px;
1338
1425
  }
1339
1426
 
1340
1427
  .auth-signup-agreement-detail:disabled {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniai-fe/uds-templates",
3
- "version": "0.4.32",
3
+ "version": "0.5.0",
4
4
  "description": "UNIAI Design System; UI Templates Package",
5
5
  "type": "module",
6
6
  "private": false,
@@ -1,3 +1,6 @@
1
+ export * from "./useSignupTypeSelectForm";
1
2
  export * from "./useSignupUserInfoForm";
3
+ export * from "./useSignupFarmCodeForm";
4
+ export * from "./useSignupAgreementForm";
2
5
  export * from "./useSignupVerificationForm";
3
6
  export * from "./useSignupAccountForm";
@@ -0,0 +1,48 @@
1
+ "use client";
2
+
3
+ import { useMemo } from "react";
4
+ import type {
5
+ UseSignupAgreementFormOptions,
6
+ UseSignupAgreementFormReturn,
7
+ } from "../types";
8
+
9
+ const DEFAULT_THIRD_PARTY_AGREEMENT_ID = "thirdParty";
10
+
11
+ /**
12
+ * 회원가입 Step4 훅; 약관 동의 상태 계산
13
+ * @hook
14
+ * @param {UseSignupAgreementFormOptions} options 훅 옵션
15
+ * @desc
16
+ * - 필수 약관 id 집합과 CTA 활성화 여부를 계산한다.
17
+ */
18
+ export function useSignupAgreementForm({
19
+ agreements,
20
+ agreementState,
21
+ isThirdPartyRequired,
22
+ thirdPartyAgreementId = DEFAULT_THIRD_PARTY_AGREEMENT_ID,
23
+ }: UseSignupAgreementFormOptions): UseSignupAgreementFormReturn {
24
+ const requiredAgreementIds = useMemo(() => {
25
+ return agreements
26
+ .filter(option => {
27
+ if (option.required) {
28
+ return true;
29
+ }
30
+
31
+ return isThirdPartyRequired && option.id === thirdPartyAgreementId;
32
+ })
33
+ .map(option => option.id);
34
+ }, [agreements, isThirdPartyRequired, thirdPartyAgreementId]);
35
+
36
+ const allRequiredChecked =
37
+ requiredAgreementIds.length > 0
38
+ ? requiredAgreementIds.every(id => Boolean(agreementState[id]))
39
+ : true;
40
+
41
+ return {
42
+ requiredAgreementIds,
43
+ allRequiredChecked,
44
+ disabled: !allRequiredChecked,
45
+ };
46
+ }
47
+
48
+ export default useSignupAgreementForm;
@@ -0,0 +1,87 @@
1
+ "use client";
2
+
3
+ import { useMemo } from "react";
4
+ import { useWatch } from "react-hook-form";
5
+ import type { ReactNode } from "react";
6
+ import type {
7
+ AuthSignupFarmCodeFields,
8
+ AuthSignupFarmCodeValues,
9
+ AuthSignupFieldProps,
10
+ AuthSignupFormValues,
11
+ UseSignupFarmCodeFormOptions,
12
+ UseSignupFarmCodeFormReturn,
13
+ } from "../types";
14
+
15
+ /**
16
+ * 회원가입 Step3 훅; 농장 식별번호 입력
17
+ * @hook
18
+ * @template TFields
19
+ * @param {UseSignupFarmCodeFormOptions<TFields>} options 훅 옵션
20
+ * @desc
21
+ * - 1) form init → 2) register merge → 3) helper/state → 4) submit 순서
22
+ */
23
+ export function useSignupFarmCodeForm<
24
+ TFields extends Record<string, AuthSignupFieldProps> =
25
+ AuthSignupFarmCodeFields,
26
+ >({
27
+ fields,
28
+ form,
29
+ onSubmit,
30
+ }: UseSignupFarmCodeFormOptions<TFields>): UseSignupFarmCodeFormReturn<TFields> {
31
+ const values = useWatch({ control: form.control }) as
32
+ | AuthSignupFormValues
33
+ | undefined;
34
+
35
+ const register = useMemo(() => {
36
+ return (Object.keys(fields) as Array<keyof TFields>).reduce(
37
+ (acc, fieldKey) => {
38
+ const config = fields[fieldKey] as AuthSignupFieldProps;
39
+ const fieldName = config.attr?.name ?? String(fieldKey);
40
+ acc[fieldKey] = form.register(fieldName);
41
+ return acc;
42
+ },
43
+ {} as UseSignupFarmCodeFormReturn<TFields>["register"],
44
+ );
45
+ }, [fields, form]);
46
+
47
+ const helpers = useMemo(() => {
48
+ return (Object.keys(fields) as Array<keyof TFields>).reduce(
49
+ (acc, fieldKey) => {
50
+ const config = fields[fieldKey] as AuthSignupFieldProps;
51
+ const fieldName = config.attr?.name ?? String(fieldKey);
52
+ const state = form.getFieldState(fieldName);
53
+ acc[fieldKey] = {
54
+ text:
55
+ (state.error?.message as ReactNode | undefined) ?? config.helper,
56
+ state: state.invalid ? "error" : undefined,
57
+ };
58
+ return acc;
59
+ },
60
+ {} as UseSignupFarmCodeFormReturn<TFields>["helpers"],
61
+ );
62
+ }, [fields, form]);
63
+
64
+ const farmCodeFieldName =
65
+ fields.farmCode.attr?.name ??
66
+ ("farmCode" as keyof AuthSignupFarmCodeValues);
67
+ const farmCodeValue =
68
+ values?.[farmCodeFieldName as keyof AuthSignupFormValues] ?? "";
69
+ const normalizedFarmCode =
70
+ typeof farmCodeValue === "string"
71
+ ? farmCodeValue.replace(/\D/g, "")
72
+ : String(farmCodeValue ?? "").replace(/\D/g, "");
73
+
74
+ const disabled =
75
+ form.formState.isSubmitting || !/^\d{6}$/.test(normalizedFarmCode);
76
+
77
+ const onSubmitHandler = form.handleSubmit(onSubmit);
78
+
79
+ return {
80
+ register,
81
+ helpers,
82
+ disabled,
83
+ onSubmit: onSubmitHandler,
84
+ };
85
+ }
86
+
87
+ export default useSignupFarmCodeForm;
@@ -0,0 +1,56 @@
1
+ "use client";
2
+
3
+ import { useCallback, useEffect } from "react";
4
+ import { useWatch } from "react-hook-form";
5
+ import type {
6
+ AuthSignupFormValues,
7
+ AuthSignupTypeSelectValues,
8
+ AuthSignupTypeValue,
9
+ UseSignupTypeSelectFormOptions,
10
+ UseSignupTypeSelectFormReturn,
11
+ } from "../types";
12
+
13
+ /**
14
+ * 회원가입 Step1 훅; 가입 유형 선택
15
+ * @hook
16
+ * @param {UseSignupTypeSelectFormOptions} options 훅 옵션
17
+ * @desc
18
+ * - 1) form init → 2) register merge → 3) state/helper → 4) submit 순서
19
+ */
20
+ export function useSignupTypeSelectForm({
21
+ form,
22
+ onSubmit,
23
+ }: UseSignupTypeSelectFormOptions): UseSignupTypeSelectFormReturn {
24
+ useEffect(() => {
25
+ // 가입 유형은 커스텀 버튼으로 변경되므로 RHF에 명시적으로 등록한다.
26
+ form.register("signupType");
27
+ }, [form]);
28
+
29
+ const selectedType = useWatch({
30
+ control: form.control,
31
+ name: "signupType",
32
+ }) as AuthSignupTypeValue | undefined;
33
+
34
+ const handleValueChange = useCallback(
35
+ (value: AuthSignupTypeValue) => {
36
+ form.setValue("signupType", value, {
37
+ shouldDirty: true,
38
+ shouldTouch: true,
39
+ shouldValidate: true,
40
+ });
41
+ },
42
+ [form],
43
+ );
44
+
45
+ const disabled = form.formState.isSubmitting || !selectedType;
46
+ const onSubmitHandler = form.handleSubmit(onSubmit);
47
+
48
+ return {
49
+ selectedType,
50
+ onValueChange: handleValueChange,
51
+ disabled,
52
+ onSubmit: onSubmitHandler,
53
+ };
54
+ }
55
+
56
+ export default useSignupTypeSelectForm;
@@ -0,0 +1,15 @@
1
+ <svg
2
+ width="24"
3
+ height="24"
4
+ viewBox="0 0 24 24"
5
+ fill="none"
6
+ xmlns="http://www.w3.org/2000/svg"
7
+ >
8
+ <path
9
+ d="M4 10.8995L9.65685 16.5563L19.5563 6.65683"
10
+ stroke="currentColor"
11
+ stroke-width="1.6"
12
+ stroke-linecap="round"
13
+ stroke-linejoin="round"
14
+ />
15
+ </svg>
@@ -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="M4 10.8995L9.65685 16.5563L19.5563 6.65683" stroke="#1A6AFF" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
3
+ </svg>
@@ -1,5 +1,8 @@
1
1
  import {
2
+ AuthSignupTypeSelectForm,
2
3
  AuthSignupUserInfoForm,
4
+ AuthSignupFarmCodeForm,
5
+ AuthSignupAgreementForm,
3
6
  AuthSignupVerificationForm,
4
7
  AuthSignupAccountForm,
5
8
  AuthSignupComplete,
@@ -12,8 +15,11 @@ import "./styles/signup.scss";
12
15
  export type * from "./types";
13
16
  export * from "./hooks";
14
17
  export {
18
+ AuthSignupTypeSelectForm,
15
19
  AuthSignupProvider,
16
20
  AuthSignupUserInfoForm,
21
+ AuthSignupFarmCodeForm,
22
+ AuthSignupAgreementForm,
17
23
  AuthSignupVerificationForm,
18
24
  AuthSignupAccountForm,
19
25
  AuthSignupComplete,
@@ -23,6 +29,10 @@ export {
23
29
  export const AuthSignup = {
24
30
  Provider: AuthSignupProvider,
25
31
  Template: AuthSignupTemplate,
32
+ StepTypeSelect: AuthSignupTypeSelectForm,
33
+ StepIdentity: AuthSignupUserInfoForm,
34
+ StepFarmCode: AuthSignupFarmCodeForm,
35
+ StepAgreement: AuthSignupAgreementForm,
26
36
  StepUserInfo: AuthSignupUserInfoForm,
27
37
  StepVerification: AuthSignupVerificationForm,
28
38
  StepAccount: AuthSignupAccountForm,
@@ -86,6 +86,7 @@ export function AuthSignupAccountForm({
86
86
  <div className="auth-signup-fields">
87
87
  <Form.Field.Template
88
88
  width={fields.accountId.template?.width ?? "full"}
89
+ state={helpers.accountId?.state === "error" ? "error" : undefined}
89
90
  className={clsx(
90
91
  "auth-signup-field",
91
92
  "auth-signup-field-account-id",