@xfilecom/front-core 0.1.0 → 0.2.5

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.
Files changed (49) hide show
  1. package/dist/base.css +518 -0
  2. package/dist/components/Badge.d.ts +6 -0
  3. package/dist/components/Badge.js +8 -0
  4. package/dist/components/Box.d.ts +6 -0
  5. package/dist/components/Box.js +9 -0
  6. package/dist/components/Button.d.ts +5 -0
  7. package/dist/components/Button.js +8 -0
  8. package/dist/components/Card.d.ts +5 -0
  9. package/dist/components/Card.js +8 -0
  10. package/dist/components/InlineErrorList.d.ts +12 -0
  11. package/dist/components/InlineErrorList.js +11 -0
  12. package/dist/components/Input.d.ts +3 -0
  13. package/dist/components/Input.js +9 -0
  14. package/dist/components/LoadingOverlay.d.ts +8 -0
  15. package/dist/components/LoadingOverlay.js +11 -0
  16. package/dist/components/Stack.d.ts +8 -0
  17. package/dist/components/Stack.js +16 -0
  18. package/dist/components/Text.d.ts +22 -0
  19. package/dist/components/Text.js +20 -0
  20. package/dist/components/Toast.d.ts +25 -0
  21. package/dist/components/Toast.js +42 -0
  22. package/dist/components/atoms/Badge.d.ts +6 -0
  23. package/dist/components/atoms/Badge.js +8 -0
  24. package/dist/components/atoms/Box.d.ts +6 -0
  25. package/dist/components/atoms/Box.js +9 -0
  26. package/dist/components/atoms/Button.d.ts +5 -0
  27. package/dist/components/atoms/Button.js +8 -0
  28. package/dist/components/atoms/Card.d.ts +5 -0
  29. package/dist/components/atoms/Card.js +8 -0
  30. package/dist/components/atoms/InlineErrorList.d.ts +12 -0
  31. package/dist/components/atoms/InlineErrorList.js +11 -0
  32. package/dist/components/atoms/Input.d.ts +3 -0
  33. package/dist/components/atoms/Input.js +9 -0
  34. package/dist/components/atoms/LoadingOverlay.d.ts +8 -0
  35. package/dist/components/atoms/LoadingOverlay.js +11 -0
  36. package/dist/components/atoms/Stack.d.ts +8 -0
  37. package/dist/components/atoms/Stack.js +16 -0
  38. package/dist/components/atoms/Text.d.ts +22 -0
  39. package/dist/components/atoms/Text.js +20 -0
  40. package/dist/components/atoms/Toast.d.ts +25 -0
  41. package/dist/components/atoms/Toast.js +42 -0
  42. package/dist/components/atoms/index.d.ts +14 -0
  43. package/dist/components/atoms/index.js +29 -0
  44. package/dist/components/index.d.ts +1 -0
  45. package/dist/components/index.js +17 -0
  46. package/dist/index.d.ts +37 -3
  47. package/dist/index.js +50 -4
  48. package/dist/tokens.css +80 -4
  49. package/package.json +18 -7
