@arronqzy/react-view 1.0.26 → 1.0.28

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arronqzy/react-view",
3
- "version": "1.0.26",
3
+ "version": "1.0.28",
4
4
  "files": [
5
5
  "src",
6
6
  "dist",
@@ -31,13 +31,13 @@
31
31
  "@codemirror/theme-one-dark": "^6.1.2",
32
32
  "@codemirror/view": "^6.36.2",
33
33
  "@arronqzy/ui": "1.0.9",
34
- "@arronqzy/blueprint-dsl": "1.0.14",
35
34
  "@arronqzy/react-blueprint": "1.0.16",
35
+ "@arronqzy/blueprint-dsl": "1.0.14",
36
36
  "@arronqzy/i18n": "0.1.11",
37
37
  "@arronqzy/rx-store": "1.0.8",
38
38
  "@arronqzy/view-scene3d": "0.1.5",
39
- "@arronqzy/view-table": "0.1.8",
40
- "@arronqzy/webllm-assistant": "0.1.14"
39
+ "@arronqzy/webllm-assistant": "0.1.16",
40
+ "@arronqzy/view-table": "0.1.8"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/react": "^19.0.1",
@@ -46,8 +46,8 @@
46
46
  "eslint": "^8.57.0",
47
47
  "typescript": "^5.5.4",
48
48
  "@arronqzy/eslint-config": "0.0.0",
49
- "@arronqzy/typescript-config": "1.0.0",
50
- "@arronqzy/tailwind": "1.0.2"
49
+ "@arronqzy/tailwind": "1.0.2",
50
+ "@arronqzy/typescript-config": "1.0.0"
51
51
  },
