@uniai-fe/uds-templates 0.3.14 → 0.3.15

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 CHANGED
@@ -142,7 +142,7 @@ ui-legacy에서 사용하던 모달 스택/옵션을 templates 레이어로 옮
142
142
 
143
143
  1. **Provider 1회 장착** — 서비스 레이아웃에서 `<Modal.Provider />`를 렌더합니다.
144
144
  2. **Route Reset 장착** — `<Modal.RouteReset />`을 Provider와 같은 레벨에서 렌더해 경로 변경 시 스택을 초기화합니다.
145
- 3. **훅 사용** — `const { newModal, closeModal } = Modal.useModal();`
145
+ 3. **훅 사용** — `const { newModal, closeModal, hasBlockingModal } = Modal.useModal();`
146
146
  4. **템플릿 팩토리** — `Modal.Alert`, `Modal.Dialog`이 `ModalState` 객체를 반환하므로 `newModal(...)`에 그대로 전달합니다.
147
147
 
148
148
  ```tsx
@@ -211,6 +211,8 @@ export function ExampleActions() {
211
211
  ```
212
212
 
213
213
  - footer 버튼은 ui-legacy와 동일하게 `stackKey`, `role`, `defaultOptions/linkOptions` 구조로 관리하며, Alert(Text)/Dialog(Solid) 규격은 templates 내부에서 보장합니다.
214
+ - `closeOnOutsideClick?: boolean`으로 overlay 클릭 닫힘을 제어할 수 있습니다. 기본값은 `true`입니다.
215
+ - `preventRouteChange?: boolean`은 현재 열린 modal 중 route 변경 확인이 필요한 modal이 있는지 집계할 때 사용합니다. 서비스 앱은 `hasBlockingModal`을 읽어 공통 confirm UX를 연결할 수 있습니다.
214
216
  - Route Reset을 누락하면 이전 라우트의 모달 스택이 그대로 남으므로, layout 수준에서 Provider와 함께 반드시 렌더합니다.
215
217
  - 세부 가드레일·확장 기록은 `CONTEXT-MODAL.md`를 참고하고, 새 템플릿을 추가할 때 해당 문서를 선행 업데이트합니다.
216
218
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniai-fe/uds-templates",
3
- "version": "0.3.14",
3
+ "version": "0.3.15",
4
4
  "description": "UNIAI Design System; UI Templates Package",
5
5
  "type": "module",
6
6
  "private": false,
