@uniai-fe/uds-templates 0.3.10 → 0.3.12

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 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-6, 16px);
530
- gap: var(--spacing-gap-4, 16px);
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, 16px);
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, 16px);
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-6) 0;
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, 8px);
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, 12px);
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, 8px);
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, 4px);
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, 8px);
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, 400);
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, var(--spacing-padding-7, 20px) var(--spacing-padding-6, 16px));
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, 400);
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, 8px);
726
+ margin-top: var(--spacing-gap-2);
722
727
  }
723
728
 
724
729
  .uds-modal-surface {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniai-fe/uds-templates",
3
- "version": "0.3.10",
3
+ "version": "0.3.12",
4
4
  "description": "UNIAI Design System; UI Templates Package",
5
5
  "type": "module",
6
6
  "private": false,
@@ -12,7 +12,7 @@
12
12
  "publishConfig": {
13
13
  "access": "public"
14
14
  },
15
- "packageManager": "pnpm@10.30.3",
15
+ "packageManager": "pnpm@10.32.0",
16
16
  "engines": {
17
17
  "node": ">=24",
18
18
  "pnpm": ">=10"
@@ -86,7 +86,7 @@
86
86
  "@uniai-fe/util-next": "workspace:*",
87
87
  "@uniai-fe/util-rtc": "workspace:*",
88
88
  "eslint": "^9.39.2",
89
- "jotai": "^2.18.0",
89
+ "jotai": "^2.18.1",
90
90
  "next": "^15.5.11",
91
91
  "prettier": "^3.8.1",
92
92
  "react-hook-form": "^7.71.2",
@@ -58,3 +58,12 @@ export const farmIdAtom = atomWithStorage<number>(
58
58
  -1,
59
59
  jotaiStorage.session<number>(),
60
60
  );
61
+
62
+ /**
63
+ * 로그인 사용자 농장 관리번호
64
+ */
65
+ export const farmIdxAtom = atomWithStorage<number>(
66
+ "farmIdx",
67
+ -1,
68
+ jotaiStorage.session<number>(),
69
+ );
@@ -1,4 +1,4 @@
1
- import type { API_Res_Base } from "../../../types/api";
1
+ import type { API_Res_Base } from "@uniai-fe/util-api";
2
2
 
3
3
  /**
4
4
  * 로그인 API; 요청
@@ -34,10 +34,14 @@ export interface API_Res_LoginUserFarm {
34
34
  * 농장명
35
35
  */
36
36
  name: string;
37
+ /**
38
+ * 유니아이 농장 관리번호
39
+ */
40
+ farm_idx: number | string;
37
41
  /**
38
42
  * farm_code (농장 식별번호)
39
43
  */
40
- farm_code: string;
44
+ farm_code: number | string;
41
45
  /**
42
46
  * 계정 권한/직책 코드
43
47
  * - OWNER: 농장주
@@ -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
- X
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, useEffect, useState } from "react";
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
- let resolvedDelay = DEFAULT_CLOSE_DELAY;
93
- let hasTarget = false;
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
- if (!hasTarget) {
110
- console.warn(
111
- `[useModal] stack "${stackKey}" not found; close skipped.`,
116
+ // 변경 설명: close 예약 상태를 훅 local state로 두지 않고 즉시 타이머를 등록해 호출 경로별 제거 동작을 동일화한다.
117
+ setTimeout(() => {
118
+ updateModalStack((stacks: ModalState[]) =>
119
+ stacks.filter(stack => stack.stackKey !== stackKey),
112
120
  );
113
- return;
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-6, 16px);
23
- gap: var(--spacing-gap-4, 16px);
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, 16px);
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, 16px);
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-6) 0;
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, 8px);
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, 12px);
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, 8px);
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, 4px);
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, 8px);
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, 400);
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, 400);
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, 8px);
247
+ margin-top: var(--spacing-gap-2);
248
248
  }
249
249
  }