@xfilecom/xframe 0.1.32 → 0.1.34
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/defaults.json
CHANGED
package/package.json
CHANGED
|
@@ -28,6 +28,32 @@ function SectionTitle({ children }: { children: ReactNode }) {
|
|
|
28
28
|
);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
function StarIcon() {
|
|
32
|
+
return (
|
|
33
|
+
<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden style={{ display: 'block' }}>
|
|
34
|
+
<path
|
|
35
|
+
fill="currentColor"
|
|
36
|
+
d="m8 1.3 1.9 4.2 4.6.4-3.5 3.1 1 4.5L8 11.5l-4 2 1-4.5-3.5-3.1 4.6-.4L8 1.3z"
|
|
37
|
+
/>
|
|
38
|
+
</svg>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function CheckIcon() {
|
|
43
|
+
return (
|
|
44
|
+
<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden style={{ display: 'block' }}>
|
|
45
|
+
<path
|
|
46
|
+
fill="none"
|
|
47
|
+
stroke="currentColor"
|
|
48
|
+
strokeWidth="2"
|
|
49
|
+
strokeLinecap="round"
|
|
50
|
+
strokeLinejoin="round"
|
|
51
|
+
d="M3.5 8.5l3 3 6-7"
|
|
52
|
+
/>
|
|
53
|
+
</svg>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
31
57
|
export function FrontCoreShowcase() {
|
|
32
58
|
const uid = useId();
|
|
33
59
|
const toastSeq = useRef(0);
|
|
@@ -35,11 +61,16 @@ export function FrontCoreShowcase() {
|
|
|
35
61
|
const [toasts, setToasts] = useState<ToastEntry[]>([]);
|
|
36
62
|
const [errors, setErrors] = useState<InlineErrorEntry[]>([]);
|
|
37
63
|
const [loading, setLoading] = useState(false);
|
|
64
|
+
const [badgeClicks, setBadgeClicks] = useState(0);
|
|
65
|
+
const [btnLoading, setBtnLoading] = useState(false);
|
|
38
66
|
const [dialogOpen, setDialogOpen] = useState(false);
|
|
67
|
+
const [dialogRichOpen, setDialogRichOpen] = useState(false);
|
|
39
68
|
const [confirmOpen, setConfirmOpen] = useState(false);
|
|
40
69
|
const [confirmDestructiveOpen, setConfirmDestructiveOpen] = useState(false);
|
|
41
70
|
const [confirmLoadingDemo, setConfirmLoadingDemo] = useState(false);
|
|
71
|
+
const [confirmCustomOpen, setConfirmCustomOpen] = useState(false);
|
|
42
72
|
const [sheetOpen, setSheetOpen] = useState(false);
|
|
73
|
+
const [sheetRichOpen, setSheetRichOpen] = useState(false);
|
|
43
74
|
|
|
44
75
|
const pushToast = useCallback(
|
|
45
76
|
(severity: ToastSeverity) => {
|
|
@@ -50,17 +81,56 @@ export function FrontCoreShowcase() {
|
|
|
50
81
|
[uid],
|
|
51
82
|
);
|
|
52
83
|
|
|
84
|
+
const pushToastCustomIcon = useCallback(() => {
|
|
85
|
+
toastSeq.current += 1;
|
|
86
|
+
const id = `${uid}-t-${toastSeq.current}`;
|
|
87
|
+
setToasts((prev) => [
|
|
88
|
+
...prev,
|
|
89
|
+
{
|
|
90
|
+
id,
|
|
91
|
+
severity: 'success',
|
|
92
|
+
message: 'ToastEntry.icon 으로 별 아이콘',
|
|
93
|
+
icon: <StarIcon />,
|
|
94
|
+
},
|
|
95
|
+
]);
|
|
96
|
+
}, [uid]);
|
|
97
|
+
|
|
53
98
|
const addError = useCallback(() => {
|
|
54
99
|
errorSeq.current += 1;
|
|
55
100
|
const id = `${uid}-e-${errorSeq.current}`;
|
|
56
101
|
setErrors((prev) => [...prev, { id, message: `샘플 에러 #${errorSeq.current}` }]);
|
|
57
102
|
}, [uid]);
|
|
58
103
|
|
|
104
|
+
const addErrorRich = useCallback(() => {
|
|
105
|
+
errorSeq.current += 1;
|
|
106
|
+
const id = `${uid}-e-${errorSeq.current}`;
|
|
107
|
+
setErrors((prev) => [
|
|
108
|
+
...prev,
|
|
109
|
+
{
|
|
110
|
+
id,
|
|
111
|
+
message: '아이콘 + 행 클릭(콘솔)',
|
|
112
|
+
icon: <ToastSeverityIcon severity="error" />,
|
|
113
|
+
onClick: () => {
|
|
114
|
+
console.info('[FrontCoreShowcase] inline error row click', id);
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
]);
|
|
118
|
+
}, [uid]);
|
|
119
|
+
|
|
59
120
|
return (
|
|
60
121
|
<>
|
|
61
|
-
<InlineErrorList
|
|
122
|
+
<InlineErrorList
|
|
123
|
+
errors={errors}
|
|
124
|
+
onDismiss={(id) => setErrors((e) => e.filter((x) => x.id !== id))}
|
|
125
|
+
dismissAriaLabel="배너 닫기"
|
|
126
|
+
/>
|
|
62
127
|
<ToastList toasts={toasts} onDismiss={(id) => setToasts((t) => t.filter((x) => x.id !== id))} />
|
|
63
|
-
<LoadingOverlay
|
|
128
|
+
<LoadingOverlay
|
|
129
|
+
active={loading}
|
|
130
|
+
title="처리 중"
|
|
131
|
+
message="데이터를 가져옵니다…"
|
|
132
|
+
ariaLabel="데이터 로딩 중"
|
|
133
|
+
/>
|
|
64
134
|
|
|
65
135
|
<Dialog
|
|
66
136
|
open={dialogOpen}
|
|
@@ -79,6 +149,26 @@ export function FrontCoreShowcase() {
|
|
|
79
149
|
</div>
|
|
80
150
|
</Dialog>
|
|
81
151
|
|
|
152
|
+
<Dialog
|
|
153
|
+
open={dialogRichOpen}
|
|
154
|
+
onOpenChange={setDialogRichOpen}
|
|
155
|
+
title="헤더 커스텀"
|
|
156
|
+
titleAdornment={<ToastSeverityIcon severity="info" />}
|
|
157
|
+
headerExtra={
|
|
158
|
+
<Button variant="ghost" type="button" onClick={() => setDialogRichOpen(false)} aria-label="닫기">
|
|
159
|
+
✕
|
|
160
|
+
</Button>
|
|
161
|
+
}
|
|
162
|
+
description="titleAdornment · headerExtra — Dialog props 로 조합"
|
|
163
|
+
>
|
|
164
|
+
<Text variant="body">bodyClassName · descriptionClassName 으로 패널 영역 클래스도 조정 가능합니다.</Text>
|
|
165
|
+
<div className="xfc-dialog-footer">
|
|
166
|
+
<Button type="button" variant="primary" onClick={() => setDialogRichOpen(false)}>
|
|
167
|
+
OK
|
|
168
|
+
</Button>
|
|
169
|
+
</div>
|
|
170
|
+
</Dialog>
|
|
171
|
+
|
|
82
172
|
<ConfirmDialog
|
|
83
173
|
open={confirmOpen}
|
|
84
174
|
onOpenChange={setConfirmOpen}
|
|
@@ -106,6 +196,24 @@ export function FrontCoreShowcase() {
|
|
|
106
196
|
onConfirm={() => {}}
|
|
107
197
|
/>
|
|
108
198
|
|
|
199
|
+
<ConfirmDialog
|
|
200
|
+
open={confirmCustomOpen}
|
|
201
|
+
onOpenChange={setConfirmCustomOpen}
|
|
202
|
+
title="버튼 props 확장"
|
|
203
|
+
message="confirmButtonProps / cancelButtonProps 로 icon·className 등 전달"
|
|
204
|
+
confirmLabel="저장"
|
|
205
|
+
cancelLabel="닫기"
|
|
206
|
+
onConfirm={() => setConfirmCustomOpen(false)}
|
|
207
|
+
confirmButtonProps={{
|
|
208
|
+
icon: <CheckIcon />,
|
|
209
|
+
iconPosition: 'start',
|
|
210
|
+
}}
|
|
211
|
+
cancelButtonProps={{
|
|
212
|
+
icon: <span aria-hidden>←</span>,
|
|
213
|
+
iconPosition: 'start',
|
|
214
|
+
}}
|
|
215
|
+
/>
|
|
216
|
+
|
|
109
217
|
<BottomSheet open={sheetOpen} onOpenChange={setSheetOpen} title="BottomSheet" showHandle>
|
|
110
218
|
<Text variant="body">화면 하단에서 올라오는 패널입니다.</Text>
|
|
111
219
|
<Stack direction="row" gap="sm" align="center" style={{ marginTop: 'var(--xfc-space-md)', flexWrap: 'wrap' }}>
|
|
@@ -115,6 +223,21 @@ export function FrontCoreShowcase() {
|
|
|
115
223
|
</Stack>
|
|
116
224
|
</BottomSheet>
|
|
117
225
|
|
|
226
|
+
<BottomSheet
|
|
227
|
+
open={sheetRichOpen}
|
|
228
|
+
onOpenChange={setSheetRichOpen}
|
|
229
|
+
title="액션 시트"
|
|
230
|
+
subtitle="subtitle · headerExtra 로 상단 구성"
|
|
231
|
+
headerExtra={
|
|
232
|
+
<Button variant="ghost" type="button" onClick={() => setSheetRichOpen(false)} aria-label="닫기">
|
|
233
|
+
완료
|
|
234
|
+
</Button>
|
|
235
|
+
}
|
|
236
|
+
showHandle
|
|
237
|
+
>
|
|
238
|
+
<Text variant="body">BottomSheet bodyClassName 등으로 본문 영역도 조정할 수 있습니다.</Text>
|
|
239
|
+
</BottomSheet>
|
|
240
|
+
|
|
118
241
|
<Stack direction="column" gap="lg" align="stretch">
|
|
119
242
|
<Card>
|
|
120
243
|
<Stack direction="column" gap="md" align="stretch">
|
|
@@ -125,24 +248,41 @@ export function FrontCoreShowcase() {
|
|
|
125
248
|
<Text variant="muted">muted</Text>
|
|
126
249
|
<Text variant="small">small</Text>
|
|
127
250
|
<Text variant="accent">accent</Text>
|
|
251
|
+
<Text variant="small" style={{ color: 'var(--xfc-fg-muted)' }}>
|
|
252
|
+
truncate (좁은 영역)
|
|
253
|
+
</Text>
|
|
254
|
+
<div style={{ maxWidth: 200 }}>
|
|
255
|
+
<Text variant="body" truncate title="말줄임 전체 텍스트은 title 속성으로 툴팁">
|
|
256
|
+
아주 긴 한 줄 텍스트는 truncate prop 으로 말줄임 처리합니다.
|
|
257
|
+
</Text>
|
|
258
|
+
</div>
|
|
128
259
|
</Stack>
|
|
129
260
|
</Card>
|
|
130
261
|
|
|
131
262
|
<Card>
|
|
132
263
|
<Stack direction="column" gap="md" align="stretch">
|
|
133
|
-
<SectionTitle>Badge</SectionTitle>
|
|
134
|
-
<Stack direction="row" gap="sm" align="center">
|
|
264
|
+
<SectionTitle>Badge · icon · enabled · onClick</SectionTitle>
|
|
265
|
+
<Stack direction="row" gap="sm" align="center" style={{ flexWrap: 'wrap' }}>
|
|
135
266
|
<Badge tone="neutral">neutral</Badge>
|
|
136
267
|
<Badge tone="accent">accent</Badge>
|
|
137
268
|
<Badge tone="success">success</Badge>
|
|
138
269
|
<Badge tone="danger">danger</Badge>
|
|
270
|
+
<Badge tone="accent" icon={<StarIcon />}>
|
|
271
|
+
icon
|
|
272
|
+
</Badge>
|
|
273
|
+
<Badge tone="neutral" enabled={false}>
|
|
274
|
+
enabled=false
|
|
275
|
+
</Badge>
|
|
276
|
+
<Badge tone="accent" icon={<CheckIcon />} onClick={() => setBadgeClicks((c) => c + 1)}>
|
|
277
|
+
클릭 {badgeClicks}
|
|
278
|
+
</Badge>
|
|
139
279
|
</Stack>
|
|
140
280
|
</Stack>
|
|
141
281
|
</Card>
|
|
142
282
|
|
|
143
283
|
<Card>
|
|
144
284
|
<Stack direction="column" gap="md" align="stretch">
|
|
145
|
-
<SectionTitle>Button</SectionTitle>
|
|
285
|
+
<SectionTitle>Button · enabled · loading · icon</SectionTitle>
|
|
146
286
|
<Stack direction="row" gap="sm" align="center" style={{ flexWrap: 'wrap' }}>
|
|
147
287
|
<Button variant="primary">primary</Button>
|
|
148
288
|
<Button variant="secondary">secondary</Button>
|
|
@@ -152,26 +292,85 @@ export function FrontCoreShowcase() {
|
|
|
152
292
|
<Button variant="primary" disabled>
|
|
153
293
|
disabled
|
|
154
294
|
</Button>
|
|
295
|
+
<Button variant="primary" enabled={false}>
|
|
296
|
+
enabled=false
|
|
297
|
+
</Button>
|
|
298
|
+
<Button variant="primary" icon={<StarIcon />} iconPosition="start">
|
|
299
|
+
아이콘 앞
|
|
300
|
+
</Button>
|
|
301
|
+
<Button variant="outline" icon={<CheckIcon />} iconPosition="end">
|
|
302
|
+
아이콘 뒤
|
|
303
|
+
</Button>
|
|
304
|
+
<Button
|
|
305
|
+
variant="secondary"
|
|
306
|
+
loading={btnLoading}
|
|
307
|
+
onClick={() => {
|
|
308
|
+
setBtnLoading(true);
|
|
309
|
+
window.setTimeout(() => setBtnLoading(false), 1200);
|
|
310
|
+
}}
|
|
311
|
+
>
|
|
312
|
+
loading 데모
|
|
313
|
+
</Button>
|
|
155
314
|
</Stack>
|
|
156
315
|
</Stack>
|
|
157
316
|
</Card>
|
|
158
317
|
|
|
318
|
+
<Card
|
|
319
|
+
title="Card · title / footer"
|
|
320
|
+
footer={
|
|
321
|
+
<Stack direction="row" justify="end" gap="sm" align="center">
|
|
322
|
+
<Button variant="ghost" type="button">
|
|
323
|
+
보조
|
|
324
|
+
</Button>
|
|
325
|
+
<Button variant="primary" type="button">
|
|
326
|
+
주 액션
|
|
327
|
+
</Button>
|
|
328
|
+
</Stack>
|
|
329
|
+
}
|
|
330
|
+
>
|
|
331
|
+
<Text variant="body">
|
|
332
|
+
Card 의 title·footer 가 있으면 본문이 .xfc-card__body 로 감싸져 패딩이 맞춰집니다. 루트에 onClick 등
|
|
333
|
+
HTML 속성을 그대로 넘길 수 있습니다.
|
|
334
|
+
</Text>
|
|
335
|
+
</Card>
|
|
336
|
+
|
|
159
337
|
<Card>
|
|
160
338
|
<Stack direction="column" gap="md" align="stretch">
|
|
161
|
-
<SectionTitle>Input</SectionTitle>
|
|
339
|
+
<SectionTitle>Input · description · invalid · enabled</SectionTitle>
|
|
162
340
|
<label htmlFor={`${uid}-email`}>
|
|
163
341
|
<Text as="span" variant="labelBlock">
|
|
164
342
|
이메일
|
|
165
343
|
</Text>
|
|
166
344
|
</label>
|
|
167
345
|
<Input id={`${uid}-email`} type="email" placeholder="you@example.com" autoComplete="email" />
|
|
168
|
-
<Input
|
|
346
|
+
<Input
|
|
347
|
+
placeholder="형식 오류 예시"
|
|
348
|
+
invalid
|
|
349
|
+
description="invalid + description (aria-describedby 자동)"
|
|
350
|
+
defaultValue="not-an-email"
|
|
351
|
+
/>
|
|
352
|
+
<Input placeholder="enabled=false" enabled={false} />
|
|
353
|
+
<Input placeholder="비활성(disabled)" disabled />
|
|
169
354
|
</Stack>
|
|
170
355
|
</Card>
|
|
171
356
|
|
|
172
357
|
<Card>
|
|
173
358
|
<Stack direction="column" gap="md" align="stretch">
|
|
174
|
-
<SectionTitle>Box · Stack ·
|
|
359
|
+
<SectionTitle>Box · Stack · 레이아웃 props</SectionTitle>
|
|
360
|
+
<Box
|
|
361
|
+
as="section"
|
|
362
|
+
padding="md"
|
|
363
|
+
style={{ border: '1px dashed var(--xfc-border-strong)', borderRadius: 'var(--xfc-radius-xs)' }}
|
|
364
|
+
aria-label="섹션 박스 예시"
|
|
365
|
+
>
|
|
366
|
+
<Text variant="small">Box as="section" + padding</Text>
|
|
367
|
+
</Box>
|
|
368
|
+
<Stack direction="row" justify="between" align="center" wrap={false} gap="md">
|
|
369
|
+
<Text variant="small">justify="between"</Text>
|
|
370
|
+
<Button variant="ghost" type="button">
|
|
371
|
+
오른쪽
|
|
372
|
+
</Button>
|
|
373
|
+
</Stack>
|
|
175
374
|
<Stack direction="row" gap="md" align="stretch" style={{ flexWrap: 'wrap' }}>
|
|
176
375
|
<Box padding="md" style={{ border: '1px dashed var(--xfc-border-strong)', borderRadius: 'var(--xfc-radius-xs)' }}>
|
|
177
376
|
<Text variant="small">Box padding md</Text>
|
|
@@ -185,20 +384,29 @@ export function FrontCoreShowcase() {
|
|
|
185
384
|
|
|
186
385
|
<Card>
|
|
187
386
|
<Stack direction="column" gap="md" align="stretch">
|
|
188
|
-
<SectionTitle>ToastSeverityIcon</SectionTitle>
|
|
387
|
+
<SectionTitle>ToastSeverityIcon · className / style</SectionTitle>
|
|
189
388
|
<Stack direction="row" gap="lg" align="center" style={{ color: 'var(--xfc-fg)' }}>
|
|
190
389
|
<ToastSeverityIcon severity="info" />
|
|
191
390
|
<ToastSeverityIcon severity="success" />
|
|
192
391
|
<ToastSeverityIcon severity="warn" />
|
|
193
392
|
<ToastSeverityIcon severity="error" />
|
|
393
|
+
<ToastSeverityIcon severity="info" style={{ opacity: 0.45 }} />
|
|
194
394
|
</Stack>
|
|
195
395
|
</Stack>
|
|
196
396
|
</Card>
|
|
197
397
|
|
|
198
398
|
<Card>
|
|
199
399
|
<Stack direction="column" gap="md" align="stretch">
|
|
200
|
-
<SectionTitle>Toast (단일)</SectionTitle>
|
|
201
|
-
<Toast severity="info" message="
|
|
400
|
+
<SectionTitle>Toast (단일) · icon · dismissible</SectionTitle>
|
|
401
|
+
<Toast severity="info" message="기본 닫기 버튼" onDismiss={() => {}} />
|
|
402
|
+
<Toast severity="warn" message="아이콘 숨김 (icon=null)" icon={null} onDismiss={() => {}} />
|
|
403
|
+
<Toast
|
|
404
|
+
severity="success"
|
|
405
|
+
message="커스텀 dismiss 라벨"
|
|
406
|
+
dismissAriaLabel="토스트 제거"
|
|
407
|
+
onDismiss={() => {}}
|
|
408
|
+
/>
|
|
409
|
+
<Toast severity="error" message="닫기 없음" dismissible={false} />
|
|
202
410
|
</Stack>
|
|
203
411
|
</Card>
|
|
204
412
|
|
|
@@ -206,12 +414,14 @@ export function FrontCoreShowcase() {
|
|
|
206
414
|
<Stack direction="column" gap="md" align="stretch">
|
|
207
415
|
<SectionTitle>Dialog · ConfirmDialog · BottomSheet</SectionTitle>
|
|
208
416
|
<Text variant="small" style={{ color: 'var(--xfc-fg-muted)' }}>
|
|
209
|
-
document.body 포털 · z-index 10050
|
|
210
|
-
.xfc-bottom-sheet-* 로 테마 조정.
|
|
417
|
+
document.body 포털 · z-index 10050. base.css 의 .xfc-dialog-* / .xfc-bottom-sheet-* 로 테마 조정.
|
|
211
418
|
</Text>
|
|
212
419
|
<Stack direction="row" gap="sm" align="center" style={{ flexWrap: 'wrap' }}>
|
|
213
420
|
<Button type="button" variant="secondary" onClick={() => setDialogOpen(true)}>
|
|
214
|
-
Dialog
|
|
421
|
+
Dialog
|
|
422
|
+
</Button>
|
|
423
|
+
<Button type="button" variant="secondary" onClick={() => setDialogRichOpen(true)}>
|
|
424
|
+
Dialog (헤더 커스텀)
|
|
215
425
|
</Button>
|
|
216
426
|
<Button type="button" variant="secondary" onClick={() => setConfirmOpen(true)}>
|
|
217
427
|
Confirm
|
|
@@ -222,9 +432,15 @@ export function FrontCoreShowcase() {
|
|
|
222
432
|
<Button type="button" variant="secondary" onClick={() => setConfirmLoadingDemo(true)}>
|
|
223
433
|
Confirm (loading)
|
|
224
434
|
</Button>
|
|
435
|
+
<Button type="button" variant="secondary" onClick={() => setConfirmCustomOpen(true)}>
|
|
436
|
+
Confirm (버튼 props)
|
|
437
|
+
</Button>
|
|
225
438
|
<Button type="button" variant="secondary" onClick={() => setSheetOpen(true)}>
|
|
226
439
|
BottomSheet
|
|
227
440
|
</Button>
|
|
441
|
+
<Button type="button" variant="secondary" onClick={() => setSheetRichOpen(true)}>
|
|
442
|
+
BottomSheet (subtitle·extra)
|
|
443
|
+
</Button>
|
|
228
444
|
</Stack>
|
|
229
445
|
</Stack>
|
|
230
446
|
</Card>
|
|
@@ -233,7 +449,7 @@ export function FrontCoreShowcase() {
|
|
|
233
449
|
<Stack direction="column" gap="md" align="stretch">
|
|
234
450
|
<SectionTitle>ToastList · InlineErrorList · LoadingOverlay</SectionTitle>
|
|
235
451
|
<Text variant="small" style={{ color: 'var(--xfc-fg-muted)' }}>
|
|
236
|
-
ToastList / InlineErrorList / LoadingOverlay 는 화면 고정 레이어입니다.
|
|
452
|
+
ToastList / InlineErrorList / LoadingOverlay 는 화면 고정 레이어입니다.
|
|
237
453
|
</Text>
|
|
238
454
|
<Stack direction="row" gap="sm" align="center" style={{ flexWrap: 'wrap' }}>
|
|
239
455
|
<Button type="button" variant="secondary" onClick={() => pushToast('info')}>
|
|
@@ -248,15 +464,24 @@ export function FrontCoreShowcase() {
|
|
|
248
464
|
<Button type="button" variant="secondary" onClick={() => pushToast('error')}>
|
|
249
465
|
toast error
|
|
250
466
|
</Button>
|
|
467
|
+
<Button type="button" variant="secondary" onClick={pushToastCustomIcon}>
|
|
468
|
+
toast 커스텀 icon
|
|
469
|
+
</Button>
|
|
251
470
|
</Stack>
|
|
252
471
|
<Stack direction="row" gap="sm" align="center" style={{ flexWrap: 'wrap' }}>
|
|
253
472
|
<Button type="button" variant="outline" onClick={addError}>
|
|
254
473
|
에러 배너 추가
|
|
255
474
|
</Button>
|
|
475
|
+
<Button type="button" variant="outline" onClick={addErrorRich}>
|
|
476
|
+
에러 (icon·onClick)
|
|
477
|
+
</Button>
|
|
256
478
|
<Button type="button" variant="outline" onClick={() => setLoading((v) => !v)}>
|
|
257
479
|
로딩 오버레이 {loading ? '끄기' : '켜기'}
|
|
258
480
|
</Button>
|
|
259
481
|
</Stack>
|
|
482
|
+
<Text variant="label">
|
|
483
|
+
LoadingOverlay: title · message · ariaLabel · spinner(null 로 숨김) · enabled
|
|
484
|
+
</Text>
|
|
260
485
|
</Stack>
|
|
261
486
|
</Card>
|
|
262
487
|
</Stack>
|