@uniai-fe/uds-templates 0.3.9 → 0.3.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.
- package/dist/styles.css +19 -14
- package/package.json +1 -1
- package/src/modal/components/core/header/CloseButton.tsx +8 -1
- package/src/modal/hooks/useModal.ts +21 -31
- package/src/modal/img/close.svg +3 -0
- package/src/modal/styles/base.scss +1 -0
- package/src/modal/styles/container.scss +17 -17
- package/src/page-frame/desktop/components/header/util/setting/Button.tsx +35 -11
package/dist/styles.css
CHANGED
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
--modal-dialog-title-weight: var(--font-heading-small-weight);
|
|
58
58
|
--modal-dialog-body-color: var(--color-label-standard);
|
|
59
59
|
--modal-dialog-body-font-size: var(--font-body-medium-size);
|
|
60
|
+
--modal-dialog-body-padding: var(--spacing-padding-7) var(--spacing-padding-8);
|
|
60
61
|
--auth-container-max-width: 335px;
|
|
61
62
|
--auth-container-gap: var(--spacing-padding-7, 28px);
|
|
62
63
|
--auth-container-padding-inline: var(--spacing-padding-6, 24px);
|
|
@@ -517,6 +518,10 @@
|
|
|
517
518
|
margin: 0;
|
|
518
519
|
}
|
|
519
520
|
|
|
521
|
+
.uds-modal-header-close-button {
|
|
522
|
+
font-size: 0;
|
|
523
|
+
}
|
|
524
|
+
|
|
520
525
|
.uds-modal-footer {
|
|
521
526
|
padding: 0;
|
|
522
527
|
border-top: none;
|
|
@@ -526,17 +531,17 @@
|
|
|
526
531
|
display: flex;
|
|
527
532
|
flex-wrap: wrap;
|
|
528
533
|
width: 100%;
|
|
529
|
-
padding: var(--spacing-padding-
|
|
530
|
-
gap: var(--spacing-gap-4
|
|
534
|
+
padding: 0 var(--spacing-padding-8) var(--spacing-padding-7);
|
|
535
|
+
gap: var(--spacing-gap-4);
|
|
531
536
|
align-items: stretch;
|
|
532
|
-
row-gap: var(--spacing-gap-4
|
|
537
|
+
row-gap: var(--spacing-gap-4);
|
|
533
538
|
}
|
|
534
539
|
|
|
535
540
|
.uds-modal-footer-group {
|
|
536
541
|
display: flex;
|
|
537
542
|
flex: 1 1 0;
|
|
538
543
|
flex-wrap: nowrap;
|
|
539
|
-
gap: var(--spacing-gap-4
|
|
544
|
+
gap: var(--spacing-gap-4);
|
|
540
545
|
align-items: stretch;
|
|
541
546
|
}
|
|
542
547
|
.uds-modal-footer-group[data-position=left] {
|
|
@@ -626,18 +631,18 @@
|
|
|
626
631
|
}
|
|
627
632
|
|
|
628
633
|
.uds-modal-dialog-header-content {
|
|
629
|
-
padding: var(--spacing-padding-9) var(--spacing-padding-
|
|
634
|
+
padding: var(--spacing-padding-9) var(--spacing-padding-8) 0;
|
|
630
635
|
text-align: center;
|
|
631
636
|
display: flex;
|
|
632
637
|
flex-direction: column;
|
|
633
|
-
gap: var(--spacing-gap-2
|
|
638
|
+
gap: var(--spacing-gap-2);
|
|
634
639
|
}
|
|
635
640
|
|
|
636
641
|
.uds-modal-dialog-header-row {
|
|
637
642
|
display: flex;
|
|
638
643
|
align-items: center;
|
|
639
644
|
justify-content: center;
|
|
640
|
-
gap: var(--spacing-gap-3
|
|
645
|
+
gap: var(--spacing-gap-3);
|
|
641
646
|
}
|
|
642
647
|
|
|
643
648
|
.uds-modal-dialog-header-row-split {
|
|
@@ -647,14 +652,14 @@
|
|
|
647
652
|
.uds-modal-dialog-header-leading {
|
|
648
653
|
display: inline-flex;
|
|
649
654
|
align-items: center;
|
|
650
|
-
gap: var(--spacing-gap-2
|
|
655
|
+
gap: var(--spacing-gap-2);
|
|
651
656
|
justify-content: center;
|
|
652
657
|
}
|
|
653
658
|
|
|
654
659
|
.uds-modal-dialog-header-leading-content {
|
|
655
660
|
display: inline-flex;
|
|
656
661
|
align-items: center;
|
|
657
|
-
gap: var(--spacing-gap-1
|
|
662
|
+
gap: var(--spacing-gap-1);
|
|
658
663
|
}
|
|
659
664
|
.uds-modal-dialog-header-leading-content > :where(p, span, strong, em):not([class]) {
|
|
660
665
|
color: var(--modal-dialog-title-color);
|
|
@@ -679,7 +684,7 @@
|
|
|
679
684
|
.uds-modal-dialog-header-trailing {
|
|
680
685
|
display: inline-flex;
|
|
681
686
|
align-items: center;
|
|
682
|
-
gap: var(--spacing-gap-2
|
|
687
|
+
gap: var(--spacing-gap-2);
|
|
683
688
|
}
|
|
684
689
|
|
|
685
690
|
.uds-modal-dialog-header-description {
|
|
@@ -691,7 +696,7 @@
|
|
|
691
696
|
color: var(--modal-dialog-body-color);
|
|
692
697
|
font-size: var(--modal-dialog-body-font-size);
|
|
693
698
|
line-height: 1.5em;
|
|
694
|
-
font-weight: var(--font-body-small-weight
|
|
699
|
+
font-weight: var(--font-body-small-weight);
|
|
695
700
|
}
|
|
696
701
|
|
|
697
702
|
.uds-modal-dialog-header[data-layout=split] .uds-modal-dialog-header-content,
|
|
@@ -705,7 +710,7 @@
|
|
|
705
710
|
}
|
|
706
711
|
|
|
707
712
|
.uds-modal-dialog-body-content {
|
|
708
|
-
padding: var(--modal-dialog-body-padding,
|
|
713
|
+
padding: var(--modal-dialog-body-padding, );
|
|
709
714
|
text-align: center;
|
|
710
715
|
word-break: keep-all;
|
|
711
716
|
color: var(--modal-dialog-body-color);
|
|
@@ -714,11 +719,11 @@
|
|
|
714
719
|
margin: 0;
|
|
715
720
|
font-size: var(--modal-dialog-body-font-size);
|
|
716
721
|
line-height: 1.5em;
|
|
717
|
-
font-weight: var(--font-body-small-weight
|
|
722
|
+
font-weight: var(--font-body-small-weight);
|
|
718
723
|
word-break: inherit;
|
|
719
724
|
}
|
|
720
725
|
.uds-modal-dialog-body-content > * + * {
|
|
721
|
-
margin-top: var(--spacing-gap-2
|
|
726
|
+
margin-top: var(--spacing-gap-2);
|
|
722
727
|
}
|
|
723
728
|
|
|
724
729
|
.uds-modal-surface {
|
package/package.json
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import clsx from "clsx";
|
|
4
4
|
|
|
5
5
|
import type { ModalHeaderCloseButtonProps } from "../../../types";
|
|
6
|
+
import CloseIcon from "../../../img/close.svg";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Modal Header CloseButton; 모달 헤더 닫기 버튼
|
|
@@ -26,7 +27,13 @@ export function ModalHeaderCloseButton({
|
|
|
26
27
|
aria-label={ariaLabel}
|
|
27
28
|
onClick={onClick}
|
|
28
29
|
>
|
|
29
|
-
|
|
30
|
+
<CloseIcon
|
|
31
|
+
width={20}
|
|
32
|
+
height={20}
|
|
33
|
+
alt={ariaLabel}
|
|
34
|
+
viewBox="0 0 20 20"
|
|
35
|
+
preserveAspectRatio="xMidYMid meet"
|
|
36
|
+
/>
|
|
30
37
|
</button>
|
|
31
38
|
);
|
|
32
39
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useCallback
|
|
3
|
+
import { useCallback } from "react";
|
|
4
4
|
|
|
5
5
|
import { useAtom } from "jotai";
|
|
6
6
|
|
|
7
7
|
import type {
|
|
8
|
-
CloseFlagState,
|
|
9
8
|
ModalCloseRequest,
|
|
10
9
|
ModalProps,
|
|
11
10
|
ModalState,
|
|
@@ -29,7 +28,6 @@ const ensureDelay = (value?: number): number =>
|
|
|
29
28
|
*/
|
|
30
29
|
export function useModal(): UseModalReturn {
|
|
31
30
|
const [modalStacks, updateModalStack] = useAtom(modalStackAtom);
|
|
32
|
-
const [closeFlag, setCloseFlag] = useState<CloseFlagState | null>(null);
|
|
33
31
|
|
|
34
32
|
const newModal = useCallback(
|
|
35
33
|
<FormContext extends FieldValues>(newStack: ModalState<FormContext>) => {
|
|
@@ -89,16 +87,25 @@ export function useModal(): UseModalReturn {
|
|
|
89
87
|
|
|
90
88
|
const closeModal = useCallback(
|
|
91
89
|
({ stackKey, callback }: ModalCloseRequest) => {
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
const targetStack = modalStacks.find(
|
|
91
|
+
stack => stack.stackKey === stackKey,
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
if (!targetStack) {
|
|
95
|
+
console.warn(
|
|
96
|
+
`[useModal] stack "${stackKey}" not found; close skipped.`,
|
|
97
|
+
);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const resolvedDelay = ensureDelay(targetStack.modalProps.showDelay);
|
|
94
102
|
|
|
103
|
+
// 변경 설명: updater 내부 side-effect 의존을 제거하고, 현재 스택 스냅샷 기준으로 close 타이머를 확정한다.
|
|
95
104
|
updateModalStack((stacks: ModalState[]) =>
|
|
96
105
|
stacks.map(stack => {
|
|
97
106
|
if (stack.stackKey !== stackKey) {
|
|
98
107
|
return stack;
|
|
99
108
|
}
|
|
100
|
-
hasTarget = true;
|
|
101
|
-
resolvedDelay = ensureDelay(stack.modalProps.showDelay);
|
|
102
109
|
return {
|
|
103
110
|
...stack,
|
|
104
111
|
modalProps: { ...stack.modalProps, show: false },
|
|
@@ -106,33 +113,16 @@ export function useModal(): UseModalReturn {
|
|
|
106
113
|
}),
|
|
107
114
|
);
|
|
108
115
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
116
|
+
// 변경 설명: close 예약 상태를 훅 local state로 두지 않고 즉시 타이머를 등록해 호출 경로별 제거 동작을 동일화한다.
|
|
117
|
+
setTimeout(() => {
|
|
118
|
+
updateModalStack((stacks: ModalState[]) =>
|
|
119
|
+
stacks.filter(stack => stack.stackKey !== stackKey),
|
|
112
120
|
);
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
setCloseFlag({ stackKey, showDelay: resolvedDelay, callback });
|
|
121
|
+
callback?.();
|
|
122
|
+
}, resolvedDelay);
|
|
117
123
|
},
|
|
118
|
-
[updateModalStack],
|
|
124
|
+
[modalStacks, updateModalStack],
|
|
119
125
|
);
|
|
120
126
|
|
|
121
|
-
useEffect(() => {
|
|
122
|
-
if (!closeFlag) {
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const timer = setTimeout(() => {
|
|
127
|
-
updateModalStack((stacks: ModalState[]) =>
|
|
128
|
-
stacks.filter(stack => stack.stackKey !== closeFlag.stackKey),
|
|
129
|
-
);
|
|
130
|
-
closeFlag.callback?.();
|
|
131
|
-
setCloseFlag(null);
|
|
132
|
-
}, closeFlag.showDelay);
|
|
133
|
-
|
|
134
|
-
return () => clearTimeout(timer);
|
|
135
|
-
}, [closeFlag, updateModalStack]);
|
|
136
|
-
|
|
137
127
|
return { modalStacks, newModal, updateModal, closeModal };
|
|
138
128
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M15.1264 3.37588C15.5169 2.9854 16.1503 2.98537 16.5408 3.37588C16.9312 3.7664 16.9312 4.39976 16.5408 4.79027L11.3723 9.95791L16.5408 15.1264C16.931 15.5169 16.9304 16.1495 16.54 16.5399C16.1495 16.9304 15.5169 16.931 15.1264 16.5408L9.95793 11.3723L4.79029 16.5408C4.39974 16.931 3.76634 16.9304 3.3759 16.5399C2.9858 16.1495 2.98574 15.5168 3.3759 15.1264L8.54354 9.95791L3.3759 4.79027C2.98538 4.39974 2.98538 3.7664 3.3759 3.37588C3.76643 2.9854 4.39978 2.98537 4.79029 3.37588L9.95793 8.54352L15.1264 3.37588Z" fill="#94989E"/>
|
|
3
|
+
</svg>
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
--modal-dialog-title-weight: var(--font-heading-small-weight);
|
|
22
22
|
--modal-dialog-body-color: var(--color-label-standard);
|
|
23
23
|
--modal-dialog-body-font-size: var(--font-body-medium-size);
|
|
24
|
+
--modal-dialog-body-padding: var(--spacing-padding-7) var(--spacing-padding-8);
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
.uds-modal-root {
|
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
padding: 0;
|
|
10
10
|
margin: 0;
|
|
11
11
|
}
|
|
12
|
+
.uds-modal-header-close-button {
|
|
13
|
+
font-size: 0;
|
|
14
|
+
}
|
|
12
15
|
|
|
13
16
|
.uds-modal-footer {
|
|
14
17
|
padding: 0;
|
|
@@ -19,17 +22,17 @@
|
|
|
19
22
|
display: flex;
|
|
20
23
|
flex-wrap: wrap;
|
|
21
24
|
width: 100%;
|
|
22
|
-
padding: var(--spacing-padding-
|
|
23
|
-
gap: var(--spacing-gap-4
|
|
25
|
+
padding: 0 var(--spacing-padding-8) var(--spacing-padding-7);
|
|
26
|
+
gap: var(--spacing-gap-4);
|
|
24
27
|
align-items: stretch;
|
|
25
|
-
row-gap: var(--spacing-gap-4
|
|
28
|
+
row-gap: var(--spacing-gap-4);
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
.uds-modal-footer-group {
|
|
29
32
|
display: flex;
|
|
30
33
|
flex: 1 1 0;
|
|
31
34
|
flex-wrap: nowrap;
|
|
32
|
-
gap: var(--spacing-gap-4
|
|
35
|
+
gap: var(--spacing-gap-4);
|
|
33
36
|
align-items: stretch;
|
|
34
37
|
|
|
35
38
|
&[data-position="left"] {
|
|
@@ -138,18 +141,18 @@
|
|
|
138
141
|
}
|
|
139
142
|
|
|
140
143
|
.uds-modal-dialog-header-content {
|
|
141
|
-
padding: var(--spacing-padding-9) var(--spacing-padding-
|
|
144
|
+
padding: var(--spacing-padding-9) var(--spacing-padding-8) 0;
|
|
142
145
|
text-align: center;
|
|
143
146
|
display: flex;
|
|
144
147
|
flex-direction: column;
|
|
145
|
-
gap: var(--spacing-gap-2
|
|
148
|
+
gap: var(--spacing-gap-2);
|
|
146
149
|
}
|
|
147
150
|
|
|
148
151
|
.uds-modal-dialog-header-row {
|
|
149
152
|
display: flex;
|
|
150
153
|
align-items: center;
|
|
151
154
|
justify-content: center;
|
|
152
|
-
gap: var(--spacing-gap-3
|
|
155
|
+
gap: var(--spacing-gap-3);
|
|
153
156
|
}
|
|
154
157
|
|
|
155
158
|
.uds-modal-dialog-header-row-split {
|
|
@@ -159,14 +162,14 @@
|
|
|
159
162
|
.uds-modal-dialog-header-leading {
|
|
160
163
|
display: inline-flex;
|
|
161
164
|
align-items: center;
|
|
162
|
-
gap: var(--spacing-gap-2
|
|
165
|
+
gap: var(--spacing-gap-2);
|
|
163
166
|
justify-content: center;
|
|
164
167
|
}
|
|
165
168
|
|
|
166
169
|
.uds-modal-dialog-header-leading-content {
|
|
167
170
|
display: inline-flex;
|
|
168
171
|
align-items: center;
|
|
169
|
-
gap: var(--spacing-gap-1
|
|
172
|
+
gap: var(--spacing-gap-1);
|
|
170
173
|
|
|
171
174
|
// 변경: header leading slot은 class 없는 직계 텍스트만 기본 타이포를 적용한다.
|
|
172
175
|
> :where(p, span, strong, em):not([class]) {
|
|
@@ -195,7 +198,7 @@
|
|
|
195
198
|
.uds-modal-dialog-header-trailing {
|
|
196
199
|
display: inline-flex;
|
|
197
200
|
align-items: center;
|
|
198
|
-
gap: var(--spacing-gap-2
|
|
201
|
+
gap: var(--spacing-gap-2);
|
|
199
202
|
}
|
|
200
203
|
|
|
201
204
|
.uds-modal-dialog-header-description {
|
|
@@ -208,7 +211,7 @@
|
|
|
208
211
|
color: var(--modal-dialog-body-color);
|
|
209
212
|
font-size: var(--modal-dialog-body-font-size);
|
|
210
213
|
line-height: 1.5em;
|
|
211
|
-
font-weight: var(--font-body-small-weight
|
|
214
|
+
font-weight: var(--font-body-small-weight);
|
|
212
215
|
}
|
|
213
216
|
}
|
|
214
217
|
|
|
@@ -225,10 +228,7 @@
|
|
|
225
228
|
}
|
|
226
229
|
|
|
227
230
|
.uds-modal-dialog-body-content {
|
|
228
|
-
padding: var(
|
|
229
|
-
--modal-dialog-body-padding,
|
|
230
|
-
var(--spacing-padding-7, 20px) var(--spacing-padding-6, 16px)
|
|
231
|
-
);
|
|
231
|
+
padding: var(--modal-dialog-body-padding,);
|
|
232
232
|
text-align: center;
|
|
233
233
|
word-break: keep-all;
|
|
234
234
|
color: var(--modal-dialog-body-color);
|
|
@@ -239,11 +239,11 @@
|
|
|
239
239
|
margin: 0;
|
|
240
240
|
font-size: var(--modal-dialog-body-font-size);
|
|
241
241
|
line-height: 1.5em;
|
|
242
|
-
font-weight: var(--font-body-small-weight
|
|
242
|
+
font-weight: var(--font-body-small-weight);
|
|
243
243
|
word-break: inherit;
|
|
244
244
|
}
|
|
245
245
|
|
|
246
246
|
> * + * {
|
|
247
|
-
margin-top: var(--spacing-gap-2
|
|
247
|
+
margin-top: var(--spacing-gap-2);
|
|
248
248
|
}
|
|
249
249
|
}
|
|
@@ -25,12 +25,11 @@ import { PAGE_FRAME_DESKTOP_SETTING_ITEMS } from "../../../../data/setting";
|
|
|
25
25
|
* @property {string} [props.className] 사용자 정의 className
|
|
26
26
|
* @property {PageFrameDesktopSettingItem[]} [props.items] dropdown 항목 배열
|
|
27
27
|
* @description
|
|
28
|
-
* -
|
|
29
|
-
* -
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* - 비밀번호 재설정
|
|
28
|
+
* - 내 정보 관리
|
|
29
|
+
* - 내 농장 관리
|
|
30
|
+
* - 기본 정보
|
|
31
|
+
* - 인증 정보
|
|
32
|
+
* - 시설 정보
|
|
34
33
|
*/
|
|
35
34
|
export default function PageHeaderSettingButton({
|
|
36
35
|
className,
|
|
@@ -46,17 +45,43 @@ export default function PageHeaderSettingButton({
|
|
|
46
45
|
() => getClosestRoute(menuItems, resolvedPath),
|
|
47
46
|
[menuItems, resolvedPath],
|
|
48
47
|
);
|
|
48
|
+
const commonRoute = useMemo(() => {
|
|
49
|
+
const routes = menuItems.map(({ path }) => path.split("/").filter(Boolean));
|
|
50
|
+
|
|
51
|
+
// route 각 index 아이템이 가장 많이 겹치는 route 추출
|
|
52
|
+
const maxLength = Math.max(...routes.map(route => route.length));
|
|
53
|
+
const res = Array(maxLength)
|
|
54
|
+
.fill("")
|
|
55
|
+
.map((_, index) => {
|
|
56
|
+
const current = routes[0][index] ?? "";
|
|
57
|
+
if (routes.every(route => route[index] === current)) {
|
|
58
|
+
return current;
|
|
59
|
+
}
|
|
60
|
+
return "";
|
|
61
|
+
})
|
|
62
|
+
.filter(Boolean);
|
|
63
|
+
return `/${res.join("/")}`;
|
|
64
|
+
}, [menuItems]);
|
|
65
|
+
|
|
66
|
+
const isMatchRouteGroup = useMemo(
|
|
67
|
+
() => pathname.startsWith(commonRoute),
|
|
68
|
+
[pathname, commonRoute],
|
|
69
|
+
);
|
|
49
70
|
|
|
50
71
|
const dropdownItems: DropdownTemplateItem[] = useMemo(
|
|
51
72
|
() =>
|
|
52
73
|
menuItems.map(item => ({
|
|
53
74
|
id: item.routeKey,
|
|
75
|
+
// 변경: Dropdown.Template value-first 계약에 맞춰 routeKey를 value로도 전달한다.
|
|
76
|
+
value: item.routeKey,
|
|
54
77
|
label: item.name,
|
|
55
78
|
left: item.icon,
|
|
56
79
|
// 변경: Dropdown.Template 선택 계약은 items[].selected를 source로 사용한다.
|
|
57
|
-
selected:
|
|
80
|
+
selected:
|
|
81
|
+
isMatchRouteGroup &&
|
|
82
|
+
String(closestRoute?.routeKey) === String(item.routeKey),
|
|
58
83
|
})),
|
|
59
|
-
[closestRoute?.routeKey
|
|
84
|
+
[menuItems, isMatchRouteGroup, closestRoute?.routeKey],
|
|
60
85
|
);
|
|
61
86
|
|
|
62
87
|
/**
|
|
@@ -65,9 +90,8 @@ export default function PageHeaderSettingButton({
|
|
|
65
90
|
*/
|
|
66
91
|
const handleSelect = useCallback(
|
|
67
92
|
(payload: DropdownTemplateChangePayload) => {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
);
|
|
93
|
+
const currentRouteKey = String(payload.currentValue);
|
|
94
|
+
const target = menuItems.find(item => item.routeKey === currentRouteKey);
|
|
71
95
|
target?.onSelect?.();
|
|
72
96
|
},
|
|
73
97
|
[menuItems],
|