@uniai-fe/uds-templates 0.3.14 → 0.4.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/README.md +62 -1
- package/package.json +1 -1
- package/src/cctv/components/index.tsx +3 -0
- package/src/cctv/index.tsx +3 -0
- package/src/modal/components/alert/Template.tsx +4 -0
- package/src/modal/components/core/Root.tsx +4 -1
- package/src/modal/components/dialog/Template.tsx +4 -0
- package/src/modal/hooks/useModal.ts +4 -1
- package/src/modal/index.tsx +3 -0
- package/src/modal/types/hooks.ts +5 -0
- package/src/modal/types/state.ts +12 -0
- package/src/modal/types/templates.ts +14 -0
- package/src/page-frame/desktop/components/index.tsx +3 -0
- package/src/page-frame/desktop/index.tsx +3 -0
- package/src/page-frame/index.tsx +3 -0
- package/src/page-frame/mobile/components/index.tsx +3 -0
- package/src/page-frame/mobile/index.tsx +3 -0
- package/src/weather/components/index.tsx +3 -0
- package/src/weather/index.tsx +3 -0
package/README.md
CHANGED
|
@@ -29,6 +29,59 @@ Next.js 서비스에서 primitives와 동일한 방식으로 **Raw TypeScript**
|
|
|
29
29
|
- **templates**는 레이아웃/플로우/상태 표현까지 담당하고,
|
|
30
30
|
- API 호출, 인증 토큰 관리, 라우팅, i18n 등 비즈니스 로직은 서비스 앱에서 구현합니다.
|
|
31
31
|
|
|
32
|
+
## 확인 완료 도구 목록
|
|
33
|
+
|
|
34
|
+
- `/modal`
|
|
35
|
+
- `Modal.Provider`
|
|
36
|
+
- `Modal.StackProvider`
|
|
37
|
+
- `Modal.RouteReset`
|
|
38
|
+
- `Modal.useModal`
|
|
39
|
+
- `Modal.Alert`
|
|
40
|
+
- `Modal.Dialog`
|
|
41
|
+
- `createAlertModal`
|
|
42
|
+
- `createDialogModal`
|
|
43
|
+
- `modalStackAtom`
|
|
44
|
+
- `ModalState`
|
|
45
|
+
- `ModalStatePatch`
|
|
46
|
+
- `ModalProps`
|
|
47
|
+
- `ModalFooterButton`
|
|
48
|
+
- `AlertTemplateOptions`
|
|
49
|
+
- `DialogTemplateOptions`
|
|
50
|
+
- `UseModalReturn`
|
|
51
|
+
- `/weather`
|
|
52
|
+
- `WeatherComponents.PageHeader`
|
|
53
|
+
- `WeatherPageHeaderContainer`
|
|
54
|
+
- `WeatherMockProvider`
|
|
55
|
+
- `weatherCoordinate`
|
|
56
|
+
- `useWeatherKorea`
|
|
57
|
+
- `useOpenWeatherMap`
|
|
58
|
+
- `/cctv`
|
|
59
|
+
- `CCTV.Provider`
|
|
60
|
+
- `CCTV.CamList.Container`
|
|
61
|
+
- `CCTV.Video.Container`
|
|
62
|
+
- `CCTV.Video.Contents`
|
|
63
|
+
- `CCTV.Video.Overlay.Container`
|
|
64
|
+
- `CCTV.Pagination.Container`
|
|
65
|
+
- `CCTV.Pagination.List.Container`
|
|
66
|
+
- `CCTV.Pagination.Button.Prev`
|
|
67
|
+
- `CCTV.Pagination.Button.Next`
|
|
68
|
+
- `CCTV.Viewer.Desktop.Container`
|
|
69
|
+
- `useCctvCompanyData`
|
|
70
|
+
- `useCctvContext`
|
|
71
|
+
- `useCctvRtcStream`
|
|
72
|
+
- `getServerCompanyList`
|
|
73
|
+
- `getServerCctvToken`
|
|
74
|
+
- `postCctvRtcToken`
|
|
75
|
+
- `/page-frame`
|
|
76
|
+
- `Frame`
|
|
77
|
+
- `Frame.Mobile`
|
|
78
|
+
- `MobileFrame`
|
|
79
|
+
- `Frame.Desktop`
|
|
80
|
+
- `DesktopFrame`
|
|
81
|
+
- `MobileFrameProps`
|
|
82
|
+
- `PageFrameDesktopNavProps`
|
|
83
|
+
- `SitemapDataType`
|
|
84
|
+
|
|
32
85
|
## 현재 제공 템플릿/모듈
|
|
33
86
|
|
|
34
87
|
- `/auth/**`
|
|
@@ -42,6 +95,12 @@ Next.js 서비스에서 primitives와 동일한 방식으로 **Raw TypeScript**
|
|
|
42
95
|
- `/modal/**`
|
|
43
96
|
- ui-legacy 스택 기반 모달 Provider/Root/Container + 템플릿(`Modal.Alert`, `Modal.Dialog`)
|
|
44
97
|
- Storybook(`apps/design-storybook/src/stories/templates/modal`)에서 Alert/Confirm 케이스를 검증한다.
|
|
98
|
+
- `/weather/**`
|
|
99
|
+
- page-frame header utility에 결합되는 weather header 템플릿과 weather data hook/mock 도구를 제공한다.
|
|
100
|
+
- `/cctv/**`
|
|
101
|
+
- finder/viewer/video/pagination 조합과 rtc/company-list API helper를 제공한다.
|
|
102
|
+
- `/page-frame/**`
|
|
103
|
+
- mobile/desktop private route frame, nav/header/popup 조합을 제공한다.
|
|
45
104
|
|
|
46
105
|
각 템플릿의 상세한 범위와 의사결정은 `CONTEXT-*.md` 문서에서 관리합니다.
|
|
47
106
|
|
|
@@ -142,7 +201,7 @@ ui-legacy에서 사용하던 모달 스택/옵션을 templates 레이어로 옮
|
|
|
142
201
|
|
|
143
202
|
1. **Provider 1회 장착** — 서비스 레이아웃에서 `<Modal.Provider />`를 렌더합니다.
|
|
144
203
|
2. **Route Reset 장착** — `<Modal.RouteReset />`을 Provider와 같은 레벨에서 렌더해 경로 변경 시 스택을 초기화합니다.
|
|
145
|
-
3. **훅 사용** — `const { newModal, closeModal } = Modal.useModal();`
|
|
204
|
+
3. **훅 사용** — `const { newModal, closeModal, hasBlockingModal } = Modal.useModal();`
|
|
146
205
|
4. **템플릿 팩토리** — `Modal.Alert`, `Modal.Dialog`이 `ModalState` 객체를 반환하므로 `newModal(...)`에 그대로 전달합니다.
|
|
147
206
|
|
|
148
207
|
```tsx
|
|
@@ -211,6 +270,8 @@ export function ExampleActions() {
|
|
|
211
270
|
```
|
|
212
271
|
|
|
213
272
|
- footer 버튼은 ui-legacy와 동일하게 `stackKey`, `role`, `defaultOptions/linkOptions` 구조로 관리하며, Alert(Text)/Dialog(Solid) 규격은 templates 내부에서 보장합니다.
|
|
273
|
+
- `closeOnOutsideClick?: boolean`으로 overlay 클릭 닫힘을 제어할 수 있습니다. 기본값은 `true`입니다.
|
|
274
|
+
- `preventRouteChange?: boolean`은 현재 열린 modal 중 route 변경 확인이 필요한 modal이 있는지 집계할 때 사용합니다. 서비스 앱은 `hasBlockingModal`을 읽어 공통 confirm UX를 연결할 수 있습니다.
|
|
214
275
|
- Route Reset을 누락하면 이전 라우트의 모달 스택이 그대로 남으므로, layout 수준에서 Provider와 함께 반드시 렌더합니다.
|
|
215
276
|
- 세부 가드레일·확장 기록은 `CONTEXT-MODAL.md`를 참고하고, 새 템플릿을 추가할 때 해당 문서를 선행 업데이트합니다.
|
|
216
277
|
|
package/package.json
CHANGED
package/src/cctv/index.tsx
CHANGED
|
@@ -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
|
}
|
package/src/modal/index.tsx
CHANGED
package/src/modal/types/hooks.ts
CHANGED
|
@@ -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
|
*/
|
package/src/modal/types/state.ts
CHANGED
|
@@ -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<
|
package/src/page-frame/index.tsx
CHANGED