@uniai-fe/uds-templates 0.0.10 → 0.0.11

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 (44) hide show
  1. package/README.md +77 -1
  2. package/dist/styles.css +212 -267
  3. package/package.json +4 -2
  4. package/src/components/auth/index.tsx +10 -10
  5. package/src/components/auth/login/index.tsx +1 -7
  6. package/src/components/auth/login/markup/FormField.tsx +2 -2
  7. package/src/components/auth/login/types/props.ts +12 -12
  8. package/src/components/auth/login/types.ts +2 -2
  9. package/src/components/auth/signup/hooks/index.ts +3 -0
  10. package/src/components/auth/signup/hooks/useSignupAccountForm.ts +77 -0
  11. package/src/components/auth/signup/hooks/useSignupUserInfoForm.ts +81 -0
  12. package/src/components/auth/signup/hooks/useSignupVerificationForm.ts +77 -0
  13. package/src/components/auth/signup/index.ts +24 -0
  14. package/src/components/auth/signup/markup/AccountForm.tsx +124 -0
  15. package/src/components/auth/signup/markup/Complete.tsx +61 -0
  16. package/src/components/auth/signup/markup/UserInfoForm.tsx +97 -0
  17. package/src/components/auth/signup/markup/VerificationForm.tsx +155 -0
  18. package/src/components/auth/signup/markup/index.ts +4 -0
  19. package/src/components/auth/signup/styles/signup.scss +135 -0
  20. package/src/components/auth/signup/types/hooks.ts +85 -0
  21. package/src/components/auth/signup/types/index.ts +2 -0
  22. package/src/components/auth/signup/types/props.ts +105 -0
  23. package/src/components/auth/signup/utils/composeFieldProps.ts +50 -0
  24. package/src/components/modal/core/components/Container.tsx +41 -0
  25. package/src/components/modal/core/components/FooterButtons.tsx +132 -0
  26. package/src/components/modal/core/components/Provider.tsx +28 -0
  27. package/src/components/modal/core/components/Root.tsx +93 -0
  28. package/src/components/modal/core/hooks/useModal.ts +136 -0
  29. package/src/components/modal/core/jotai/atoms.ts +10 -0
  30. package/src/components/modal/index.scss +4 -0
  31. package/src/components/modal/index.tsx +16 -0
  32. package/src/components/modal/styles/animations.scss +24 -0
  33. package/src/components/modal/styles/base.scss +45 -0
  34. package/src/components/modal/styles/container.scss +138 -0
  35. package/src/components/modal/styles/dimmer.scss +23 -0
  36. package/src/components/modal/templates/Alert.tsx +104 -0
  37. package/src/components/modal/templates/Dialog.tsx +112 -0
  38. package/src/components/modal/types/footer.ts +36 -0
  39. package/src/components/modal/types/index.ts +21 -0
  40. package/src/components/modal/types/options.ts +6 -0
  41. package/src/components/modal/types/state.ts +31 -0
  42. package/src/components/modal/types/templates.ts +32 -0
  43. package/src/index.scss +1 -0
  44. package/src/index.tsx +1 -0