@@ -38,6 +38,8 @@ export function createAlertModal<FormContext extends FieldValues>({
38
38
  showDelay,
39
39
  width,
40
40
  padding,
41
+ closeOnOutsideClick,
42
+ preventRouteChange,
41
43
  formContextOptions,
42
44
  }: AlertTemplateOptions<FormContext>): ModalState<FormContext> {
43
45
  const primary: ModalTemplateButtonSpec = confirm ?? {
@@ -51,6 +53,8 @@ export function createAlertModal<FormContext extends FieldValues>({
51
53
  showDelay,
52
54
  width,
53
55
  padding,
56
+ closeOnOutsideClick,
57
+ preventRouteChange,
54
58
  // 변경 설명: 템플릿 생성 시점의 FormContext 제네릭을 modalProps로 전달한다.
55
59
  formContextOptions,
56
60
  body: <AlertContents message={message} />,
@@ -39,8 +39,11 @@ export function ModalRoot({
39
39
  }, [modalProps.show, stackKey, updateModal]);
40
40
 
41
41
  const handleClose = useCallback(() => {
42
+ if (modalProps.closeOnOutsideClick === false) {
43
+ return;
44
+ }
42
45
  closeModal({ stackKey });
43
- }, [closeModal, stackKey]);
46
+ }, [closeModal, modalProps.closeOnOutsideClick, stackKey]);
44
47
 
45
48
  const stopPropagation = useCallback(
46
49
  (event: React.MouseEvent<HTMLDivElement>) => {
@@ -78,6 +78,8 @@ export function createDialogModal<FormContext extends FieldValues>({
78
78
  showDelay,
79
79
  width,
80
80
  padding,
81
+ closeOnOutsideClick,
82
+ preventRouteChange,
81
83
  formContextOptions,
82
84
  }: DialogTemplateOptions<FormContext>): ModalState<FormContext> {
83
85
  const primary = resolveDialogPrimaryButton(confirm);
@@ -122,6 +124,8 @@ export function createDialogModal<FormContext extends FieldValues>({
122
124
  showDelay,
123
125
  width,
124
126
  padding,
127
+ closeOnOutsideClick,
128
+ preventRouteChange,
125
129
  // 변경 설명: 템플릿 생성 시점의 FormContext 제네릭을 modalProps로 전달한다.
126
130
  formContextOptions,
127
131
  header: headerNode ?? undefined,
@@ -29,6 +29,9 @@ const ensureDelay = (value?: number): number =>
29
29
  export function useModal(): UseModalReturn {
30
30
  const [modalStacks, updateModalStack] = useAtom(modalStackAtom);
31
31
  const modalStacksRef = useRef(modalStacks);
32
+ const hasBlockingModal = modalStacks.some(
33
+ stack => stack.modalProps.preventRouteChange === true,
34
+ );
32
35
 
33
36
  useEffect(() => {
34
37
  // 변경 설명: closeModal이 최신 stack 상태를 기준으로 대상을 찾도록 현재 스냅샷 ref를 동기화한다.
@@ -130,5 +133,5 @@ export function useModal(): UseModalReturn {
130
133
  [updateModalStack],
131
134
  );
132
135
 
133
- return { modalStacks, newModal, updateModal, closeModal };
136
+ return { modalStacks, hasBlockingModal, newModal, updateModal, closeModal };
134
137
  }
@@ -26,6 +26,7 @@ export type CloseFlagState = {
26
26
  /**
27
27
  * useModal 훅 반환값.
28
28
  * @property {ModalState[]} modalStacks 현재 모달 스택 목록
29
+ * @property {boolean} hasBlockingModal route 변경 확인이 필요한 모달 존재 여부
29
30
  * @property {<FormContext extends FieldValues>(newStack: ModalState<FormContext>) => void} newModal 신규 모달 추가 함수
30
31
  * @property {<FormContext extends FieldValues>(nextStack: ModalStatePatch<FormContext>) => void} updateModal 스택 상태 갱신 함수
31
32
  * @property {(request: ModalCloseRequest) => void} closeModal close 제어 함수
@@ -35,6 +36,10 @@ export type UseModalReturn = {
35
36
  * 현재 모달 스택 목록
36
37
  */
37
38
  modalStacks: ModalState[];
39
+ /**
40
+ * route 변경 확인이 필요한 모달 존재 여부
41
+ */
42
+ hasBlockingModal: boolean;
38
43
  /**
39
44
  * 신규 모달 추가 함수
40
45
  */
@@ -17,6 +17,8 @@ export type ModalShowState = "init" | boolean;
17
17
  * @property {ReactNode} [footer] footer 콘텐츠
18
18
  * @property {number | string} [width] 커스텀 width
19
19
  * @property {StyleSpacingType} [padding] 커스텀 padding
20
+ * @property {boolean} [closeOnOutsideClick] overlay 클릭 close 허용 여부
21
+ * @property {boolean} [preventRouteChange] route 변경 전 확인 필요 여부
20
22
  * @property {UseFormProps<FormContext>} [formContextOptions] form context 옵션
21
23
  */
22
24
  export type ModalSections<
@@ -48,6 +50,14 @@ export type ModalSections<
48
50
  * - [상, 우, 하, 좌]
49
51
  */
50
52
  padding?: StyleSpacingType;
53
+ /**
54
+ * overlay 클릭 close 허용 여부
55
+ */
56
+ closeOnOutsideClick?: boolean;
57
+ /**
58
+ * route 변경 전 확인 필요 여부
59
+ */
60
+ preventRouteChange?: boolean;
51
61
  /**
52
62
  * form context 옵션
53
63
  * - 옵션이 활성화되면, Form.Provider 사용으로 간주하여 활성화시킴
@@ -69,6 +79,8 @@ export type ModalSections<
69
79
  * @property {ModalShowState} show 노출 상태
70
80
  * @property {number} [showDelay] close 후 제거 지연(ms)
71
81
  * @property {ModalFooterButton[]} [footerButtons] footer 버튼 스펙
82
+ * @property {boolean} [closeOnOutsideClick] overlay 클릭 close 허용 여부 (@see ModalSections)
83
+ * @property {boolean} [preventRouteChange] route 변경 전 확인 필요 여부 (@see ModalSections)
72
84
  * @property {UseFormProps<FormContext>} [formContextOptions] form context 옵션
73
85
  */
74
86
  export type ModalProps<
@@ -56,6 +56,8 @@ export type ModalTemplateButtonSpec = {
56
56
  * @property {number} [showDelay] close 후 제거 지연(ms)
57
57
  * @property {number | string} [width] 커스텀 width
58
58
  * @property {StyleSpacingType} [padding] 커스텀 padding
59
+ * @property {boolean} [closeOnOutsideClick] overlay 클릭 close 허용 여부
60
+ * @property {boolean} [preventRouteChange] route 변경 전 확인 필요 여부
59
61
  */
60
62
  type ModalTemplateBase<
61
63
  FormContext extends FieldValues = Record<string, unknown>,
@@ -82,6 +84,14 @@ type ModalTemplateBase<
82
84
  * - [상, 우, 하, 좌]
83
85
  */
84
86
  padding?: StyleSpacingType;
87
+ /**
88
+ * overlay 클릭 close 허용 여부
89
+ */
90
+ closeOnOutsideClick?: boolean;
91
+ /**
92
+ * route 변경 전 확인 필요 여부
93
+ */
94
+ preventRouteChange?: boolean;
85
95
  /**
86
96
  * modal 내부 Form.Provider 옵션
87
97
  * - 타입 안정성은 Modal.Alert/Dialog 생성 시점 제네릭으로 보장한다.
@@ -99,6 +109,8 @@ type ModalTemplateBase<
99
109
  * @property {ReactNode} [footer] 커스텀 footer
100
110
  * @property {number | string} [width] 커스텀 width
101
111
  * @property {StyleSpacingType} [padding] 커스텀 padding
112
+ * @property {boolean} [closeOnOutsideClick] overlay 클릭 close 허용 여부
113
+ * @property {boolean} [preventRouteChange] route 변경 전 확인 필요 여부
102
114
  * @property {UseFormProps<FormContext>} [formContextOptions] modal 내부 Form.Provider 옵션
103
115
  */
104
116
  export type AlertTemplateOptions<
@@ -145,6 +157,8 @@ export type AlertTemplateOptions<
145
157
  * @property {ReactNode} [footer] 커스텀 footer
146
158
  * @property {number | string} [width] 커스텀 width
147
159
  * @property {StyleSpacingType} [padding] 커스텀 padding
160
+ * @property {boolean} [closeOnOutsideClick] overlay 클릭 close 허용 여부
161
+ * @property {boolean} [preventRouteChange] route 변경 전 확인 필요 여부
148
162
  * @property {UseFormProps<FormContext>} [formContextOptions] modal 내부 Form.Provider 옵션
149
163
  */
150
164
  export type DialogTemplateOptions<