@xfilecom/xframe 0.1.29 → 0.1.32
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/bin/xframe.js +67 -1
- package/defaults.json +2 -2
- package/package.json +1 -1
- package/template/web/client/src/FrontCoreShowcase.tsx +88 -0
package/bin/xframe.js
CHANGED
|
@@ -316,6 +316,70 @@ async function collectDatabaseConfig(fromCli, skipDbPrompt, wantDbPullFlag) {
|
|
|
316
316
|
}
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
+
/** drizzle 루트 db:pull 과 Nest 런타임이 backend-core 로드 시 필요 (피어 미설치 시 모듈 누락) */
|
|
320
|
+
const NEST_MICROSERVICES_SPEC = '^10.0.0';
|
|
321
|
+
const REFLECT_METADATA_SPEC = '^0.2.0';
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* 옛 템플릿·캐시된 npx 패키지에도 대응: 루트 devDeps + apps/api deps 에 보강.
|
|
325
|
+
* 이미 있으면 변경 없음.
|
|
326
|
+
*/
|
|
327
|
+
function ensureNestPeerDepsInPackageJsons(targetRoot) {
|
|
328
|
+
let touched = false;
|
|
329
|
+
|
|
330
|
+
const rootPkgPath = path.join(targetRoot, 'package.json');
|
|
331
|
+
if (fs.existsSync(rootPkgPath)) {
|
|
332
|
+
try {
|
|
333
|
+
const pkg = JSON.parse(fs.readFileSync(rootPkgPath, 'utf8'));
|
|
334
|
+
pkg.devDependencies = pkg.devDependencies || {};
|
|
335
|
+
let rootChanged = false;
|
|
336
|
+
if (!pkg.devDependencies['@nestjs/microservices']) {
|
|
337
|
+
pkg.devDependencies['@nestjs/microservices'] = NEST_MICROSERVICES_SPEC;
|
|
338
|
+
rootChanged = true;
|
|
339
|
+
}
|
|
340
|
+
if (!pkg.devDependencies['reflect-metadata']) {
|
|
341
|
+
pkg.devDependencies['reflect-metadata'] = REFLECT_METADATA_SPEC;
|
|
342
|
+
rootChanged = true;
|
|
343
|
+
}
|
|
344
|
+
if (rootChanged) {
|
|
345
|
+
fs.writeFileSync(rootPkgPath, `${JSON.stringify(pkg, null, 2)}\n`, 'utf8');
|
|
346
|
+
touched = true;
|
|
347
|
+
}
|
|
348
|
+
} catch {
|
|
349
|
+
/* ignore */
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const apiPkgPath = path.join(targetRoot, 'apps', 'api', 'package.json');
|
|
354
|
+
if (fs.existsSync(apiPkgPath)) {
|
|
355
|
+
try {
|
|
356
|
+
const pkg = JSON.parse(fs.readFileSync(apiPkgPath, 'utf8'));
|
|
357
|
+
pkg.dependencies = pkg.dependencies || {};
|
|
358
|
+
let apiChanged = false;
|
|
359
|
+
if (!pkg.dependencies['@nestjs/microservices']) {
|
|
360
|
+
pkg.dependencies['@nestjs/microservices'] = NEST_MICROSERVICES_SPEC;
|
|
361
|
+
apiChanged = true;
|
|
362
|
+
}
|
|
363
|
+
if (!pkg.dependencies['reflect-metadata']) {
|
|
364
|
+
pkg.dependencies['reflect-metadata'] = REFLECT_METADATA_SPEC;
|
|
365
|
+
apiChanged = true;
|
|
366
|
+
}
|
|
367
|
+
if (apiChanged) {
|
|
368
|
+
fs.writeFileSync(apiPkgPath, `${JSON.stringify(pkg, null, 2)}\n`, 'utf8');
|
|
369
|
+
touched = true;
|
|
370
|
+
}
|
|
371
|
+
} catch {
|
|
372
|
+
/* ignore */
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (touched) {
|
|
377
|
+
console.log(
|
|
378
|
+
'xframe: package.json 에 @nestjs/microservices · reflect-metadata 를 보강했습니다 (db:pull / API).',
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
319
383
|
/** yarn 우선, 없거나 실패 시 npm (부모 셸 PATH 그대로 — shell:true 쓰면 sh라 yarn/nvm PATH가 빠지는 경우가 많음) */
|
|
320
384
|
function installDependencies(cwd) {
|
|
321
385
|
const publicNpm = 'https://registry.npmjs.org/';
|
|
@@ -544,6 +608,7 @@ async function main() {
|
|
|
544
608
|
/** 템플릿은 모노레포 기준 front-core 소스를 가리키고, 생성 앱은 설치된 패키지로 바꿉니다. */
|
|
545
609
|
patchWebTsconfigFrontCorePath(targetRoot);
|
|
546
610
|
patchWebViteFrontCoreAlias(targetRoot);
|
|
611
|
+
ensureNestPeerDepsInPackageJsons(targetRoot);
|
|
547
612
|
|
|
548
613
|
console.log(`Created ${packageName} at ${targetRoot}
|
|
549
614
|
|
|
@@ -577,7 +642,8 @@ Next:
|
|
|
577
642
|
cd ${rel}
|
|
578
643
|
yarn dev # 또는 npm run dev
|
|
579
644
|
→ API :3000/health · Client :3001 · Admin :3002
|
|
580
|
-
(설치 생략: npx @xfilecom/xframe <dir> --no-install)
|
|
645
|
+
(설치 생략: npx @xfilecom/xframe <dir> --no-install)
|
|
646
|
+
최신 스캐폴드: npx --yes @xfilecom/xframe@latest <dir>`);
|
|
581
647
|
}
|
|
582
648
|
|
|
583
649
|
main().catch((err) => {
|
package/defaults.json
CHANGED
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { useCallback, useId, useRef, useState, type ReactNode } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Badge,
|
|
4
|
+
BottomSheet,
|
|
4
5
|
Box,
|
|
5
6
|
Button,
|
|
6
7
|
Card,
|
|
8
|
+
ConfirmDialog,
|
|
9
|
+
Dialog,
|
|
7
10
|
Input,
|
|
8
11
|
InlineErrorList,
|
|
9
12
|
LoadingOverlay,
|
|
@@ -32,6 +35,11 @@ export function FrontCoreShowcase() {
|
|
|
32
35
|
const [toasts, setToasts] = useState<ToastEntry[]>([]);
|
|
33
36
|
const [errors, setErrors] = useState<InlineErrorEntry[]>([]);
|
|
34
37
|
const [loading, setLoading] = useState(false);
|
|
38
|
+
const [dialogOpen, setDialogOpen] = useState(false);
|
|
39
|
+
const [confirmOpen, setConfirmOpen] = useState(false);
|
|
40
|
+
const [confirmDestructiveOpen, setConfirmDestructiveOpen] = useState(false);
|
|
41
|
+
const [confirmLoadingDemo, setConfirmLoadingDemo] = useState(false);
|
|
42
|
+
const [sheetOpen, setSheetOpen] = useState(false);
|
|
35
43
|
|
|
36
44
|
const pushToast = useCallback(
|
|
37
45
|
(severity: ToastSeverity) => {
|
|
@@ -54,6 +62,59 @@ export function FrontCoreShowcase() {
|
|
|
54
62
|
<ToastList toasts={toasts} onDismiss={(id) => setToasts((t) => t.filter((x) => x.id !== id))} />
|
|
55
63
|
<LoadingOverlay active={loading} message="로딩 중…" />
|
|
56
64
|
|
|
65
|
+
<Dialog
|
|
66
|
+
open={dialogOpen}
|
|
67
|
+
onOpenChange={setDialogOpen}
|
|
68
|
+
title="Dialog"
|
|
69
|
+
description="title / description / children 조합. 배경 클릭·Escape 로 닫을 수 있습니다."
|
|
70
|
+
>
|
|
71
|
+
<Text variant="body">children 에 폼·리스트 등 자유롭게 넣을 수 있습니다.</Text>
|
|
72
|
+
<div className="xfc-dialog-footer">
|
|
73
|
+
<Button type="button" variant="muted" onClick={() => setDialogOpen(false)}>
|
|
74
|
+
취소
|
|
75
|
+
</Button>
|
|
76
|
+
<Button type="button" variant="primary" onClick={() => setDialogOpen(false)}>
|
|
77
|
+
확인
|
|
78
|
+
</Button>
|
|
79
|
+
</div>
|
|
80
|
+
</Dialog>
|
|
81
|
+
|
|
82
|
+
<ConfirmDialog
|
|
83
|
+
open={confirmOpen}
|
|
84
|
+
onOpenChange={setConfirmOpen}
|
|
85
|
+
title="ConfirmDialog"
|
|
86
|
+
message="일반 확인창입니다. 취소 시 onCancel 후 닫힙니다."
|
|
87
|
+
onConfirm={() => setConfirmOpen(false)}
|
|
88
|
+
/>
|
|
89
|
+
|
|
90
|
+
<ConfirmDialog
|
|
91
|
+
open={confirmDestructiveOpen}
|
|
92
|
+
onOpenChange={setConfirmDestructiveOpen}
|
|
93
|
+
title="위험 작업"
|
|
94
|
+
message="destructive + 빨간 확인 버튼 예시입니다."
|
|
95
|
+
destructive
|
|
96
|
+
confirmLabel="삭제"
|
|
97
|
+
onConfirm={() => setConfirmDestructiveOpen(false)}
|
|
98
|
+
/>
|
|
99
|
+
|
|
100
|
+
<ConfirmDialog
|
|
101
|
+
open={confirmLoadingDemo}
|
|
102
|
+
onOpenChange={setConfirmLoadingDemo}
|
|
103
|
+
title="로딩 중 확인"
|
|
104
|
+
message="confirmLoading 시 버튼이 잠깁니다."
|
|
105
|
+
confirmLoading
|
|
106
|
+
onConfirm={() => {}}
|
|
107
|
+
/>
|
|
108
|
+
|
|
109
|
+
<BottomSheet open={sheetOpen} onOpenChange={setSheetOpen} title="BottomSheet" showHandle>
|
|
110
|
+
<Text variant="body">화면 하단에서 올라오는 패널입니다.</Text>
|
|
111
|
+
<Stack direction="row" gap="sm" align="center" style={{ marginTop: 'var(--xfc-space-md)', flexWrap: 'wrap' }}>
|
|
112
|
+
<Button type="button" variant="primary" onClick={() => setSheetOpen(false)}>
|
|
113
|
+
닫기
|
|
114
|
+
</Button>
|
|
115
|
+
</Stack>
|
|
116
|
+
</BottomSheet>
|
|
117
|
+
|
|
57
118
|
<Stack direction="column" gap="lg" align="stretch">
|
|
58
119
|
<Card>
|
|
59
120
|
<Stack direction="column" gap="md" align="stretch">
|
|
@@ -141,6 +202,33 @@ export function FrontCoreShowcase() {
|
|
|
141
202
|
</Stack>
|
|
142
203
|
</Card>
|
|
143
204
|
|
|
205
|
+
<Card>
|
|
206
|
+
<Stack direction="column" gap="md" align="stretch">
|
|
207
|
+
<SectionTitle>Dialog · ConfirmDialog · BottomSheet</SectionTitle>
|
|
208
|
+
<Text variant="small" style={{ color: 'var(--xfc-fg-muted)' }}>
|
|
209
|
+
document.body 포털 · z-index 10050 (토스트·로딩보다 위). base.css 의 .xfc-dialog-* /
|
|
210
|
+
.xfc-bottom-sheet-* 로 테마 조정.
|
|
211
|
+
</Text>
|
|
212
|
+
<Stack direction="row" gap="sm" align="center" style={{ flexWrap: 'wrap' }}>
|
|
213
|
+
<Button type="button" variant="secondary" onClick={() => setDialogOpen(true)}>
|
|
214
|
+
Dialog 열기
|
|
215
|
+
</Button>
|
|
216
|
+
<Button type="button" variant="secondary" onClick={() => setConfirmOpen(true)}>
|
|
217
|
+
Confirm
|
|
218
|
+
</Button>
|
|
219
|
+
<Button type="button" variant="secondary" onClick={() => setConfirmDestructiveOpen(true)}>
|
|
220
|
+
Confirm (destructive)
|
|
221
|
+
</Button>
|
|
222
|
+
<Button type="button" variant="secondary" onClick={() => setConfirmLoadingDemo(true)}>
|
|
223
|
+
Confirm (loading)
|
|
224
|
+
</Button>
|
|
225
|
+
<Button type="button" variant="secondary" onClick={() => setSheetOpen(true)}>
|
|
226
|
+
BottomSheet
|
|
227
|
+
</Button>
|
|
228
|
+
</Stack>
|
|
229
|
+
</Stack>
|
|
230
|
+
</Card>
|
|
231
|
+
|
|
144
232
|
<Card>
|
|
145
233
|
<Stack direction="column" gap="md" align="stretch">
|
|
146
234
|
<SectionTitle>ToastList · InlineErrorList · LoadingOverlay</SectionTitle>
|