@@ -0,0 +1,104 @@
1
+ "use client";
2
+
3
+ import type {
4
+ AlertTemplateOptions,
5
+ ModalFooterButton,
6
+ ModalState,
7
+ ModalTemplateButtonSpec,
8
+ } from "../types";
9
+
10
+ const DEFAULT_CONFIRM_LABEL = "확인";
11
+ const DEFAULT_CANCEL_LABEL = "취소";
12
+
13
+ function AlertBody({ message }: { message: AlertTemplateOptions["message"] }) {
14
+ return (
15
+ <div className="uds-modal-alert-message">
16
+ {typeof message === "string" ? <p>{message}</p> : message}
17
+ </div>
18
+ );
19
+ }
20
+
21
+ const createAlertFooterButtons = ({
22
+ stackKey,
23
+ primary,
24
+ cancel,
25
+ }: {
26
+ stackKey: string;
27
+ primary: ModalTemplateButtonSpec;
28
+ cancel?: ModalTemplateButtonSpec;
29
+ }): ModalFooterButton[] => {
30
+ const buttons: ModalFooterButton[] = [];
31
+
32
+ if (cancel) {
33
+ // cancel 버튼은 저강조 텍스트 버튼 규격을 고정해 디자인 일관성을 맞춘다.
34
+ buttons.push({
35
+ stackKey,
36
+ role: "close",
37
+ defaultOptions: {
38
+ label: cancel.label ?? DEFAULT_CANCEL_LABEL,
39
+ appearance: "text",
40
+ priority: "tertiary",
41
+ textSize: "large",
42
+ disabled: cancel.disabled,
43
+ onClick: cancel.onClick,
44
+ },
45
+ closeOnClick: cancel.closeOnClick,
46
+ });
47
+ }
48
+
49
+ // confirm 버튼은 고강조 텍스트 버튼으로 구성한다.
50
+ buttons.push({
51
+ stackKey,
52
+ role: "close",
53
+ defaultOptions: {
54
+ label: primary.label ?? DEFAULT_CONFIRM_LABEL,
55
+ appearance: "text",
56
+ priority: "secondary",
57
+ textSize: "large",
58
+ disabled: primary.disabled,
59
+ onClick: primary.onClick,
60
+ },
61
+ closeOnClick: primary.closeOnClick,
62
+ });
63
+
64
+ return buttons;
65
+ };
66
+
67
+ /**
68
+ * Alert 템플릿 모달 상태 생성.
69
+ * @component
70
+ * @param {AlertTemplateOptions} options Alert 모달 옵션
71
+ * @param {string} options.stackKey 모달 스택 키
72
+ * @param {React.ReactNode} options.message 본문 메시지
73
+ * @param {ModalTemplateButtonSpec} [options.confirm] 확인 버튼 스펙
74
+ * @param {ModalTemplateButtonSpec} [options.cancel] 취소 버튼 스펙
75
+ * @param {React.ReactNode} [options.footer] 완전 커스텀 footer
76
+ * @param {number} [options.showDelay] close 후 제거 지연(ms)
77
+ * @returns {ModalState}
78
+ */
79
+ export function createAlertModal({
80
+ stackKey,
81
+ message,
82
+ confirm,
83
+ cancel,
84
+ footer,
85
+ showDelay,
86
+ }: AlertTemplateOptions): ModalState {
87
+ const primary: ModalTemplateButtonSpec = confirm ?? {
88
+ label: DEFAULT_CONFIRM_LABEL,
89
+ };
90
+
91
+ return {
92
+ stackKey,
93
+ modalProps: {
94
+ show: "init",
95
+ showDelay,
96
+ body: <AlertBody message={message} />,
97
+ footer,
98
+ footerButtons:
99
+ footer || !primary
100
+ ? undefined
101
+ : createAlertFooterButtons({ stackKey, primary, cancel }),
102
+ },
103
+ };
104
+ }
@@ -0,0 +1,112 @@
1
+ "use client";
2
+
3
+ import type {
4
+ DialogTemplateOptions,
5
+ ModalFooterButton,
6
+ ModalState,
7
+ ModalTemplateButtonSpec,
8
+ } from "../types";
9
+
10
+ const DEFAULT_DIALOG_CONFIRM = "확인";
11
+ const DEFAULT_DIALOG_CANCEL = "취소";
12
+
13
+ function DialogHeader({ title }: { title?: DialogTemplateOptions["title"] }) {
14
+ if (!title) return null;
15
+
16
+ return (
17
+ <div className="uds-modal-dialog-header">
18
+ <div className="uds-modal-dialog-header-content">
19
+ {typeof title === "string" ? <h3>{title}</h3> : title}
20
+ </div>
21
+ </div>
22
+ );
23
+ }
24
+
25
+ function DialogBody({
26
+ content,
27
+ }: {
28
+ content: DialogTemplateOptions["content"];
29
+ }) {
30
+ return (
31
+ <div className="uds-modal-dialog-body">
32
+ <div className="uds-modal-dialog-body-content">
33
+ {typeof content === "string" ? <p>{content}</p> : content}
34
+ </div>
35
+ </div>
36
+ );
37
+ }
38
+
39
+ /**
40
+ * Dialog 템플릿 모달 상태 생성.
41
+ * @component
42
+ * @param {DialogTemplateOptions} options Dialog 모달 옵션
43
+ * @param {string} options.stackKey 모달 스택 키
44
+ * @param {React.ReactNode} [options.title] 헤더 타이틀
45
+ * @param {React.ReactNode} options.content 본문 컨텐츠
46
+ * @param {ModalTemplateButtonSpec} [options.confirm] 확인 버튼 스펙
47
+ * @param {ModalTemplateButtonSpec} [options.cancel] 취소 버튼 스펙
48
+ * @param {React.ReactNode} [options.footer] 커스텀 footer
49
+ * @param {number} [options.showDelay] close 후 제거 지연(ms)
50
+ * @returns {ModalState}
51
+ */
52
+ export function createDialogModal({
53
+ stackKey,
54
+ title,
55
+ content,
56
+ confirm,
57
+ cancel,
58
+ footer,
59
+ showDelay,
60
+ }: DialogTemplateOptions): ModalState {
61
+ const primary: ModalTemplateButtonSpec = confirm ?? {
62
+ label: DEFAULT_DIALOG_CONFIRM,
63
+ };
64
+
65
+ const footerButtons: ModalFooterButton[] =
66
+ primary && !footer
67
+ ? [
68
+ ...(cancel
69
+ ? [
70
+ {
71
+ stackKey,
72
+ role: "close",
73
+ // cancel 버튼은 secondary solid 규격으로 일관된 스킨을 유지한다.
74
+ defaultOptions: {
75
+ label: cancel.label ?? DEFAULT_DIALOG_CANCEL,
76
+ scale: "solid-large",
77
+ priority: "secondary",
78
+ disabled: cancel.disabled,
79
+ onClick: cancel.onClick,
80
+ },
81
+ closeOnClick: cancel.closeOnClick,
82
+ } as ModalFooterButton,
83
+ ]
84
+ : []),
85
+ {
86
+ stackKey,
87
+ role: "close",
88
+ // confirm 버튼은 primary solid 규격으로 고정한다.
89
+ defaultOptions: {
90
+ label: primary.label ?? DEFAULT_DIALOG_CONFIRM,
91
+ scale: "solid-large",
92
+ priority: "primary",
93
+ disabled: primary.disabled,
94
+ onClick: primary.onClick,
95
+ },
96
+ closeOnClick: primary.closeOnClick,
97
+ },
98
+ ]
99
+ : [];
100
+
101
+ return {
102
+ stackKey,
103
+ modalProps: {
104
+ show: "init",
105
+ showDelay,
106
+ header: <DialogHeader title={title} />,
107
+ body: <DialogBody content={content} />,
108
+ footer,
109
+ footerButtons: footerButtons.length ? footerButtons : undefined,
110
+ },
111
+ };
112
+ }
@@ -0,0 +1,36 @@
1
+ import type { ReactNode } from "react";
2
+ import type {
3
+ ButtonPriority,
4
+ ButtonScale,
5
+ TextButtonSize,
6
+ } from "@uniai-fe/uds-primitives";
7
+
8
+ import type { ModalStackKey } from "./state";
9
+
10
+ export type ModalFooterButtonAppearance = "default" | "text";
11
+
12
+ export type ModalFooterButtonDefaultOptions = {
13
+ label: ReactNode;
14
+ onClick?: () => void;
15
+ disabled?: boolean;
16
+ block?: boolean;
17
+ priority?: ButtonPriority;
18
+ scale?: ButtonScale;
19
+ textSize?: TextButtonSize;
20
+ appearance?: ModalFooterButtonAppearance;
21
+ className?: string;
22
+ };
23
+
24
+ export type ModalFooterButtonLinkOptions = {
25
+ href: string;
26
+ target?: string;
27
+ rel?: string;
28
+ };
29
+
30
+ export type ModalFooterButton = {
31
+ stackKey: ModalStackKey;
32
+ role: "close" | string;
33
+ defaultOptions: ModalFooterButtonDefaultOptions;
34
+ linkOptions?: ModalFooterButtonLinkOptions;
35
+ closeOnClick?: boolean;
36
+ };
@@ -0,0 +1,21 @@
1
+ export type {
2
+ ModalShowState,
3
+ ModalStackKey,
4
+ ModalSections,
5
+ ModalProps,
6
+ ModalState,
7
+ ModalStatePatch,
8
+ } from "./state";
9
+ export type {
10
+ ModalFooterButton,
11
+ ModalFooterButtonDefaultOptions,
12
+ ModalFooterButtonLinkOptions,
13
+ ModalFooterButtonAppearance,
14
+ } from "./footer";
15
+ export type { ModalCloseRequest } from "./options";
16
+ export type {
17
+ AlertTemplateOptions,
18
+ DialogTemplateOptions,
19
+ ModalTemplateButtonSpec,
20
+ ModalTemplateResult,
21
+ } from "./templates";
@@ -0,0 +1,6 @@
1
+ import type { ModalStackKey } from "./state";
2
+
3
+ export type ModalCloseRequest = {
4
+ stackKey: ModalStackKey;
5
+ callback?: () => void;
6
+ };
@@ -0,0 +1,31 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ import type { ModalFooterButton } from "./footer";
4
+
5
+ export type ModalShowState = "init" | boolean;
6
+
7
+ export type ModalStackKey = string;
8
+
9
+ export type ModalSections = {
10
+ header?: ReactNode;
11
+ body: ReactNode;
12
+ footer?: ReactNode;
13
+ };
14
+
15
+ export type ModalProps = ModalSections & {
16
+ show: ModalShowState;
17
+ showDelay?: number;
18
+ footerButtons?: ModalFooterButton[];
19
+ };
20
+
21
+ export type ModalState = {
22
+ stackKey: ModalStackKey;
23
+ modalProps: ModalProps;
24
+ className?: string;
25
+ };
26
+
27
+ export type ModalStatePatch = {
28
+ stackKey: ModalStackKey;
29
+ modalProps?: Partial<ModalProps>;
30
+ className?: string;
31
+ };
@@ -0,0 +1,32 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ import type { ModalState, ModalStackKey } from "./state";
4
+
5
+ export type ModalTemplateButtonSpec = {
6
+ label?: ReactNode;
7
+ onClick?: () => void;
8
+ closeOnClick?: boolean;
9
+ disabled?: boolean;
10
+ };
11
+
12
+ type ModalTemplateBase = {
13
+ stackKey: ModalStackKey;
14
+ showDelay?: number;
15
+ };
16
+
17
+ export type AlertTemplateOptions = ModalTemplateBase & {
18
+ message: ReactNode;
19
+ confirm?: ModalTemplateButtonSpec;
20
+ cancel?: ModalTemplateButtonSpec;
21
+ footer?: ReactNode;
22
+ };
23
+
24
+ export type DialogTemplateOptions = ModalTemplateBase & {
25
+ title?: ReactNode;
26
+ content: ReactNode;
27
+ confirm?: ModalTemplateButtonSpec;
28
+ cancel?: ModalTemplateButtonSpec;
29
+ footer?: ReactNode;
30
+ };
31
+
32
+ export type ModalTemplateResult = ModalState;
package/src/index.scss CHANGED
@@ -4,3 +4,4 @@
4
4
  @use "./components/page-frame/container/index.scss" as *;
5
5
  @use "./components/page-frame/mobile/index.scss" as *;
6
6
  @use "./components/page-frame/navigation/index.scss" as *;
7
+ @use "./components/modal/index.scss" as *;
package/src/index.tsx CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from "./components/page-frame";
2
2
  export * from "./components/auth";
3
+ export * from "./components/modal";