@arronqzy/react-blueprint 1.0.23 → 1.0.25

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/index.ts CHANGED
@@ -56,6 +56,7 @@ export {
56
56
  getApiCollectionById,
57
57
  getApiCollectionListItems,
58
58
  upsertApiCollectionFromJson,
59
+ putApiCollection,
59
60
  removeApiCollection,
60
61
  deleteApiCollection,
61
62
  clearApiCollections,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arronqzy/react-blueprint",
3
- "version": "1.0.23",
3
+ "version": "1.0.25",
4
4
  "description": "",
5
5
  "files": [
6
6
  "index.ts",
@@ -18,10 +18,10 @@
18
18
  "dayjs": "^1.11.13",
19
19
  "react-dom": "^19",
20
20
  "@xyflow/react": "^12.9.3",
21
- "@arronqzy/blueprint-dsl": "1.0.17",
22
21
  "@arronqzy/i18n": "0.1.17",
23
- "@arronqzy/react-rx-store": "1.1.8",
24
- "@arronqzy/ui": "1.0.10"
22
+ "@arronqzy/blueprint-dsl": "1.0.17",
23
+ "@arronqzy/ui": "1.0.11",
24
+ "@arronqzy/react-rx-store": "1.1.8"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/react": "^19.0.1",
@@ -35,8 +35,8 @@
35
35
  "typescript": "^5.5.4",
36
36
  "vite": "^5.1.4",
37
37
  "@arronqzy/tailwind": "1.0.2",
38
- "@arronqzy/eslint-config": "0.0.0",
39
- "@arronqzy/typescript-config": "1.0.0"
38
+ "@arronqzy/typescript-config": "1.0.0",
39
+ "@arronqzy/eslint-config": "0.0.0"
40
40
  },
41
41
  "keywords": [],
42
42
  "author": "",
@@ -241,7 +241,10 @@ export function ApiCollectionManagerDialog({
241
241
  return (
242
242
  <>
243
243
  <Dialog open={open} onOpenChange={onOpenChange}>
244
- <DialogContent className="flex max-h-[90vh] w-[min(96vw,960px)] flex-col sm:max-w-[960px]">
244
+ <DialogContent
245
+ overlayClassName="bg-transparent"
246
+ className="flex max-h-[90vh] w-[min(96vw,960px)] flex-col sm:max-w-[960px]"
247
+ >
245
248
  <DialogHeader>
246
249
  <DialogTitle>{t("blueprint.apiCollection.managerTitle")}</DialogTitle>
247
250
  <DialogDescription>
@@ -376,8 +379,12 @@ export function ApiCollectionManagerDialog({
376
379
  </Dialog>
377
380
 
378
381
  <Dialog open={editorOpen} onOpenChange={setEditorOpen}>
379
- <DialogContent className="flex max-h-[90vh] w-[min(96vw,1100px)] flex-col sm:max-w-[1100px]">
380
- <DialogHeader>
382
+ <DialogContent
383
+ overlayClassName="bg-transparent"
384
+ className="flex w-[min(92vw,620px)] flex-col gap-3 overflow-hidden sm:max-w-[620px]"
385
+ resizable={false}
386
+ >
387
+ <DialogHeader className="shrink-0 space-y-1">
381
388
  <DialogTitle>
382
389
  {editingId
383
390
  ? t("blueprint.apiCollection.editTitle")
@@ -388,59 +395,71 @@ export function ApiCollectionManagerDialog({
388
395
  </DialogDescription>
389
396
  </DialogHeader>
390
397
 
391
- <div className="space-y-2">
392
- <div className="flex items-center justify-between gap-2">
393
- <Label className="text-xs">
394
- {t("blueprint.apiCollection.jsonEditor")}
395
- </Label>
396
- <label className="flex cursor-pointer items-center gap-2 text-[11px] text-muted-foreground">
397
- <input
398
- type="checkbox"
399
- className="h-3.5 w-3.5 rounded border border-input"
400
- checked={editorPersistLocal}
401
- onChange={(e) => setEditorPersistLocal(e.target.checked)}
402
- />
403
- {t("blueprint.apiCollection.persistLocal")}
404
- </label>
405
- </div>
406
- <Textarea
407
- value={editorJson}
408
- onChange={(e) => setEditorJson(e.target.value)}
409
- className={cn(
410
- "min-h-[min(62vh,640px)] flex-1 font-mono text-[12px] leading-relaxed",
411
- editorError && "border-destructive"
412
- )}
413
- spellCheck={false}
414
- />
415
- {editorError ? (
416
- <p className="text-[11px] text-destructive">{editorError}</p>
417
- ) : null}
418
- </div>
419
-
420
- <DialogFooter>
398
+ <div className="flex shrink-0 flex-wrap items-center gap-1.5 border-b border-border/60 pb-2">
421
399
  <Button
422
400
  type="button"
401
+ size="sm"
423
402
  variant="outline"
403
+ className="h-7 px-2 text-[11px]"
424
404
  onClick={() => downloadApiCollectionTemplate()}
425
405
  >
426
406
  {t("blueprint.apiCollection.downloadTemplate")}
427
407
  </Button>
428
408
  <Button
429
409
  type="button"
410
+ size="sm"
430
411
  variant="outline"
412
+ className="h-7 px-2 text-[11px]"
431
413
  disabled={busy || !onSyncCollection}
432
414
  title={t("blueprint.apiCollection.syncHint")}
433
415
  onClick={() => void handleSyncFromEditor()}
434
416
  >
435
417
  {t("blueprint.apiCollection.sync")}
436
418
  </Button>
437
- <Button type="button" variant="outline" onClick={() => setEditorOpen(false)}>
419
+ <Button
420
+ type="button"
421
+ size="sm"
422
+ variant="outline"
423
+ className="h-7 px-2 text-[11px]"
424
+ onClick={() => setEditorOpen(false)}
425
+ >
438
426
  {t("common.cancel")}
439
427
  </Button>
440
- <Button type="button" disabled={busy} onClick={() => void handleSaveEditor()}>
428
+ <Button
429
+ type="button"
430
+ size="sm"
431
+ className="h-7 px-2.5 text-[11px]"
432
+ disabled={busy}
433
+ onClick={() => void handleSaveEditor()}
434
+ >
441
435
  {t("common.save")}
442
436
  </Button>
443
- </DialogFooter>
437
+ <label className="ml-auto flex cursor-pointer items-center gap-1.5 text-[11px] text-muted-foreground">
438
+ <input
439
+ type="checkbox"
440
+ className="h-3.5 w-3.5 rounded border border-input"
441
+ checked={editorPersistLocal}
442
+ onChange={(e) => setEditorPersistLocal(e.target.checked)}
443
+ />
444
+ {t("blueprint.apiCollection.persistLocal")}
445
+ </label>
446
+ </div>
447
+
448
+ <div className="flex flex-col gap-2">
449
+ <Label className="text-xs">{t("blueprint.apiCollection.jsonEditor")}</Label>
450
+ <Textarea
451
+ value={editorJson}
452
+ onChange={(e) => setEditorJson(e.target.value)}
453
+ className={cn(
454
+ "h-[min(68vh,640px)] min-h-[320px] w-full resize-y overflow-auto font-mono text-[12px] leading-relaxed",
455
+ editorError && "border-destructive"
456
+ )}
457
+ spellCheck={false}
458
+ />
459
+ {editorError ? (
460
+ <p className="text-[11px] text-destructive">{editorError}</p>
461
+ ) : null}
462
+ </div>
444
463
  </DialogContent>
445
464
  </Dialog>
446
465
  </>
@@ -70,18 +70,48 @@ export type UpsertApiCollectionInput = {
70
70
  persistLocal: boolean;
71
71
  /** 未写 document.name 时用文件名 */
72
72
  fallbackName?: string;
73
+ /** 省略时写入当前已 hydrate 的 nameSpace */
74
+ nameSpace?: string | null;
73
75
  };
74
76
 
75
77
  export type UpsertApiCollectionResult =
76
78
  | { ok: true; record: ApiCollectionRecord }
77
79
  | { ok: false; error: string };
78
80
 
81
+ function isCurrentNameSpace(nameSpace?: string | null): boolean {
82
+ return normalizeAppNameSpace(nameSpace) === normalizeAppNameSpace(nameSpaceRef);
83
+ }
84
+
85
+ /** 写入接口集合;persistLocal 时落 IndexedDB,同一 nameSpace 下刷新内存列表 */
86
+ export async function putApiCollection(
87
+ record: ApiCollectionRecord,
88
+ nameSpace?: string | null
89
+ ): Promise<ApiCollectionRecord> {
90
+ if (record.persistLocal) {
91
+ await putApiCollectionRecord(record, nameSpace);
92
+ } else if (isCurrentNameSpace(nameSpace)) {
93
+ const existing = records.find((item) => item.id === record.id);
94
+ if (existing?.persistLocal) {
95
+ await deleteApiCollectionRecord(record.id, nameSpace);
96
+ }
97
+ }
98
+ if (isCurrentNameSpace(nameSpace)) {
99
+ const next = records.filter((item) => item.id !== record.id);
100
+ next.push(record);
101
+ records = sortRecords(next);
102
+ emit();
103
+ }
104
+ return record;
105
+ }
106
+
79
107
  export async function upsertApiCollectionFromJson(
80
108
  input: UpsertApiCollectionInput
81
109
  ): Promise<UpsertApiCollectionResult> {
82
110
  const parsed = parseApiCollectionJson(input.rawJson);
83
111
  if (!parsed.ok) return parsed;
84
112
 
113
+ const targetNameSpace =
114
+ input.nameSpace !== undefined ? input.nameSpace : nameSpaceRef;
85
115
  const document: ApiCollectionDocument = {
86
116
  ...parsed.document,
87
117
  name:
@@ -90,11 +120,18 @@ export async function upsertApiCollectionFromJson(
90
120
  parsed.document.name,
91
121
  };
92
122
  const now = Date.now();
93
- const existing = input.id
123
+ const existingInMemory = input.id
94
124
  ? records.find((item) => item.id === input.id) ?? null
95
125
  : null;
126
+ const existingPersisted =
127
+ input.id && !isCurrentNameSpace(targetNameSpace)
128
+ ? await listApiCollectionRecords(targetNameSpace).then(
129
+ (list) => list.find((item) => item.id === input.id) ?? null
130
+ )
131
+ : null;
132
+ const existing = existingInMemory ?? existingPersisted;
96
133
  const record: ApiCollectionRecord = {
97
- id: existing?.id ?? createApiCollectionId(),
134
+ id: existing?.id ?? input.id ?? createApiCollectionId(),
98
135
  name: document.name,
99
136
  persistLocal: input.persistLocal,
100
137
  document,
@@ -103,16 +140,7 @@ export async function upsertApiCollectionFromJson(
103
140
  updatedAt: now,
104
141
  };
105
142
 
106
- if (record.persistLocal) {
107
- await putApiCollectionRecord(record, nameSpaceRef);
108
- } else if (existing?.persistLocal) {
109
- await deleteApiCollectionRecord(record.id, nameSpaceRef);
110
- }
111
-
112
- const next = records.filter((item) => item.id !== record.id);
113
- next.push(record);
114
- records = sortRecords(next);
115
- emit();
143
+ await putApiCollection(record, targetNameSpace);
116
144
  return { ok: true, record };
117
145
  }
118
146
 
@@ -129,10 +157,6 @@ export async function removeApiCollection(id: string): Promise<void> {
129
157
  emit();
130
158
  }
131
159
 
132
- function isCurrentNameSpace(nameSpace?: string | null): boolean {
133
- return normalizeAppNameSpace(nameSpace) === normalizeAppNameSpace(nameSpaceRef);
134
- }
135
-
136
160
  /** 按 nameSpace 删除接口集合;若该 nameSpace 正在被编辑器使用,内存列表同步移除 */
137
161
  export async function deleteApiCollection(
138
162
  id: string,