@uniai-fe/uds-templates 0.3.3 → 0.3.5

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 (32) hide show
  1. package/dist/styles.css +7 -5
  2. package/package.json +8 -7
  3. package/src/modal/components/alert/Contents.tsx +28 -0
  4. package/src/modal/components/alert/Template.tsx +59 -0
  5. package/src/modal/components/core/Body.tsx +23 -0
  6. package/src/modal/components/core/Container.tsx +59 -0
  7. package/src/modal/{core/components → components/core}/Root.tsx +12 -10
  8. package/src/modal/{core/components → components/core}/RouteReset.tsx +4 -1
  9. package/src/modal/{core/components/Provider.tsx → components/core/StackProvider.tsx} +12 -6
  10. package/src/modal/{core/components/FooterPositionButton.tsx → components/core/footer/Button.tsx} +13 -4
  11. package/src/modal/components/core/footer/ButtonGroup.tsx +34 -0
  12. package/src/modal/{core/components/FooterButtons.tsx → components/core/footer/ButtonWrapper.tsx} +28 -19
  13. package/src/modal/components/core/header/CloseButton.tsx +32 -0
  14. package/src/modal/components/core/header/Container.tsx +21 -0
  15. package/src/modal/components/dialog/Contents.tsx +32 -0
  16. package/src/modal/{templates/components/DialogHeader.tsx → components/dialog/Header.tsx} +17 -5
  17. package/src/modal/components/dialog/Template.tsx +86 -0
  18. package/src/modal/components/index.tsx +27 -0
  19. package/src/modal/{core/hooks → hooks}/useModal.ts +16 -13
  20. package/src/modal/index.tsx +12 -13
  21. package/src/modal/{core/jotai → jotai}/atoms.ts +2 -2
  22. package/src/modal/styles/container.scss +14 -5
  23. package/src/modal/types/components.ts +139 -4
  24. package/src/modal/types/index.ts +7 -1
  25. package/src/modal/utils/create-alert-footer-buttons.ts +59 -0
  26. package/src/modal/utils/create-dialog-footer-buttons.ts +72 -0
  27. package/src/page-frame/desktop/components/header/util/setting/Button.tsx +7 -11
  28. package/src/modal/core/components/Container.tsx +0 -55
  29. package/src/modal/core/components/FooterPositionGroup.tsx +0 -29
  30. package/src/modal/templates/Alert.tsx +0 -115
  31. package/src/modal/templates/Dialog.tsx +0 -127
  32. package/src/modal/templates/components/DialogBody.tsx +0 -20
@@ -5,10 +5,17 @@ import clsx from "clsx";
5
5
  import type { DialogHeaderProps } from "../../types";
6
6
 
7
7
  /**
8
- * Dialog 템플릿 header 컴포넌트.
8
+ * Modal Dialog Header; Dialog 헤더 영역
9
9
  * @component
10
- * @param {DialogHeaderProps} props header 구성 옵션
11
- * @returns {JSX.Element}
10
+ * @param {DialogHeaderProps} props
11
+ * @param {React.ReactNode} props.title 타이틀 콘텐츠
12
+ * @param {React.ReactNode} [props.description] description 콘텐츠
13
+ * @param {"center" | "split"} [props.layout] 헤더 레이아웃
14
+ * @param {React.ReactNode} [props.leadingContent] 타이틀 왼쪽 콘텐츠
15
+ * @param {React.ReactNode} [props.trailingContent] 우측 액션 콘텐츠
16
+ * @param {string} [props.className] 헤더 className
17
+ * @example
18
+ * <DialogHeader title="제목" description="설명" />
12
19
  */