52
52
  "scripts": {
53
53
  "lint": "eslint \"src/**/*.{ts,tsx}\"",
@@ -213,30 +213,30 @@ export function PanelConfigSidebar({
213
213
  if (!hit) return null;
214
214
  matchCountRef.current += 1;
215
215
  return (
216
- <Collapsible
217
- open={hasSearch ? true : isSectionExpanded(key, defaultOpen)}
218
- onOpenChange={(open) => setSectionExpanded(key, open)}
219
- className="rounded-xl border border-border/70 bg-card/95 shadow-sm"
220
- data-config-section={key}
221
- >
222
- <div className="flex items-center gap-1.5 px-3 py-2">
223
- <CollapsibleTrigger asChild>
224
- <button
225
- type="button"
226
- className="flex h-6 w-6 items-center justify-center rounded-md text-xs hover:bg-accent"
227
- >
228
- {isSectionExpanded(key, defaultOpen) ? "▾" : "▸"}
229
- </button>
230
- </CollapsibleTrigger>
231
- <div className="flex min-w-0 flex-1 items-center gap-1">
232
- <div className="text-[11px] font-semibold tracking-wide text-muted-foreground">{title}</div>
233
- {hint ? <ConfigHintIcon label={title}>{hint}</ConfigHintIcon> : null}
234
- </div>
216
+ <Collapsible
217
+ open={hasSearch ? true : isSectionExpanded(key, defaultOpen)}
218
+ onOpenChange={(open) => setSectionExpanded(key, open)}
219
+ className="rounded-xl border border-border/70 bg-card/95 shadow-sm"
220
+ data-config-section={key}
221
+ >
222
+ <div className="flex items-center gap-1.5 px-3 py-2">
223
+ <CollapsibleTrigger asChild>
224
+ <button
225
+ type="button"
226
+ className="flex h-6 w-6 items-center justify-center rounded-md text-xs hover:bg-accent"
227
+ >
228
+ {isSectionExpanded(key, defaultOpen) ? "▾" : "▸"}
229
+ </button>
230
+ </CollapsibleTrigger>
231
+ <div className="flex min-w-0 flex-1 items-center gap-1">
232
+ <div className="text-[11px] font-semibold tracking-wide text-muted-foreground">{title}</div>
233
+ {hint ? <ConfigHintIcon label={title}>{hint}</ConfigHintIcon> : null}
235
234
  </div>
236
- <CollapsibleContent className="space-y-3 border-t border-border/60 bg-muted/[0.1] px-3 pb-3 pt-2.5">
237
- {children}
238
- </CollapsibleContent>
239
- </Collapsible>
235
+ </div>
236
+ <CollapsibleContent className="space-y-3 border-t border-border/60 bg-muted/[0.1] px-3 pb-3 pt-2.5">
237
+ {children}
238
+ </CollapsibleContent>
239
+ </Collapsible>
240
240
  );
241
241
  };
242
242
 
@@ -360,51 +360,51 @@ export function PanelConfigSidebar({
360
360
  warnings={scopeWarnings}
361
361
  scrollContainerRef={sidebarScrollRef}
362
362
  >
363
- <aside
364
- ref={sidebarScrollRef}
365
- className={`scope-config-sidebar h-full overflow-auto border-l border-border bg-muted/[0.14] px-3 py-3 text-foreground [&_.config-field--highlight]:rounded-md [&_.config-field--highlight]:ring-2 [&_.config-field--highlight]:ring-sky-400/80 [&_.scope-field--highlight]:rounded-md [&_.scope-field--highlight]:ring-2 [&_.scope-field--highlight]:ring-amber-400/80 [&_button[role=checkbox]]:border-2 [&_button[role=checkbox]]:border-foreground/80 [&_button[role=checkbox]]:bg-background [&_button[role=checkbox]]:ring-1 [&_button[role=checkbox]]:ring-foreground/40 [&_button[role=checkbox][data-state=checked]]:border-primary [&_button[role=checkbox][data-state=checked]]:ring-primary/40 ${themedScrollbarClass}`}
366
- >
367
- <div className="sticky top-0 z-20 mb-3 rounded-lg border border-border/70 bg-card/95 px-2.5 py-2 shadow-sm backdrop-blur supports-[backdrop-filter]:bg-card/80">
368
- <div className="flex items-center justify-between gap-2">
369
- <div className="text-xs font-semibold tracking-wide">{t("panel.config.panelTitle")}</div>
370
- </div>
363
+ <aside
364
+ ref={sidebarScrollRef}
365
+ className={`scope-config-sidebar h-full overflow-auto border-l border-border bg-muted/[0.14] px-3 py-3 text-foreground [&_.config-field--highlight]:rounded-md [&_.config-field--highlight]:ring-2 [&_.config-field--highlight]:ring-sky-400/80 [&_.scope-field--highlight]:rounded-md [&_.scope-field--highlight]:ring-2 [&_.scope-field--highlight]:ring-amber-400/80 [&_button[role=checkbox]]:border-2 [&_button[role=checkbox]]:border-foreground/80 [&_button[role=checkbox]]:bg-background [&_button[role=checkbox]]:ring-1 [&_button[role=checkbox]]:ring-foreground/40 [&_button[role=checkbox][data-state=checked]]:border-primary [&_button[role=checkbox][data-state=checked]]:ring-primary/40 ${themedScrollbarClass}`}
366
+ >
367
+ <div className="sticky top-0 z-20 mb-3 rounded-lg border border-border/70 bg-card/95 px-2.5 py-2 shadow-sm backdrop-blur supports-[backdrop-filter]:bg-card/80">
368
+ <div className="flex items-center justify-between gap-2">
369
+ <div className="text-xs font-semibold tracking-wide">{t("panel.config.panelTitle")}</div>
370
+ </div>
371
371
  {showScopePanel ? <ViewElementScopePanel scope={viewElementScope} /> : null}
372
- <ScopeTemplateWarningsPanel />
373
- <div
374
- className={
375
- showScopePanel || scopeWarnings.length > 0
376
- ? "mt-2 border-t border-border/50 pt-2"
377
- : "mt-0"
378
- }
379
- >
380
- <div className="flex items-center justify-end gap-2">
381
- <button
382
- type="button"
383
- className="rounded border border-border px-1.5 py-0.5 text-[11px] text-muted-foreground hover:bg-accent"
384
- onClick={() => setIsSearchCollapsed((prev) => !prev)}
385
- >
386
- {isSearchCollapsed ? t("panel.config.expandSearch") : t("panel.config.collapseSearch")}
387
- </button>
388
- </div>
389
- {!isSearchCollapsed ? (
390
- <div className="mt-2">
391
- <Input
392
- value={configSearch}
393
- onChange={(e) => setConfigSearch(e.target.value)}
394
- placeholder={t("panel.config.searchPlaceholder")}
395
- className="h-7"
396
- data-scope-autocomplete="off"
397
- />
398
- {hasSearch ? (
399
- <div className="mt-1 text-[11px] text-muted-foreground">
400
- {t("panel.config.searching", { query: configSearch })}
401
- </div>
402
- ) : null}
403
- </div>
404
- ) : null}
372
+ <ScopeTemplateWarningsPanel />
373
+ <div
374
+ className={
375
+ showScopePanel || scopeWarnings.length > 0
376
+ ? "mt-2 border-t border-border/50 pt-2"
377
+ : "mt-0"
378
+ }
379
+ >
380
+ <div className="flex items-center justify-end gap-2">
381
+ <button
382
+ type="button"
383
+ className="rounded border border-border px-1.5 py-0.5 text-[11px] text-muted-foreground hover:bg-accent"
384
+ onClick={() => setIsSearchCollapsed((prev) => !prev)}
385
+ >
386
+ {isSearchCollapsed ? t("panel.config.expandSearch") : t("panel.config.collapseSearch")}
387
+ </button>
405
388
  </div>
389
+ {!isSearchCollapsed ? (
390
+ <div className="mt-2">
391
+ <Input
392
+ value={configSearch}
393
+ onChange={(e) => setConfigSearch(e.target.value)}
394
+ placeholder={t("panel.config.searchPlaceholder")}
395
+ className="h-7"
396
+ data-scope-autocomplete="off"
397
+ />
398
+ {hasSearch ? (
399
+ <div className="mt-1 text-[11px] text-muted-foreground">
400
+ {t("panel.config.searching", { query: configSearch })}
401
+ </div>
402
+ ) : null}
403
+ </div>
404
+ ) : null}
406
405
  </div>
407
- {isMultiSelectMode ? (
406
+ </div>
407
+ {isMultiSelectMode ? (
408
408
  <PanelConfigMultiSelect
409
409
  elements={effectiveSelectedElements}
410
410
  helpers={helpers}
@@ -414,34 +414,34 @@ export function PanelConfigSidebar({
414
414
  onExcludeSelectedNode={onExcludeSelectedNode}
415
415
  onAdjustNodeZOrder={onAdjustNodeZOrder}
416
416
  />
417
- ) : null}
418
- {!isMultiSelectMode && !selectedElement ? (
419
- <Empty className="py-7">
420
- <EmptyIcon>
421
- <svg
422
- viewBox="0 0 24 24"
423
- className="h-4 w-4"
424
- fill="none"
425
- stroke="currentColor"
426
- strokeWidth="1.8"
427
- aria-hidden="true"
428
- >
429
- <rect x="4" y="4" width="16" height="16" rx="2.5" />
430
- <path d="M8 9h8M8 12h8M8 15h5" />
431
- </svg>
432
- </EmptyIcon>
433
- <EmptyTitle>{t("panel.config.emptyNoNodeTitle")}</EmptyTitle>
434
- <EmptyDescription>{t("panel.config.emptyNoNodeDesc")}</EmptyDescription>
435
- </Empty>
436
- ) : !isMultiSelectMode && selectedElement ? (
437
- <div className="space-y-3">
438
- {!isNodeEditable ? (
439
- <div className="rounded border border-amber-500/40 bg-amber-500/10 px-2 py-1.5 text-[11px] text-amber-700 dark:text-amber-300">
440
- {readonlyReason}
441
- </div>
442
- ) : null}
443
- <fieldset disabled={!isNodeEditable} className={!isNodeEditable ? "opacity-60" : ""}>
444
- <div className="space-y-3.5 text-xs">
417
+ ) : null}
418
+ {!isMultiSelectMode && !selectedElement ? (
419
+ <Empty className="py-7">
420
+ <EmptyIcon>
421
+ <svg
422
+ viewBox="0 0 24 24"
423
+ className="h-4 w-4"
424
+ fill="none"
425
+ stroke="currentColor"
426
+ strokeWidth="1.8"
427
+ aria-hidden="true"
428
+ >
429
+ <rect x="4" y="4" width="16" height="16" rx="2.5" />
430
+ <path d="M8 9h8M8 12h8M8 15h5" />
431
+ </svg>
432
+ </EmptyIcon>
433
+ <EmptyTitle>{t("panel.config.emptyNoNodeTitle")}</EmptyTitle>
434
+ <EmptyDescription>{t("panel.config.emptyNoNodeDesc")}</EmptyDescription>
435
+ </Empty>
436
+ ) : !isMultiSelectMode && selectedElement ? (
437
+ <div className="space-y-3">
438
+ {!isNodeEditable ? (
439
+ <div className="rounded border border-amber-500/40 bg-amber-500/10 px-2 py-1.5 text-[11px] text-amber-700 dark:text-amber-300">
440
+ {readonlyReason}
441
+ </div>
442
+ ) : null}
443
+ <fieldset disabled={!isNodeEditable} className={!isNodeEditable ? "opacity-60" : ""}>
444
+ <div className="space-y-3.5 text-xs">
445
445
  <PanelConfigNodeInfo
446
446
  element={selectedElement}
447
447
  helpers={helpers}
@@ -449,26 +449,26 @@ export function PanelConfigSidebar({
449
449
  nodeZOrderLabel={nodeZOrderLabel}
450
450
  onAdjustNodeZOrder={onAdjustNodeZOrder}
451
451
  />
452
- </div>
453
- </fieldset>
454
- <div className="rounded-lg border border-border/60 bg-card/80 px-2.5 py-2 text-xs">
455
- <label className="flex items-center gap-2">
456
- <Checkbox
457
- checked={selectedElement.locked === true}
458
- disabled={!canToggleNodeLock}
459
- className="h-4 w-4 border-2 border-foreground/80 bg-background ring-1 ring-foreground/40 data-[state=checked]:border-primary data-[state=checked]:ring-primary/40"
460
- onCheckedChange={(checked) =>
461
- updateElement(selectedElement.id, {
462
- locked: checked === true,
463
- })
464
- }
465
- />
466
- <span>{t("panel.config.lockNode")}</span>
467
- </label>
468
452
  </div>
469
- <fieldset disabled={!isNodeEditable} className={!isNodeEditable ? "opacity-60" : ""}>
470
- <div className="space-y-3.5 text-xs">
471
- {isChartElement ? (
453
+ </fieldset>
454
+ <div className="rounded-lg border border-border/60 bg-card/80 px-2.5 py-2 text-xs">
455
+ <label className="flex items-center gap-2">
456
+ <Checkbox
457
+ checked={selectedElement.locked === true}
458
+ disabled={!canToggleNodeLock}
459
+ className="h-4 w-4 border-2 border-foreground/80 bg-background ring-1 ring-foreground/40 data-[state=checked]:border-primary data-[state=checked]:ring-primary/40"
460
+ onCheckedChange={(checked) =>
461
+ updateElement(selectedElement.id, {
462
+ locked: checked === true,
463
+ })
464
+ }
465
+ />
466
+ <span>{t("panel.config.lockNode")}</span>
467
+ </label>
468
+ </div>
469
+ <fieldset disabled={!isNodeEditable} className={!isNodeEditable ? "opacity-60" : ""}>
470
+ <div className="space-y-3.5 text-xs">
471
+ {isChartElement ? (
472
472
  <PanelConfigChartSection
473
473
  element={selectedElement}
474
474
  helpers={helpers}
@@ -476,23 +476,23 @@ export function PanelConfigSidebar({
476
476
  />
477
477
  ) : materialType === "table" ? (
478
478
  helpers.renderSection(
479
- "tableConfig",
480
- t("panel.config.sectionTable"),
481
- <PanelConfigTableSection
482
- element={selectedElement}
483
- disabled={!isNodeEditable}
484
- updateElement={updateElement}
485
- blueprintNodeOptions={blueprintNodeOptions}
486
- />,
487
- true,
488
- [
489
- t("panel.material.table"),
490
- "table",
491
- t("panel.config.tableSource"),
492
- t("panel.config.tableColumns"),
493
- t("panel.config.tableRowsText"),
494
- ]
495
- )
479
+ "tableConfig",
480
+ t("panel.config.sectionTable"),
481
+ <PanelConfigTableSection
482
+ element={selectedElement}
483
+ disabled={!isNodeEditable}
484
+ updateElement={updateElement}
485
+ blueprintNodeOptions={blueprintNodeOptions}
486
+ />,
487
+ true,
488
+ [
489
+ t("panel.material.table"),
490
+ "table",
491
+ t("panel.config.tableSource"),
492
+ t("panel.config.tableColumns"),
493
+ t("panel.config.tableRowsText"),
494
+ ]
495
+ )
496
496
  ) : materialType === "text" ? (
497
497
  <PanelConfigTextSection
498
498
  element={selectedElement}
@@ -511,7 +511,7 @@ export function PanelConfigSidebar({
511
511
  helpers={helpers}
512
512
  updateElement={updateElement}
513
513
  />
514
- ) : null}
514
+ ) : null}
515
515
  <PanelConfigGridChildSpan
516
516
  element={selectedElement}
517
517
  helpers={helpers}
@@ -525,22 +525,22 @@ export function PanelConfigSidebar({
525
525
  />
526
526
  ) : materialType === "scene3d" ? (
527
527
  helpers.renderSection(
528
- "scene3dConfig",
529
- t("panel.config.sectionScene3d"),
530
- <PanelConfigScene3dSection
531
- element={selectedElement}
532
- updateElement={updateElement}
533
- />,
534
- true,
535
- [
536
- t("panel.material.scene3d"),
537
- "3d",
538
- "glb",
539
- t("panel.config.scene3dUploadModels"),
540
- t("panel.config.groupScene3dCamera"),
541
- t("panel.config.groupScene3dModelAnim"),
542
- ]
543
- )
528
+ "scene3dConfig",
529
+ t("panel.config.sectionScene3d"),
530
+ <PanelConfigScene3dSection
531
+ element={selectedElement}
532
+ updateElement={updateElement}
533
+ />,
534
+ true,
535
+ [
536
+ t("panel.material.scene3d"),
537
+ "3d",
538
+ "glb",
539
+ t("panel.config.scene3dUploadModels"),
540
+ t("panel.config.groupScene3dCamera"),
541
+ t("panel.config.groupScene3dModelAnim"),
542
+ ]
543
+ )
544
544
  ) : materialType === "grid" ? (
545
545
  <PanelConfigGridSection
546
546
  element={selectedElement}
@@ -564,10 +564,10 @@ export function PanelConfigSidebar({
564
564
  setReferenceCopyMode={setReferenceCopyMode}
565
565
  />
566
566
  ) : materialType === "image" ? null : (
567
- <div className="text-xs leading-6 text-muted-foreground">
568
- {t("panel.config.notChartType")}
569
- </div>
570
- )}
567
+ <div className="text-xs leading-6 text-muted-foreground">
568
+ {t("panel.config.notChartType")}
569
+ </div>
570
+ )}
571
571
  <PanelConfigStyleSections
572
572
  element={selectedElement}
573
573
  helpers={helpers}
@@ -578,11 +578,11 @@ export function PanelConfigSidebar({
578
578
  matchCountRef={matchCountRef}
579
579
  label={t("panel.config.noMatch")}
580
580
  />
581
- </div>
582
- </fieldset>
583
- </div>
584
- ) : null}
585
- </aside>
581
+ </div>
582
+ </fieldset>
583
+ </div>
584
+ ) : null}
585
+ </aside>
586
586
  </ScopeConfigProvider>
587
587
  );
588
588
  }
@@ -2,6 +2,7 @@ import type { EChartsOption } from "echarts";
2
2
  import type { PanelChartConfig, PanelElement } from "../types";
3
3
 
4
4
  const DEFAULT_CHART_VALUES = [12, 18, 9, 24];
5
+ const DEFAULT_CHART_LABELS = ["A", "B", "C", "D"];
5
6
 
6
7
  export function getChartValuesDisplayText(chart?: PanelChartConfig): string {
7
8
  if (chart?.valuesText !== undefined) return chart.valuesText;
@@ -13,10 +14,26 @@ export function getChartLabelsDisplayText(chart?: PanelChartConfig): string {
13
14
  return (chart?.labels ?? []).join(",");
14
15
  }
15
16
 
17
+ export function parseChartLabelsFromText(
18
+ text: string | undefined,
19
+ fallback: string[] = DEFAULT_CHART_LABELS
20
+ ): string[] {
21
+ if (text === undefined) return fallback;
22
+ const trimmed = text.trim();
23
+ if (!trimmed) return fallback;
24
+ const parts = trimmed.split(",").map((s) => s.trim()).filter(Boolean);
25
+ if (!parts.length) return fallback;
26
+ return parts;
27
+ }
28
+
16
29
  export function resolveChartLabels(element: PanelElement): string[] {
17
30
  const chart = element.chart;
31
+ if (chart?.labelsText !== undefined) {
32
+ const parsed = parseChartLabelsFromText(chart.labelsText, []);
33
+ return parsed.length ? parsed : DEFAULT_CHART_LABELS;
34
+ }
18
35
  if (chart?.labels?.length) return chart.labels;
19
- return ["A", "B", "C", "D"];
36
+ return DEFAULT_CHART_LABELS;
20
37
  }
21
38
 
22
39
  export function parseChartValuesFromText(