package/dist/base.css ADDED
@@ -0,0 +1,518 @@
1
+ /**
2
+ * Global base + atomic classes for @xfilecom/front-core components.
3
+ *
4
+ * 권장 로드 순서 (xframe web/shared 와 함께 쓸 때):
5
+ * 1. @xfilecom/front-core/tokens.css
6
+ * 2. web/shared/src/styles/xfc-theme.css — 앱별 :root --xfc-* 덮어쓰기(선택)
7
+ * 3. 이 파일 (base.css) — atoms(.xfc-*)
8
+ * 4. web/shared/src/styles/app.css — 레이아웃 + atoms 클래스 오버라이드(선택)
9
+ *
10
+ * CSS 변수는 사용 시점에 계산되므로, (2)에서 바꾼 --xfc-* 는 (3)의 atoms 규칙에 반영됩니다.
11
+ */
12
+
13
+ *,
14
+ *::before,
15
+ *::after {
16
+ box-sizing: border-box;
17
+ }
18
+
19
+ body {
20
+ margin: 0;
21
+ min-height: 100dvh;
22
+ font-family: var(--xfc-font-sans);
23
+ font-size: var(--xfc-text-body);
24
+ line-height: var(--xfc-leading-normal);
25
+ color: var(--xfc-fg);
26
+ background: var(--xfc-bg);
27
+ -webkit-font-smoothing: antialiased;
28
+ }
29
+
30
+ a {
31
+ color: var(--xfc-accent);
32
+ text-decoration-thickness: 1px;
33
+ text-underline-offset: 2px;
34
+ }
35
+
36
+ a:hover {
37
+ color: var(--xfc-accent-hover);
38
+ }
39
+
40
+ /* —— Typography atoms —— */
41
+
42
+ .xfc-text {
43
+ margin: 0;
44
+ color: var(--xfc-fg);
45
+ }
46
+
47
+ .xfc-text--title {
48
+ font-size: var(--xfc-text-display);
49
+ font-weight: 700;
50
+ line-height: var(--xfc-leading-tight);
51
+ letter-spacing: -0.02em;
52
+ }
53
+
54
+ .xfc-text--appbar {
55
+ font-size: var(--xfc-text-appbar);
56
+ font-weight: 700;
57
+ line-height: 1.2;
58
+ color: var(--xfc-fg-strong);
59
+ }
60
+
61
+ .xfc-text--section {
62
+ font-size: var(--xfc-text-section);
63
+ font-weight: 700;
64
+ line-height: 1.3;
65
+ color: var(--xfc-fg-label);
66
+ }
67
+
68
+ .xfc-text--subtitle {
69
+ font-size: var(--xfc-text-section);
70
+ font-weight: 600;
71
+ line-height: var(--xfc-leading-tight);
72
+ }
73
+
74
+ .xfc-text--body {
75
+ font-size: var(--xfc-text-body);
76
+ }
77
+
78
+ .xfc-text--small {
79
+ font-size: var(--xfc-text-small);
80
+ }
81
+
82
+ .xfc-text--muted {
83
+ color: var(--xfc-fg-muted);
84
+ }
85
+
86
+ .xfc-text--accent {
87
+ color: var(--xfc-accent);
88
+ }
89
+
90
+ .xfc-text--label-block {
91
+ font-size: var(--xfc-text-body);
92
+ line-height: var(--xfc-leading-label);
93
+ color: var(--xfc-fg-label);
94
+ }
95
+
96
+ /* —— Layout —— */
97
+
98
+ .xfc-stack {
99
+ display: flex;
100
+ }
101
+
102
+ .xfc-stack--column {
103
+ flex-direction: column;
104
+ }
105
+
106
+ .xfc-stack--row {
107
+ flex-direction: row;
108
+ flex-wrap: wrap;
109
+ }
110
+
111
+ .xfc-stack--gap-none {
112
+ gap: 0;
113
+ }
114
+ .xfc-stack--gap-sm {
115
+ gap: var(--xfc-space-sm);
116
+ }
117
+ .xfc-stack--gap-md {
118
+ gap: var(--xfc-space-md);
119
+ }
120
+ .xfc-stack--gap-lg {
121
+ gap: var(--xfc-space-lg);
122
+ }
123
+
124
+ .xfc-stack--align-start {
125
+ align-items: flex-start;
126
+ }
127
+ .xfc-stack--align-center {
128
+ align-items: center;
129
+ }
130
+ .xfc-stack--align-stretch {
131
+ align-items: stretch;
132
+ }
133
+
134
+ .xfc-box {
135
+ display: block;
136
+ }
137
+
138
+ .xfc-box--p-sm {
139
+ padding: var(--xfc-space-sm);
140
+ }
141
+ .xfc-box--p-md {
142
+ padding: var(--xfc-space-md);
143
+ }
144
+ .xfc-box--p-lg {
145
+ padding: var(--xfc-space-lg);
146
+ }
147
+
148
+ .xfc-card {
149
+ background: var(--xfc-bg-elevated);
150
+ color: var(--xfc-fg);
151
+ border: 1px solid var(--xfc-border);
152
+ border-radius: var(--xfc-radius-md);
153
+ box-shadow: var(--xfc-card-shadow);
154
+ }
155
+
156
+ /* —— Buttons (피그마: 80×34 primary, radius 5) —— */
157
+
158
+ .xfc-btn {
159
+ display: inline-flex;
160
+ align-items: center;
161
+ justify-content: center;
162
+ gap: var(--xfc-space-xs);
163
+ min-height: 34px;
164
+ padding: 0 var(--xfc-space-lg);
165
+ font: inherit;
166
+ font-size: var(--xfc-text-body);
167
+ font-weight: 700;
168
+ line-height: 1;
169
+ border-radius: var(--xfc-radius-xs);
170
+ border: 1px solid transparent;
171
+ cursor: pointer;
172
+ transition:
173
+ background 0.15s ease,
174
+ color 0.15s ease,
175
+ border-color 0.15s ease,
176
+ box-shadow 0.15s ease;
177
+ }
178
+
179
+ .xfc-btn:focus-visible {
180
+ outline: none;
181
+ box-shadow: var(--xfc-focus-ring);
182
+ }
183
+
184
+ .xfc-btn:disabled {
185
+ opacity: 0.5;
186
+ cursor: not-allowed;
187
+ }
188
+
189
+ .xfc-btn--primary {
190
+ background: var(--xfc-accent);
191
+ color: var(--xfc-accent-fg);
192
+ }
193
+
194
+ .xfc-btn--primary:hover:not(:disabled) {
195
+ background: var(--xfc-accent-hover);
196
+ }
197
+
198
+ .xfc-btn--secondary {
199
+ background: var(--xfc-bg-elevated);
200
+ color: var(--xfc-fg);
201
+ border-color: var(--xfc-border-strong);
202
+ }
203
+
204
+ .xfc-btn--secondary:hover:not(:disabled) {
205
+ border-color: var(--xfc-fg-muted);
206
+ }
207
+
208
+ .xfc-btn--outline {
209
+ background: var(--xfc-bg-elevated);
210
+ color: var(--xfc-accent);
211
+ border-color: var(--xfc-accent);
212
+ }
213
+
214
+ .xfc-btn--outline:hover:not(:disabled) {
215
+ background: color-mix(in srgb, var(--xfc-accent) 6%, transparent);
216
+ }
217
+
218
+ .xfc-btn--muted {
219
+ background: var(--xfc-bg-disabled);
220
+ color: var(--xfc-fg-muted);
221
+ border-color: transparent;
222
+ }
223
+
224
+ .xfc-btn--muted:hover:not(:disabled) {
225
+ filter: brightness(0.97);
226
+ }
227
+
228
+ .xfc-btn--ghost {
229
+ background: transparent;
230
+ color: var(--xfc-accent);
231
+ }
232
+
233
+ .xfc-btn--ghost:hover:not(:disabled) {
234
+ background: color-mix(in srgb, var(--xfc-accent) 8%, transparent);
235
+ }
236
+
237
+ /* —— Inputs (로그인 필드 48px, border #D4D4D4) —— */
238
+
239
+ .xfc-input {
240
+ display: block;
241
+ width: 100%;
242
+ min-height: 48px;
243
+ padding: 0 var(--xfc-space-lg);
244
+ font: inherit;
245
+ font-size: var(--xfc-text-input);
246
+ color: var(--xfc-fg);
247
+ background: var(--xfc-bg-elevated);
248
+ border: 1px solid var(--xfc-border-strong);
249
+ border-radius: var(--xfc-radius-xs);
250
+ box-shadow: var(--xfc-input-shadow);
251
+ transition:
252
+ border-color 0.15s ease,
253
+ box-shadow 0.15s ease;
254
+ }
255
+
256
+ .xfc-input::placeholder {
257
+ color: var(--xfc-fg-placeholder);
258
+ }
259
+
260
+ .xfc-input:focus {
261
+ outline: none;
262
+ border-color: var(--xfc-accent);
263
+ box-shadow: var(--xfc-input-shadow), var(--xfc-focus-ring);
264
+ }
265
+
266
+ .xfc-input:disabled {
267
+ opacity: 0.6;
268
+ cursor: not-allowed;
269
+ }
270
+
271
+ /* —— Badges —— */
272
+
273
+ .xfc-badge {
274
+ display: inline-flex;
275
+ align-items: center;
276
+ padding: 2px var(--xfc-space-sm);
277
+ font-size: var(--xfc-text-small);
278
+ font-weight: 600;
279
+ line-height: 1.3;
280
+ border-radius: var(--xfc-radius-xs);
281
+ }
282
+
283
+ .xfc-badge--neutral {
284
+ background: var(--xfc-border);
285
+ color: var(--xfc-fg);
286
+ }
287
+
288
+ .xfc-badge--accent {
289
+ background: color-mix(in srgb, var(--xfc-accent) 18%, transparent);
290
+ color: var(--xfc-accent-hover);
291
+ }
292
+
293
+ .xfc-badge--success {
294
+ background: var(--xfc-success-bg);
295
+ color: var(--xfc-success);
296
+ }
297
+
298
+ .xfc-badge--danger {
299
+ background: color-mix(in srgb, var(--xfc-danger) 15%, transparent);
300
+ color: var(--xfc-danger);
301
+ }
302
+
303
+ /* —— Global loading overlay (business-promotion global-indicator) —— */
304
+
305
+ .xfc-loading-overlay {
306
+ position: fixed;
307
+ inset: 0;
308
+ z-index: 9999;
309
+ display: flex;
310
+ flex-direction: column;
311
+ align-items: center;
312
+ justify-content: center;
313
+ gap: var(--xfc-space-md);
314
+ padding: var(--xfc-space-lg);
315
+ box-sizing: border-box;
316
+ background: transparent;
317
+ pointer-events: auto;
318
+ animation: xfc-loading-overlay-enter 0.2s ease-out;
319
+ }
320
+
321
+ .xfc-loading-overlay-spinner {
322
+ width: 48px;
323
+ height: 48px;
324
+ border: 4px solid color-mix(in srgb, var(--xfc-accent) 22%, transparent);
325
+ border-top-color: var(--xfc-accent);
326
+ border-radius: 50%;
327
+ animation: xfc-loading-overlay-spin 0.68s linear infinite;
328
+ flex-shrink: 0;
329
+ box-shadow:
330
+ 0 0 0 2px rgba(255, 255, 255, 0.95),
331
+ 0 6px 24px rgba(0, 0, 0, 0.14);
332
+ filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.08));
333
+ }
334
+
335
+ .xfc-loading-overlay-message {
336
+ margin: 0;
337
+ max-width: min(280px, 100%);
338
+ font-size: var(--xfc-text-small);
339
+ font-weight: 600;
340
+ line-height: 1.4;
341
+ color: var(--xfc-fg);
342
+ text-align: center;
343
+ text-shadow:
344
+ 0 0 10px rgba(255, 255, 255, 1),
345
+ 0 0 3px rgba(255, 255, 255, 1),
346
+ 0 1px 2px rgba(0, 0, 0, 0.08);
347
+ }
348
+
349
+ @keyframes xfc-loading-overlay-enter {
350
+ from {
351
+ opacity: 0;
352
+ }
353
+ to {
354
+ opacity: 1;
355
+ }
356
+ }
357
+
358
+ @keyframes xfc-loading-overlay-spin {
359
+ to {
360
+ transform: rotate(360deg);
361
+ }
362
+ }
363
+
364
+ @media (prefers-reduced-motion: reduce) {
365
+ .xfc-loading-overlay {
366
+ animation: none;
367
+ }
368
+
369
+ .xfc-loading-overlay-spinner {
370
+ animation-duration: 1.4s;
371
+ }
372
+ }
373
+
374
+ /* —— Toasts (business-promotion toast-list) —— */
375
+
376
+ .xfc-toast-list {
377
+ position: fixed;
378
+ bottom: var(--xfc-space-md);
379
+ right: var(--xfc-space-md);
380
+ z-index: 9998;
381
+ display: flex;
382
+ flex-direction: column;
383
+ gap: var(--xfc-space-sm);
384
+ max-width: 360px;
385
+ }
386
+
387
+ .xfc-toast {
388
+ display: inline-flex;
389
+ width: 100%;
390
+ box-sizing: border-box;
391
+ align-items: flex-start;
392
+ justify-content: flex-start;
393
+ gap: var(--xfc-space-lg);
394
+ padding: var(--xfc-space-lg);
395
+ border-radius: var(--xfc-toast-radius);
396
+ color: #fff;
397
+ font-family: var(--xfc-font-sans);
398
+ font-size: var(--xfc-text-body);
399
+ font-weight: 400;
400
+ line-height: 1.25;
401
+ box-shadow: var(--xfc-toast-shadow);
402
+ transition: opacity 0.2s ease;
403
+ }
404
+
405
+ .xfc-toast--info {
406
+ background: var(--xfc-toast-info-bg);
407
+ }
408
+
409
+ .xfc-toast--success {
410
+ background: var(--xfc-toast-success-bg);
411
+ }
412
+
413
+ .xfc-toast--warn {
414
+ background: var(--xfc-toast-warn-bg);
415
+ }
416
+
417
+ .xfc-toast--error {
418
+ background: var(--xfc-toast-error-bg);
419
+ }
420
+
421
+ .xfc-toast__icon {
422
+ flex-shrink: 0;
423
+ width: 20px;
424
+ height: 20px;
425
+ display: flex;
426
+ align-items: center;
427
+ justify-content: center;
428
+ color: #fff;
429
+ }
430
+
431
+ .xfc-toast__icon svg {
432
+ display: block;
433
+ }
434
+
435
+ .xfc-toast__message {
436
+ flex: 1 1 0;
437
+ min-width: 0;
438
+ overflow-wrap: break-word;
439
+ word-wrap: break-word;
440
+ }
441
+
442
+ .xfc-toast-dismiss {
443
+ flex-shrink: 0;
444
+ width: 20px;
445
+ height: 20px;
446
+ padding: 0;
447
+ margin: 0;
448
+ border: none;
449
+ background: none;
450
+ color: #fff;
451
+ cursor: pointer;
452
+ display: flex;
453
+ align-items: center;
454
+ justify-content: center;
455
+ opacity: 0.85;
456
+ }
457
+
458
+ .xfc-toast-dismiss svg {
459
+ display: block;
460
+ }
461
+
462
+ .xfc-toast-dismiss:hover {
463
+ opacity: 1;
464
+ }
465
+
466
+ .xfc-toast-dismiss:focus-visible {
467
+ outline: none;
468
+ box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.85);
469
+ border-radius: 2px;
470
+ }
471
+
472
+ /* —— Inline error banners (business-promotion error-list) —— */
473
+
474
+ .xfc-inline-error-list {
475
+ position: fixed;
476
+ top: var(--xfc-space-md);
477
+ left: 50%;
478
+ transform: translateX(-50%);
479
+ z-index: 9998;
480
+ display: flex;
481
+ flex-direction: column;
482
+ gap: var(--xfc-space-sm);
483
+ max-width: 400px;
484
+ }
485
+
486
+ .xfc-inline-error-item {
487
+ display: flex;
488
+ align-items: center;
489
+ justify-content: space-between;
490
+ gap: var(--xfc-space-sm);
491
+ padding: var(--xfc-space-md) var(--xfc-space-lg);
492
+ background: var(--xfc-danger);
493
+ color: var(--xfc-accent-fg);
494
+ border-radius: var(--xfc-radius-md);
495
+ font-size: var(--xfc-text-small);
496
+ box-shadow: var(--xfc-toast-shadow);
497
+ }
498
+
499
+ .xfc-inline-error-dismiss {
500
+ background: none;
501
+ border: none;
502
+ color: inherit;
503
+ cursor: pointer;
504
+ font-size: 1.25rem;
505
+ line-height: 1;
506
+ opacity: 0.8;
507
+ padding: 0 var(--xfc-space-xs);
508
+ }
509
+
510
+ .xfc-inline-error-dismiss:hover {
511
+ opacity: 1;
512
+ }
513
+
514
+ .xfc-inline-error-dismiss:focus-visible {
515
+ outline: none;
516
+ box-shadow: var(--xfc-focus-ring);
517
+ border-radius: var(--xfc-radius-xs);
518
+ }
@@ -0,0 +1,6 @@
1
+ import type { HTMLAttributes, ReactNode } from 'react';
2
+ export type BadgeProps = HTMLAttributes<HTMLSpanElement> & {
3
+ tone?: 'neutral' | 'accent' | 'success' | 'danger';
4
+ children?: ReactNode;
5
+ };
6
+ export declare function Badge({ tone, className, children, ...rest }: BadgeProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Badge = Badge;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ function Badge({ tone = 'neutral', className = '', children, ...rest }) {
6
+ const cls = ['xfc-badge', `xfc-badge--${tone}`, className].filter(Boolean).join(' ');
7
+ return ((0, jsx_runtime_1.jsx)("span", { className: cls, ...rest, children: children }));
8
+ }
@@ -0,0 +1,6 @@
1
+ import type { HTMLAttributes, ReactNode } from 'react';
2
+ export type BoxProps = HTMLAttributes<HTMLDivElement> & {
3
+ padding?: 'none' | 'sm' | 'md' | 'lg';
4
+ children?: ReactNode;
5
+ };
6
+ export declare function Box({ padding, className, children, ...rest }: BoxProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Box = Box;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ function Box({ padding = 'none', className = '', children, ...rest }) {
6
+ const padClass = padding === 'none' ? '' : padding === 'sm' ? 'xfc-box--p-sm' : padding === 'md' ? 'xfc-box--p-md' : 'xfc-box--p-lg';
7
+ const cls = ['xfc-box', padClass, className].filter(Boolean).join(' ');
8
+ return ((0, jsx_runtime_1.jsx)("div", { className: cls, ...rest, children: children }));
9
+ }
@@ -0,0 +1,5 @@
1
+ import type { ButtonHTMLAttributes } from 'react';
2
+ export type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
3
+ variant?: 'primary' | 'secondary' | 'outline' | 'muted' | 'ghost';
4
+ };
5
+ export declare function Button({ variant, className, type, ...rest }: ButtonProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Button = Button;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ function Button({ variant = 'primary', className = '', type = 'button', ...rest }) {
6
+ const cls = ['xfc-btn', `xfc-btn--${variant}`, className].filter(Boolean).join(' ');
7
+ return (0, jsx_runtime_1.jsx)("button", { type: type, className: cls, ...rest });
8
+ }
@@ -0,0 +1,5 @@
1
+ import type { HTMLAttributes, ReactNode } from 'react';
2
+ export type CardProps = HTMLAttributes<HTMLDivElement> & {
3
+ children?: ReactNode;
4
+ };
5
+ export declare function Card({ className, children, ...rest }: CardProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Card = Card;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ function Card({ className = '', children, ...rest }) {
6
+ const cls = ['xfc-card', className].filter(Boolean).join(' ');
7
+ return ((0, jsx_runtime_1.jsx)("div", { className: cls, ...rest, children: children }));
8
+ }
@@ -0,0 +1,12 @@
1
+ import type { ReactNode } from 'react';
2
+ export type InlineErrorEntry = {
3
+ id: string;
4
+ message: ReactNode;
5
+ };
6
+ export type InlineErrorListProps = {
7
+ errors: InlineErrorEntry[];
8
+ onDismiss: (id: string) => void;
9
+ className?: string;
10
+ };
11
+ /** 상단 고정 에러 배너 목록 (business-promotion `ErrorList` UI, MobX 없음). */
12
+ export declare function InlineErrorList({ errors, onDismiss, className }: InlineErrorListProps): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InlineErrorList = InlineErrorList;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ /** 상단 고정 에러 배너 목록 (business-promotion `ErrorList` UI, MobX 없음). */
6
+ function InlineErrorList({ errors, onDismiss, className = '' }) {
7
+ if (!errors.length)
8
+ return null;
9
+ const listCls = ['xfc-inline-error-list', className].filter(Boolean).join(' ');
10
+ return ((0, jsx_runtime_1.jsx)("div", { className: listCls, "aria-live": "assertive", role: "alert", children: errors.map((e) => ((0, jsx_runtime_1.jsxs)("div", { className: "xfc-inline-error-item", children: [(0, jsx_runtime_1.jsx)("span", { children: e.message }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "xfc-inline-error-dismiss", onClick: () => onDismiss(e.id), "aria-label": "\uB2EB\uAE30", children: "\u00D7" })] }, e.id))) }));
11
+ }
@@ -0,0 +1,3 @@
1
+ import { type InputHTMLAttributes } from 'react';
2
+ export type InputProps = InputHTMLAttributes<HTMLInputElement>;
3
+ export declare const Input: import("react").ForwardRefExoticComponent<InputProps & import("react").RefAttributes<HTMLInputElement>>;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Input = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ exports.Input = (0, react_1.forwardRef)(function Input({ className = '', ...rest }, ref) {
7
+ const cls = ['xfc-input', className].filter(Boolean).join(' ');
8
+ return (0, jsx_runtime_1.jsx)("input", { ref: ref, className: cls, ...rest });
9
+ });
@@ -0,0 +1,8 @@
1
+ import type { ReactNode } from 'react';
2
+ export type LoadingOverlayProps = {
3
+ active: boolean;
4
+ message?: ReactNode;
5
+ className?: string;
6
+ };
7
+ /** 전역 로딩 오버레이 (business-promotion `GlobalIndicator` UI, 상태는 부모에서 주입). */
8
+ export declare function LoadingOverlay({ active, message, className }: LoadingOverlayProps): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LoadingOverlay = LoadingOverlay;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ /** 전역 로딩 오버레이 (business-promotion `GlobalIndicator` UI, 상태는 부모에서 주입). */
6
+ function LoadingOverlay({ active, message, className = '' }) {
7
+ if (!active)
8
+ return null;
9
+ const root = ['xfc-loading-overlay', className].filter(Boolean).join(' ');
10
+ return ((0, jsx_runtime_1.jsxs)("div", { className: root, role: "status", "aria-live": "polite", "aria-busy": "true", children: [(0, jsx_runtime_1.jsx)("span", { className: "xfc-loading-overlay-spinner", "aria-hidden": true }), message ? (0, jsx_runtime_1.jsx)("p", { className: "xfc-loading-overlay-message", children: message }) : null] }));
11
+ }
@@ -0,0 +1,8 @@
1
+ import type { HTMLAttributes, ReactNode } from 'react';
2
+ export type StackProps = HTMLAttributes<HTMLDivElement> & {
3
+ direction?: 'row' | 'column';
4
+ gap?: 'none' | 'sm' | 'md' | 'lg';
5
+ align?: 'start' | 'center' | 'stretch';
6
+ children?: ReactNode;
7
+ };
8
+ export declare function Stack({ direction, gap, align, className, children, ...rest }: StackProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Stack = Stack;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ function Stack({ direction = 'column', gap = 'md', align = 'start', className = '', children, ...rest }) {
6
+ const cls = [
7
+ 'xfc-stack',
8
+ direction === 'column' ? 'xfc-stack--column' : 'xfc-stack--row',
9
+ `xfc-stack--gap-${gap}`,
10
+ `xfc-stack--align-${align}`,
11
+ className,
12
+ ]
13
+ .filter(Boolean)
14
+ .join(' ');
15
+ return ((0, jsx_runtime_1.jsx)("div", { className: cls, ...rest, children: children }));
16
+ }
@@ -0,0 +1,22 @@
1
+ import { type ElementType, type HTMLAttributes, type ReactNode } from 'react';
2
+ declare const variantClass: {
3
+ readonly title: "xfc-text xfc-text--title";
4
+ readonly appbar: "xfc-text xfc-text--appbar";
5
+ readonly section: "xfc-text xfc-text--section";
6
+ readonly subtitle: "xfc-text xfc-text--subtitle";
7
+ readonly body: "xfc-text xfc-text--body";
8
+ readonly muted: "xfc-text xfc-text--body xfc-text--muted";
9
+ readonly small: "xfc-text xfc-text--small";
10
+ readonly label: "xfc-text xfc-text--small xfc-text--muted";
11
+ readonly labelBlock: "xfc-text xfc-text--label-block";
12
+ readonly accent: "xfc-text xfc-text--body xfc-text--accent";
13
+ };
14
+ export type TextVariant = keyof typeof variantClass;
15
+ export type TextProps = {
16
+ as?: ElementType;
17
+ variant?: TextVariant;
18
+ children?: ReactNode;
19
+ className?: string;
20
+ } & HTMLAttributes<HTMLElement>;
21
+ export declare function Text({ as, variant, className, children, ...rest }: TextProps): import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>>;
22
+ export {};