13
20
  export function DialogHeader({
14
21
  title,
@@ -20,6 +27,11 @@ export function DialogHeader({
20
27
  }: DialogHeaderProps) {
21
28
  const hasRight = Boolean(trailingContent);
22
29
  const hasDescription = Boolean(description);
30
+ // 변경: title/description은 string | number일 때만 래핑한다.
31
+ const shouldWrapTitleAsText = ["string", "number"].includes(typeof title);
32
+ const shouldWrapDescriptionAsText = ["string", "number"].includes(
33
+ typeof description,
34
+ );
23
35
 
24
36
  return (
25
37
  <header
@@ -41,7 +53,7 @@ export function DialogHeader({
41
53
  </div>
42
54
  ) : null}
43
55
  <div className="uds-modal-dialog-header-title">
44
- {typeof title === "string" ? <h3>{title}</h3> : title}
56
+ {shouldWrapTitleAsText ? <h3>{title}</h3> : title}
45
57
  </div>
46
58
  </div>
47
59
  {hasRight ? (
@@ -52,7 +64,7 @@ export function DialogHeader({
52
64
  </div>
53
65
  {description ? (
54
66
  <p className="uds-modal-dialog-header-description">
55
- {typeof description === "string" ? (
67
+ {shouldWrapDescriptionAsText ? (
56
68
  <span>{description}</span>
57
69
  ) : (
58
70
  description
@@ -0,0 +1,86 @@
1
+ "use client";
2
+
3
+ import { DialogHeader } from "./Header";
4
+ import { DialogContents } from "./Contents";
5
+ import {
6
+ createDialogFooterButtons,
7
+ resolveDialogPrimaryButton,
8
+ } from "../../utils/create-dialog-footer-buttons";
9
+
10
+ import type { DialogTemplateOptions, ModalState } from "../../types";
11
+
12
+ /**
13
+ * Modal Dialog Template; Dialog 모달 상태 생성기
14
+ * @component
15
+ * @param {DialogTemplateOptions} options Dialog 모달 옵션
16
+ * @param {string} options.stackKey 모달 스택 키
17
+ * @param {React.ReactNode} options.title 헤더 타이틀
18
+ * @param {React.ReactNode} [options.description] 헤더 description
19
+ * @param {"center" | "split"} [options.headerLayout] 헤더 레이아웃
20
+ * @param {React.ReactNode} [options.headerLeadingContent] 타이틀 왼쪽 콘텐츠
21
+ * @param {React.ReactNode} [options.headerTrailingContent] 우측 액션 콘텐츠
22
+ * @param {string} [options.headerClassName] 헤더 className
23
+ * @param {React.ReactNode} [options.customHeader] 완전 커스텀 header
24
+ * @param {React.ReactNode} options.content 본문 콘텐츠
25
+ * @param {string} [options.bodyClassName] body wrapper className
26
+ * @param {ModalTemplateButtonSpec} [options.confirm] 확인 버튼 스펙
27
+ * @param {ModalTemplateButtonSpec} [options.cancel] 취소 버튼 스펙
28
+ * @param {React.ReactNode} [options.footer] 커스텀 footer
29
+ * @param {number} [options.showDelay] close 후 제거 지연(ms)
30
+ * @param {number | string} [options.width] 모달 width
31
+ * @param {StyleSpacingType} [options.padding] 모달 body padding
32
+ * @returns {ModalState}
33
+ * @example
34
+ * Modal.Dialog({ stackKey: "sample", title: "제목", content: "본문" })
35
+ */
36
+ export function createDialogModal({
37
+ stackKey,
38
+ title,
39
+ description,
40
+ headerLayout,
41
+ headerLeadingContent,
42
+ headerTrailingContent,
43
+ headerClassName,
44
+ customHeader,
45
+ content,
46
+ bodyClassName,
47
+ confirm,
48
+ cancel,
49
+ footer,
50
+ showDelay,
51
+ width,
52
+ padding,
53
+ }: DialogTemplateOptions): ModalState {
54
+ const primary = resolveDialogPrimaryButton(confirm);
55
+
56
+ const headerNode =
57
+ customHeader ??
58
+ (title ? (
59
+ <DialogHeader
60
+ title={title}
61
+ description={description}
62
+ layout={headerLayout}
63
+ leadingContent={headerLeadingContent}
64
+ trailingContent={headerTrailingContent}
65
+ className={headerClassName}
66
+ />
67
+ ) : null);
68
+
69
+ const footerButtons = footer
70
+ ? undefined
71
+ : createDialogFooterButtons({ stackKey, primary, cancel });
72
+
73
+ return {
74
+ stackKey,
75
+ modalProps: {
76
+ show: "init",
77
+ showDelay,
78
+ width,
79
+ padding,
80
+ header: headerNode ?? undefined,
81
+ body: <DialogContents content={content} className={bodyClassName} />,
82
+ footer,
83
+ footerButtons,
84
+ },
85
+ };
86
+ }
@@ -0,0 +1,27 @@
1
+ import { ModalProvider, ModalStackProvider } from "./core/StackProvider";
2
+ import { ModalRouteReset } from "./core/RouteReset";
3
+ import { createAlertModal } from "./alert/Template";
4
+ import { createDialogModal } from "./dialog/Template";
5
+
6
+ import { useModal } from "../hooks/useModal";
7
+
8
+ /**
9
+ * Modal Namespace; 모달 네임스페이스 집계
10
+ */
11
+ export const ModalNamespace = {
12
+ Provider: ModalProvider,
13
+ StackProvider: ModalStackProvider,
14
+ RouteReset: ModalRouteReset,
15
+ useModal,
16
+ Alert: createAlertModal,
17
+ Dialog: createDialogModal,
18
+ };
19
+
20
+ export {
21
+ ModalProvider,
22
+ ModalStackProvider,
23
+ ModalRouteReset,
24
+ useModal,
25
+ createAlertModal,
26
+ createDialogModal,
27
+ };
@@ -1,9 +1,8 @@
1
1
  "use client";
2
2
 
3
3
  import { useCallback, useEffect, useState } from "react";
4
- import { useAtom } from "jotai";
5
4
 
6
- import { modalStackAtom } from "../jotai/atoms";
5
+ import { useAtom } from "jotai";
7
6
 
8
7
  import type {
9
8
  CloseFlagState,
@@ -11,7 +10,8 @@ import type {
11
10
  ModalState,
12
11
  ModalStatePatch,
13
12
  UseModalReturn,
14
- } from "../../types";
13
+ } from "../types";
14
+ import { modalStackAtom } from "../jotai/atoms";
15
15
 
16
16
  const DEFAULT_CLOSE_DELAY = 400;
17
17
 
@@ -19,14 +19,11 @@ const ensureDelay = (value?: number): number =>
19
19
  typeof value === "number" ? value : DEFAULT_CLOSE_DELAY;
20
20
 
21
21
  /**
22
- * ui-legacy 스펙을 최대한 유지한 모달 스택 훅.
22
+ * Modal Hook; ui-legacy 스택 정책 유지
23
23
  * @hook
24
- * @returns {UseModalReturn} 스택 상태와 조작자(new/open/close)
25
- * @desc
26
- * - modalStacks: 현재 열린 스택 배열
27
- * - newModal: stackKey 중복 검증 후 "init" 상태로 푸시
28
- * - updateModal: 부분 업데이트(클래스/슬롯 변경)
29
- * - closeModal: show=false → delay 후 제거
24
+ * @returns {UseModalReturn} 모달 스택 상태/조작자
25
+ * @example
26
+ * const { newModal, closeModal } = useModal();
30
27
  */
31
28
  export function useModal(): UseModalReturn {
32
29
  const [modalStacks, updateModalStack] = useAtom(modalStackAtom);
@@ -60,7 +57,9 @@ export function useModal(): UseModalReturn {
60
57
  (nextStack: ModalStatePatch) => {
61
58
  updateModalStack((stacks: ModalState[]) =>
62
59
  stacks.map(stack => {
63
- if (stack.stackKey !== nextStack.stackKey) return stack;
60
+ if (stack.stackKey !== nextStack.stackKey) {
61
+ return stack;
62
+ }
64
63
  const mergedProps = nextStack.modalProps
65
64
  ? { ...stack.modalProps, ...nextStack.modalProps }
66
65
  : stack.modalProps;
@@ -84,7 +83,9 @@ export function useModal(): UseModalReturn {
84
83
 
85
84
  updateModalStack((stacks: ModalState[]) =>
86
85
  stacks.map(stack => {
87
- if (stack.stackKey !== stackKey) return stack;
86
+ if (stack.stackKey !== stackKey) {
87
+ return stack;
88
+ }
88
89
  hasTarget = true;
89
90
  resolvedDelay = ensureDelay(stack.modalProps.showDelay);
90
91
  return {
@@ -107,7 +108,9 @@ export function useModal(): UseModalReturn {
107
108
  );
108
109
 
109
110
  useEffect(() => {
110
- if (!closeFlag) return;
111
+ if (!closeFlag) {
112
+ return;
113
+ }
111
114
 
112
115
  const timer = setTimeout(() => {
113
116
  updateModalStack((stacks: ModalState[]) =>
@@ -1,22 +1,21 @@
1
1
  import "./index.scss";
2
2
 
3
- import { ModalProvider } from "./core/components/Provider";
4
- import { ModalRouteReset } from "./core/components/RouteReset";
5
- import { useModal } from "./core/hooks/useModal";
6
- import { createAlertModal } from "./templates/Alert";
7
- import { createDialogModal } from "./templates/Dialog";
8
- import { modalStackAtom } from "./core/jotai/atoms";
9
-
10
- export const Modal = {
11
- Provider: ModalProvider,
12
- RouteReset: ModalRouteReset,
3
+ import { modalStackAtom } from "./jotai/atoms";
4
+ import {
5
+ ModalNamespace,
6
+ ModalProvider,
7
+ ModalStackProvider,
8
+ ModalRouteReset,
13
9
  useModal,
14
- Alert: createAlertModal,
15
- Dialog: createDialogModal,
16
- };
10
+ createAlertModal,
11
+ createDialogModal,
12
+ } from "./components";
13
+
14
+ export const Modal = ModalNamespace;
17
15
 
18
16
  export {
19
17
  ModalProvider,
18
+ ModalStackProvider,
20
19
  ModalRouteReset,
21
20
  useModal,
22
21
  createAlertModal,
@@ -1,9 +1,9 @@
1
1
  import { atom } from "jotai";
2
2
 
3
- import type { ModalState } from "../../types";
3
+ import type { ModalState } from "../types";
4
4
 
5
5
  /**
6
- * 모달 스택 상태
6
+ * Modal Atom; 모달 스택 상태 atom
7
7
  * @state
8
8
  * @desc ui-legacy 스택 정책을 유지한다.
9
9
  */
@@ -116,7 +116,10 @@
116
116
 
117
117
  color: var(--modal-alert-body-color);
118
118
 
119
- :where(p, span, strong, em) {
119
+ // 변경: alert 본문은 직계 + class 없는 텍스트 태그에만 기본 타이포를 적용한다.
120
+ // 복합 모듈(children) 내부의 텍스트 스타일 오염을 방지하기 위함이다.
121
+ > :where(p, span, strong, em):not([class]) {
122
+ margin: 0;
120
123
  font-size: var(--modal-alert-body-font-size);
121
124
  line-height: 1.5em;
122
125
  font-weight: var(--font-body-small-weight, 400);
@@ -165,7 +168,8 @@
165
168
  align-items: center;
166
169
  gap: var(--spacing-gap-1, 4px);
167
170
 
168
- :where(p, span, strong, em) {
171
+ // 변경: header leading slot은 class 없는 직계 텍스트만 기본 타이포를 적용한다.
172
+ > :where(p, span, strong, em):not([class]) {
169
173
  color: var(--modal-dialog-title-color);
170
174
  font-size: var(--modal-dialog-body-font-size);
171
175
  line-height: 1.4em;
@@ -178,7 +182,8 @@
178
182
  justify-content: center;
179
183
  text-align: center;
180
184
 
181
- > :where(h1, h2, h3, h4, h5, h6, p, span, strong, em) {
185
+ // 변경: className이 있는 헤더 텍스트 요소는 모듈 자체 스타일을 우선한다.
186
+ > :where(h1, h2, h3, h4, h5, h6, p, span, strong, em):not([class]) {
182
187
  margin: 0;
183
188
  color: var(--modal-dialog-title-color);
184
189
  font-size: var(--modal-dialog-title-font-size);
@@ -197,7 +202,8 @@
197
202
  margin: 0;
198
203
  text-align: inherit;
199
204
 
200
- > :where(p, span, strong, em) {
205
+ // 변경: description도 class 없는 직계 텍스트에만 기본 스타일을 적용한다.
206
+ > :where(p, span, strong, em):not([class]) {
201
207
  margin: 0;
202
208
  color: var(--modal-dialog-body-color);
203
209
  font-size: var(--modal-dialog-body-font-size);
@@ -227,7 +233,10 @@
227
233
  word-break: keep-all;
228
234
  color: var(--modal-dialog-body-color);
229
235
 
230
- :where(p, span, strong, em) {
236
+ // 변경: body 전체 하위 요소가 아닌 직계 텍스트 요소만 기본 타이포를 적용해
237
+ // 내부 모듈 컴포넌트의 span/p 스타일 오염을 방지한다.
238
+ > :where(p, span, strong, em):not([class]) {
239
+ margin: 0;
231
240
  font-size: var(--modal-dialog-body-font-size);
232
241
  line-height: 1.5em;
233
242
  font-weight: var(--font-body-small-weight, 400);
@@ -1,4 +1,4 @@
1
- import type { ReactNode } from "react";
1
+ import type { CSSProperties, ReactNode } from "react";
2
2
  import type {
3
3
  ModalFooterButtonAppearance,
4
4
  ModalFooterButtonDefaultOptions,
@@ -6,7 +6,8 @@ import type {
6
6
  ModalFooterButtonWidth,
7
7
  } from "./footer";
8
8
  import type { ModalDialogHeaderLayout } from "./templates";
9
- import type { ModalProps, ModalState } from "./state";
9
+ import type { ModalProps, ModalState, ModalSections } from "./state";
10
+ import type { ModalFooterButton } from "./footer";
10
11
 
11
12
  /**
12
13
  * ModalRoot 컴포넌트 props.
@@ -34,6 +35,88 @@ export type ModalRootProps = ModalState & {
34
35
  className?: string;
35
36
  };
36
37
 
38
+ /**
39
+ * Modal Container props.
40
+ * @property {string} stackKey 모달 스택 식별자
41
+ * @property {ReactNode} [header] 헤더 콘텐츠
42
+ * @property {ReactNode} body 본문 콘텐츠
43
+ * @property {ReactNode} [footer] footer 콘텐츠
44
+ * @property {ModalFooterButton[]} [footerButtons] footer 버튼 목록
45
+ * @property {string} [className] 컨테이너 className
46
+ */
47
+ export type ModalContainerProps = ModalSections & {
48
+ /**
49
+ * 모달 스택 식별자
50
+ */
51
+ stackKey: string;
52
+ /**
53
+ * footer 버튼 목록
54
+ */
55
+ footerButtons?: ModalFooterButton[];
56
+ /**
57
+ * 컨테이너 className
58
+ */
59
+ className?: string;
60
+ };
61
+
62
+ /**
63
+ * Modal Body props.
64
+ * @property {ReactNode} children body 내부 콘텐츠
65
+ * @property {CSSProperties} [style] 런타임 주입 스타일
66
+ * @property {string} [className] body className
67
+ */
68
+ export type ModalBodyProps = {
69
+ /**
70
+ * body 내부 콘텐츠
71
+ */
72
+ children: ReactNode;
73
+ /**
74
+ * 런타임 주입 스타일
75
+ */
76
+ style?: CSSProperties;
77
+ /**
78
+ * body className
79
+ */
80
+ className?: string;
81
+ };
82
+
83
+ /**
84
+ * Modal Header Container props.
85
+ * @property {ReactNode} children 헤더 콘텐츠
86
+ * @property {string} [className] 헤더 wrapper className
87
+ */
88
+ export type ModalHeaderContainerProps = {
89
+ /**
90
+ * 헤더 콘텐츠
91
+ */
92
+ children: ReactNode;
93
+ /**
94
+ * 헤더 wrapper className
95
+ */
96
+ className?: string;
97
+ };
98
+
99
+ /**
100
+ * Modal Header CloseButton props.
101
+ * @property {() => void} onClick 클릭 핸들러
102
+ * @property {string} [ariaLabel] 접근성 라벨
103
+ * @property {string} [className] 버튼 className
104
+ */
105
+ export type ModalHeaderCloseButtonProps = {
106
+ /**
107
+ * 클릭 핸들러
108
+ */
109
+ onClick: () => void;
110
+ /**
111
+ * 접근성 라벨
112
+ */
113
+ ariaLabel?: string;
114
+ /**
115
+ * 버튼 className
116
+ */
117
+ className?: string;
118
+ };
119
+
37
120
  /**
38
121
  * footer 위치 정의.
39
122
  * @typedef {"left" | "center" | "right"} FooterPosition
@@ -90,6 +173,7 @@ export type FooterResolvedButton = {
90
173
  * FooterPositionGroup props.
91
174
  * @property {FooterPosition} position 렌더링 위치
92
175
  * @property {FooterResolvedButton[]} buttons 위치별 버튼 목록
176
+ * @property {string} [className] 그룹 className
93
177
  */
94
178
  export type FooterPositionGroupProps = {
95
179
  /**
@@ -100,17 +184,63 @@ export type FooterPositionGroupProps = {
100
184
  * 위치별 버튼 목록
101
185
  */
102
186
  buttons: FooterResolvedButton[];
187
+ /**
188
+ * 그룹 className
189
+ */
190
+ className?: string;
103
191
  };
104
192
 
105
193
  /**
106
194
  * FooterPositionButton props.
107
195
  * @property {FooterResolvedButton} button 렌더링할 버튼
196
+ * @property {string} [className] 버튼 className
108
197
  */
109
198
  export type FooterPositionButtonProps = {
110
199
  /**
111
200
  * 렌더링할 버튼
112
201
  */
113
202
  button: FooterResolvedButton;
203
+ /**
204
+ * 버튼 className
205
+ */
206
+ className?: string;
207
+ };
208
+
209
+ /**
210
+ * Footer 버튼 래퍼 props.
211
+ * @property {string} stackKey 버튼이 속한 모달 스택 키
212
+ * @property {ModalFooterButton[]} buttons footer 버튼 정의 목록
213
+ * @property {string} [className] wrapper className
214
+ */
215
+ export type FooterButtonWrapperProps = {
216
+ /**
217
+ * 버튼이 속한 모달 스택 키
218
+ */
219
+ stackKey: string;
220
+ /**
221
+ * footer 버튼 정의 목록
222
+ */
223
+ buttons: ModalFooterButton[];
224
+ /**
225
+ * wrapper className
226
+ */
227
+ className?: string;
228
+ };
229
+
230
+ /**
231
+ * AlertContents props.
232
+ * @property {ReactNode} message alert 본문 메시지
233
+ * @property {string} [className] 콘텐츠 wrapper className
234
+ */
235
+ export type AlertContentsProps = {
236
+ /**
237
+ * alert 본문 메시지
238
+ */
239
+ message: ReactNode;
240
+ /**
241
+ * 콘텐츠 wrapper className
242
+ */
243
+ className?: string;
114
244
  };
115
245
 
116
246
  /**
@@ -150,11 +280,12 @@ export interface DialogHeaderProps {
150
280
  }
151
281
 
152
282
  /**
153
- * DialogBody props.
283
+ * DialogContents props.
154
284
  * @property {ReactNode} content 본문 콘텐츠
155
285
  * @property {string} [className] wrapper className
286
+ * @property {string} [contentClassName] 본문 콘텐츠 className
156
287
  */
157
- export interface DialogBodyProps {
288
+ export interface DialogContentsProps {
158
289
  /**
159
290
  * 본문 콘텐츠
160
291
  */
@@ -163,4 +294,8 @@ export interface DialogBodyProps {
163
294
  * wrapper className
164
295
  */
165
296
  className?: string;
297
+ /**
298
+ * 본문 콘텐츠 className
299
+ */
300
+ contentClassName?: string;
166
301
  }
@@ -24,10 +24,16 @@ export type {
24
24
  export type { CloseFlagState, UseModalReturn } from "./hooks";
25
25
  export type {
26
26
  ModalRootProps,
27
+ ModalContainerProps,
28
+ ModalBodyProps,
29
+ ModalHeaderContainerProps,
30
+ ModalHeaderCloseButtonProps,
27
31
  FooterPosition,
28
32
  FooterResolvedButton,
33
+ FooterButtonWrapperProps,
29
34
  FooterPositionGroupProps,
30
35
  FooterPositionButtonProps,
36
+ AlertContentsProps,
31
37
  DialogHeaderProps,
32
- DialogBodyProps,
38
+ DialogContentsProps,
33
39
  } from "./components";
@@ -0,0 +1,59 @@
1
+ import type { ModalFooterButton, ModalTemplateButtonSpec } from "../types";
2
+
3
+ const DEFAULT_CONFIRM_LABEL = "확인";
4
+ const DEFAULT_CANCEL_LABEL = "취소";
5
+
6
+ /**
7
+ * Modal Alert Footer Buttons; Alert footer 버튼 스펙 생성
8
+ * @param {string} stackKey 모달 스택 키
9
+ * @param {ModalTemplateButtonSpec} primary 확인 버튼 스펙
10
+ * @param {ModalTemplateButtonSpec} [cancel] 취소 버튼 스펙
11
+ * @returns {ModalFooterButton[]}
12
+ */
13
+ export const createAlertFooterButtons = ({
14
+ stackKey,
15
+ primary,
16
+ cancel,
17
+ }: {
18
+ stackKey: string;
19
+ primary: ModalTemplateButtonSpec;
20
+ cancel?: ModalTemplateButtonSpec;
21
+ }): ModalFooterButton[] => {
22
+ const buttons: ModalFooterButton[] = [];
23
+
24
+ if (cancel) {
25
+ buttons.push({
26
+ stackKey,
27
+ role: "close",
28
+ position: cancel.position ?? "center",
29
+ width: cancel.width === "full" || !cancel.width ? "fill" : cancel.width,
30
+ defaultOptions: {
31
+ label: cancel.label ?? DEFAULT_CANCEL_LABEL,
32
+ appearance: "text",
33
+ priority: "tertiary",
34
+ textSize: "large",
35
+ disabled: cancel.disabled,
36
+ onClick: cancel.onClick,
37
+ },
38
+ closeOnClick: cancel.closeOnClick,
39
+ });
40
+ }
41
+
42
+ buttons.push({
43
+ stackKey,
44
+ role: "close",
45
+ position: primary.position ?? "center",
46
+ width: primary.width === "full" || !primary.width ? "fill" : primary.width,
47
+ defaultOptions: {
48
+ label: primary.label ?? DEFAULT_CONFIRM_LABEL,
49
+ appearance: "text",
50
+ priority: "secondary",
51
+ textSize: "large",
52
+ disabled: primary.disabled,
53
+ onClick: primary.onClick,
54
+ },
55
+ closeOnClick: primary.closeOnClick,
56
+ });
57
+
58
+ return buttons;
59
+ };
@@ -0,0 +1,72 @@
1
+ import type {
2
+ DialogTemplateOptions,
3
+ ModalFooterButton,
4
+ ModalTemplateButtonSpec,
5
+ } from "../types";
6
+
7
+ /**
8
+ * Modal Dialog Footer Buttons; Dialog footer 버튼 스펙 생성
9
+ * @param {string} stackKey 모달 스택 키
10
+ * @param {ModalTemplateButtonSpec} primary 확인 버튼 스펙
11
+ * @param {ModalTemplateButtonSpec} [cancel] 취소 버튼 스펙
12
+ * @returns {ModalFooterButton[]}
13
+ */
14
+ export const createDialogFooterButtons = ({
15
+ stackKey,
16
+ primary,
17
+ cancel,
18
+ }: {
19
+ stackKey: string;
20
+ primary: ModalTemplateButtonSpec;
21
+ cancel?: ModalTemplateButtonSpec;
22
+ }): ModalFooterButton[] => {
23
+ const buttons: ModalFooterButton[] = [];
24
+
25
+ if (cancel) {
26
+ buttons.push({
27
+ stackKey,
28
+ role: "close",
29
+ position: cancel.position,
30
+ width: cancel.width,
31
+ defaultOptions: {
32
+ label: cancel.label ?? "취소",
33
+ fill: "outlined",
34
+ size: "large",
35
+ priority: "secondary",
36
+ disabled: cancel.disabled,
37
+ onClick: cancel.onClick,
38
+ },
39
+ closeOnClick: cancel.closeOnClick,
40
+ });
41
+ }
42
+
43
+ buttons.push({
44
+ stackKey,
45
+ role: "close",
46
+ position: primary.position,
47
+ width: primary.width,
48
+ defaultOptions: {
49
+ label: primary.label ?? "확인",
50
+ fill: "solid",
51
+ size: "large",
52
+ priority: "primary",
53
+ disabled: primary.disabled,
54
+ onClick: primary.onClick,
55
+ },
56
+ closeOnClick: primary.closeOnClick,
57
+ });
58
+
59
+ return buttons;
60
+ };
61
+
62
+ /**
63
+ * Modal Dialog Primary Button; Dialog 기본 확인 버튼 스펙 생성
64
+ * @param {DialogTemplateOptions["confirm"]} confirm 확인 버튼 옵션
65
+ * @returns {ModalTemplateButtonSpec}
66
+ */
67
+ export const resolveDialogPrimaryButton = (
68
+ confirm: DialogTemplateOptions["confirm"],
69
+ ): ModalTemplateButtonSpec =>
70
+ confirm ?? {
71
+ label: "확인",
72
+ };