@arronqzy/react-view 1.0.3 → 1.0.7
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/dist/styles.css +555 -15
- package/package.json +14 -7
- package/readme.md +74 -5
- package/src/core/template.tsx +12 -2
- package/src/panel/ReactViewOnlinePreview.tsx +18 -8
- package/src/panel/ReactViewPanel.tsx +403 -177
- package/src/panel/ai/AssistantChatPanel.tsx +895 -0
- package/src/panel/ai/AssistantMarkdown.tsx +256 -0
- package/src/panel/ai/applyAssistantAction.ts +652 -0
- package/src/panel/ai/revealConfigField.ts +137 -0
- package/src/panel/components/ConfigHintIcon.tsx +50 -0
- package/src/panel/components/ElementsLayer.tsx +39 -22
- package/src/panel/components/MaterialSidebar.tsx +157 -109
- package/src/panel/components/MoveableLayer.tsx +75 -61
- package/src/panel/components/PanelCanvas.tsx +81 -64
- package/src/panel/components/PanelConfigSidebar.tsx +471 -448
- package/src/panel/components/SelectLayer.tsx +1 -0
- package/src/panel/components/ViewElementScopePanel.tsx +13 -12
- package/src/panel/components/WorkspaceConfigSidebar.tsx +5 -2
- package/src/panel/components/WorkspaceProjectNav.tsx +25 -14
- package/src/panel/components/WorkspaceStageSplit.tsx +3 -1
- package/src/panel/components/scope-config/ScopeConfigContext.tsx +9 -0
- package/src/panel/components/scope-config/ScopeTemplatePreviewHost.tsx +242 -0
- package/src/panel/components/scope-config/ScopeTemplateUsageHint.tsx +12 -0
- package/src/panel/components/scope-config/ScopeTemplateWarningsPanel.tsx +6 -3
- package/src/panel/components/table/ConditionEditor.tsx +205 -0
- package/src/panel/components/table/JsonCodeEditor.tsx +115 -0
- package/src/panel/components/table/PanelConfigTableSection.tsx +2192 -0
- package/src/panel/components/table/TableNodeContent.tsx +544 -0
- package/src/panel/constants/messages.ts +76 -34
- package/src/panel/hooks/usePanelElements.ts +64 -52
- package/src/panel/hooks/useRafThrottledScroll.ts +30 -0
- package/src/panel/hooks/useWorkspaceProjects.ts +11 -8
- package/src/panel/types.ts +6 -0
- package/src/panel/utils/chartOptionBuilder.ts +12 -1
- package/src/panel/utils/panelElementDefaults.ts +69 -39
- package/src/panel/utils/scope-field-labels.ts +51 -37
- package/src/panel/utils/scope-template-chart.ts +92 -0
- package/src/panel/utils/scope-template-preview.ts +128 -0
- package/src/panel/utils/scope-template-spread.ts +252 -0
- package/src/panel/utils/scope-template-warnings.ts +131 -11
- package/src/panel/utils/scope-template.ts +105 -3
- package/src/tailwind.css +36 -15
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { useMemo, useState } from "react";
|
|
2
|
+
import { useI18n } from "@arronqzy/i18n/react";
|
|
2
3
|
import {
|
|
3
4
|
Card,
|
|
4
5
|
CardContent,
|
|
@@ -23,7 +24,7 @@ import {
|
|
|
23
24
|
} from "@arronqzy/ui";
|
|
24
25
|
import type { PanelElement, ReferenceCopyMode } from "../types";
|
|
25
26
|
import type { PanelLayer } from "../types";
|
|
26
|
-
import {
|
|
27
|
+
import { getPanelMessages } from "../constants/messages";
|
|
27
28
|
import {
|
|
28
29
|
concreteGridParentIdForLayer,
|
|
29
30
|
logicalGridParentIdFromConcrete,
|
|
@@ -50,25 +51,34 @@ function compareGridTreeChildOrder(a: PanelElement, b: PanelElement): number {
|
|
|
50
51
|
return a.id.localeCompare(b.id);
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
const
|
|
54
|
-
bar: "
|
|
55
|
-
line: "
|
|
56
|
-
pie: "
|
|
57
|
-
area: "
|
|
58
|
-
scatter: "
|
|
59
|
-
radar: "
|
|
60
|
-
gauge: "
|
|
61
|
-
funnel: "
|
|
62
|
-
text: "
|
|
63
|
-
rect: "
|
|
64
|
-
grid: "
|
|
65
|
-
image: "
|
|
66
|
-
video: "
|
|
67
|
-
audio: "
|
|
68
|
-
reference: "
|
|
69
|
-
geometry: "
|
|
54
|
+
const MATERIAL_LABEL_KEYS: Record<string, string> = {
|
|
55
|
+
bar: "panel.material.bar",
|
|
56
|
+
line: "panel.material.line",
|
|
57
|
+
pie: "panel.material.pie",
|
|
58
|
+
area: "panel.material.area",
|
|
59
|
+
scatter: "panel.material.scatter",
|
|
60
|
+
radar: "panel.material.radar",
|
|
61
|
+
gauge: "panel.material.gauge",
|
|
62
|
+
funnel: "panel.material.funnel",
|
|
63
|
+
text: "panel.material.text",
|
|
64
|
+
rect: "panel.material.rect",
|
|
65
|
+
grid: "panel.material.grid",
|
|
66
|
+
image: "panel.material.image",
|
|
67
|
+
video: "panel.material.video",
|
|
68
|
+
audio: "panel.material.audio",
|
|
69
|
+
reference: "panel.material.reference",
|
|
70
|
+
geometry: "panel.material.geometry",
|
|
71
|
+
table: "panel.material.table",
|
|
70
72
|
};
|
|
71
73
|
|
|
74
|
+
function getMaterialLabelMap(t: (key: string) => string): Record<string, string> {
|
|
75
|
+
const map: Record<string, string> = {};
|
|
76
|
+
for (const [id, key] of Object.entries(MATERIAL_LABEL_KEYS)) {
|
|
77
|
+
map[id] = t(key);
|
|
78
|
+
}
|
|
79
|
+
return map;
|
|
80
|
+
}
|
|
81
|
+
|
|
72
82
|
function LockGlyph({ className = "h-3.5 w-3.5" }: { className?: string }) {
|
|
73
83
|
return (
|
|
74
84
|
<svg
|
|
@@ -129,6 +139,7 @@ function IconDeepCopy({ className = "h-3.5 w-3.5" }: { className?: string }) {
|
|
|
129
139
|
}
|
|
130
140
|
|
|
131
141
|
function MaterialPreview({ id }: { id: string }) {
|
|
142
|
+
const { t } = useI18n();
|
|
132
143
|
const common =
|
|
133
144
|
"relative aspect-[4/3] w-full shrink-0 overflow-hidden rounded-md border border-border bg-gradient-to-br from-card to-muted/40";
|
|
134
145
|
|
|
@@ -243,6 +254,28 @@ function MaterialPreview({ id }: { id: string }) {
|
|
|
243
254
|
);
|
|
244
255
|
}
|
|
245
256
|
|
|
257
|
+
if (id === "table") {
|
|
258
|
+
return (
|
|
259
|
+
<div className={common}>
|
|
260
|
+
<div className="absolute inset-0 flex flex-col gap-1 p-2">
|
|
261
|
+
<div className="grid h-3 flex-none grid-cols-3 gap-1">
|
|
262
|
+
<div className="rounded-sm bg-primary/75" />
|
|
263
|
+
<div className="rounded-sm bg-primary/65" />
|
|
264
|
+
<div className="rounded-sm bg-primary/70" />
|
|
265
|
+
</div>
|
|
266
|
+
<div className="grid min-h-0 flex-1 grid-cols-3 gap-1">
|
|
267
|
+
{Array.from({ length: 6 }).map((_, idx) => (
|
|
268
|
+
<div
|
|
269
|
+
key={idx}
|
|
270
|
+
className="rounded-sm border border-primary/25 bg-primary/10"
|
|
271
|
+
/>
|
|
272
|
+
))}
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
246
279
|
if (id === "rect") {
|
|
247
280
|
return (
|
|
248
281
|
<div className={common}>
|
|
@@ -270,7 +303,7 @@ function MaterialPreview({ id }: { id: string }) {
|
|
|
270
303
|
<div className={common}>
|
|
271
304
|
<div className="absolute inset-2 rounded-md border border-dashed border-primary/70 bg-primary/10" />
|
|
272
305
|
<div className="absolute inset-0 flex items-center justify-center text-[10px] text-primary/80">
|
|
273
|
-
|
|
306
|
+
{t("panel.material.reference")}
|
|
274
307
|
</div>
|
|
275
308
|
</div>
|
|
276
309
|
);
|
|
@@ -331,41 +364,44 @@ function MaterialPreview({ id }: { id: string }) {
|
|
|
331
364
|
return <div className={common} />;
|
|
332
365
|
}
|
|
333
366
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
]
|
|
367
|
+
function getDefaultCategories(t: (key: string) => string): MaterialCategory[] {
|
|
368
|
+
return [
|
|
369
|
+
{
|
|
370
|
+
id: "charts",
|
|
371
|
+
title: t("panel.material.charts"),
|
|
372
|
+
items: [
|
|
373
|
+
{ id: "bar", title: t("panel.material.bar") },
|
|
374
|
+
{ id: "line", title: t("panel.material.line") },
|
|
375
|
+
{ id: "pie", title: t("panel.material.pie") },
|
|
376
|
+
{ id: "area", title: t("panel.material.area") },
|
|
377
|
+
{ id: "scatter", title: t("panel.material.scatter") },
|
|
378
|
+
{ id: "radar", title: t("panel.material.radar") },
|
|
379
|
+
{ id: "gauge", title: t("panel.material.gauge") },
|
|
380
|
+
{ id: "funnel", title: t("panel.material.funnel") },
|
|
381
|
+
],
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
id: "basic",
|
|
385
|
+
title: t("panel.material.basic"),
|
|
386
|
+
items: [
|
|
387
|
+
{ id: "text", title: t("panel.material.text") },
|
|
388
|
+
{ id: "table", title: t("panel.material.table") },
|
|
389
|
+
{ id: "geometry", title: t("panel.material.geometry") },
|
|
390
|
+
{ id: "grid", title: t("panel.material.grid") },
|
|
391
|
+
{ id: "image", title: t("panel.material.image") },
|
|
392
|
+
{ id: "reference", title: t("panel.material.reference") },
|
|
393
|
+
],
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
id: "media",
|
|
397
|
+
title: t("panel.material.media"),
|
|
398
|
+
items: [
|
|
399
|
+
{ id: "video", title: t("panel.material.video") },
|
|
400
|
+
{ id: "audio", title: t("panel.material.audio") },
|
|
401
|
+
],
|
|
402
|
+
},
|
|
403
|
+
];
|
|
404
|
+
}
|
|
369
405
|
|
|
370
406
|
export type MaterialSidebarProps = {
|
|
371
407
|
className?: string;
|
|
@@ -392,7 +428,11 @@ export function MaterialSidebar({
|
|
|
392
428
|
onCopyNode,
|
|
393
429
|
onMoveNodeToLayer,
|
|
394
430
|
}: MaterialSidebarProps) {
|
|
395
|
-
const
|
|
431
|
+
const { t } = useI18n();
|
|
432
|
+
const messages = useMemo(() => getPanelMessages(t), [t]);
|
|
433
|
+
const materialLabels = useMemo(() => getMaterialLabelMap(t), [t]);
|
|
434
|
+
|
|
435
|
+
const categories = useMemo(() => getDefaultCategories(t), [t]);
|
|
396
436
|
const [leftTab, setLeftTab] = useState<"materials" | "tree">("materials");
|
|
397
437
|
const [activeCategoryId, setActiveCategoryId] =
|
|
398
438
|
useState<MaterialCategoryId>("charts");
|
|
@@ -502,7 +542,7 @@ export function MaterialSidebar({
|
|
|
502
542
|
|
|
503
543
|
const getNodeDisplayName = (node: PanelElement) => {
|
|
504
544
|
const customName = node.name?.trim();
|
|
505
|
-
return customName || node.chart?.title ||
|
|
545
|
+
return customName || node.chart?.title || materialLabels[node.materialType ?? ""] || node.id;
|
|
506
546
|
};
|
|
507
547
|
|
|
508
548
|
const isExpanded = (key: string, defaultValue = false) =>
|
|
@@ -631,32 +671,36 @@ export function MaterialSidebar({
|
|
|
631
671
|
className="inline-flex shrink-0 items-center rounded-md border-2 border-violet-600 bg-violet-500/20 px-1.5 py-0.5 text-[10px] font-semibold text-violet-950 shadow-sm dark:border-violet-400 dark:bg-violet-500/35 dark:text-violet-50 dark:shadow-[0_0_14px_-3px_rgba(167,139,250,0.65)]"
|
|
632
672
|
title={
|
|
633
673
|
nodeHomeLayer.mappingBaseLayerId
|
|
634
|
-
?
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
674
|
+
? t("panel.material.mappingLayerWithBase", {
|
|
675
|
+
name: nodeHomeLayer.name,
|
|
676
|
+
base:
|
|
677
|
+
layerById.get(nodeHomeLayer.mappingBaseLayerId)?.name ??
|
|
678
|
+
nodeHomeLayer.mappingBaseLayerId,
|
|
679
|
+
})
|
|
680
|
+
: t("panel.material.mappingLayerTitle", { name: nodeHomeLayer.name })
|
|
639
681
|
}
|
|
640
682
|
>
|
|
641
|
-
|
|
683
|
+
{t("panel.material.mappingLayerNode")}
|
|
642
684
|
</span>
|
|
643
685
|
) : null}
|
|
644
686
|
{node.mappingSourceNodeId ? (
|
|
645
687
|
<span
|
|
646
688
|
className="inline-flex shrink-0 items-center rounded border border-primary/40 bg-primary/10 px-1 text-[10px] text-primary"
|
|
647
|
-
title={
|
|
648
|
-
node.
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
689
|
+
title={t("panel.material.sameSourceTitle", {
|
|
690
|
+
id: `${node.mappingSourceNodeId}${
|
|
691
|
+
node.mappingSourceLayerId
|
|
692
|
+
? ` · ${layerById.get(node.mappingSourceLayerId)?.name ?? node.mappingSourceLayerId}`
|
|
693
|
+
: ""
|
|
694
|
+
}`,
|
|
695
|
+
})}
|
|
652
696
|
>
|
|
653
|
-
|
|
697
|
+
{t("panel.material.sameSource")}
|
|
654
698
|
</span>
|
|
655
699
|
) : null}
|
|
656
700
|
{node.locked ? (
|
|
657
701
|
<span
|
|
658
702
|
className="inline-flex h-5 w-5 items-center justify-center rounded border border-border/80 bg-background/80 text-muted-foreground"
|
|
659
|
-
title="
|
|
703
|
+
title={t("panel.material.nodeLocked")}
|
|
660
704
|
>
|
|
661
705
|
<LockGlyph />
|
|
662
706
|
</span>
|
|
@@ -670,9 +714,9 @@ export function MaterialSidebar({
|
|
|
670
714
|
? "border-violet-500/50 bg-violet-500/15 text-violet-300"
|
|
671
715
|
: "border-sky-500/40 bg-sky-500/10 text-sky-300",
|
|
672
716
|
].join(" ")}
|
|
673
|
-
title={isDeepRef ? "
|
|
717
|
+
title={isDeepRef ? t("panel.material.deepCopyTitle") : t("panel.material.shallowCopyTitle")}
|
|
674
718
|
>
|
|
675
|
-
{isDeepRef ? "
|
|
719
|
+
{isDeepRef ? t("panel.material.deepCopy") : t("panel.material.shallowCopy")}
|
|
676
720
|
</span>
|
|
677
721
|
<Tooltip>
|
|
678
722
|
<TooltipTrigger asChild>
|
|
@@ -683,12 +727,12 @@ export function MaterialSidebar({
|
|
|
683
727
|
e.stopPropagation();
|
|
684
728
|
onCopyNode?.(node.id, "shallow");
|
|
685
729
|
}}
|
|
686
|
-
aria-label="
|
|
730
|
+
aria-label={t("panel.material.shallowCopy")}
|
|
687
731
|
>
|
|
688
732
|
<IconShallowCopy />
|
|
689
733
|
</button>
|
|
690
734
|
</TooltipTrigger>
|
|
691
|
-
<TooltipContent
|
|
735
|
+
<TooltipContent>{t("panel.material.shallowCopy")}</TooltipContent>
|
|
692
736
|
</Tooltip>
|
|
693
737
|
<Tooltip>
|
|
694
738
|
<TooltipTrigger asChild>
|
|
@@ -699,12 +743,12 @@ export function MaterialSidebar({
|
|
|
699
743
|
e.stopPropagation();
|
|
700
744
|
onCopyNode?.(node.id, "deep");
|
|
701
745
|
}}
|
|
702
|
-
aria-label="
|
|
746
|
+
aria-label={t("panel.material.deepCopy")}
|
|
703
747
|
>
|
|
704
748
|
<IconDeepCopy />
|
|
705
749
|
</button>
|
|
706
750
|
</TooltipTrigger>
|
|
707
|
-
<TooltipContent
|
|
751
|
+
<TooltipContent>{t("panel.material.deepCopy")}</TooltipContent>
|
|
708
752
|
</Tooltip>
|
|
709
753
|
</>
|
|
710
754
|
) : (
|
|
@@ -717,12 +761,12 @@ export function MaterialSidebar({
|
|
|
717
761
|
e.stopPropagation();
|
|
718
762
|
onCopyNode?.(node.id);
|
|
719
763
|
}}
|
|
720
|
-
aria-label="
|
|
764
|
+
aria-label={t("panel.material.duplicateNode")}
|
|
721
765
|
>
|
|
722
766
|
<IconCopy />
|
|
723
767
|
</button>
|
|
724
768
|
</TooltipTrigger>
|
|
725
|
-
<TooltipContent
|
|
769
|
+
<TooltipContent>{t("panel.material.duplicateNode")}</TooltipContent>
|
|
726
770
|
</Tooltip>
|
|
727
771
|
)}
|
|
728
772
|
<Tooltip>
|
|
@@ -736,12 +780,12 @@ export function MaterialSidebar({
|
|
|
736
780
|
if (node.locked) return;
|
|
737
781
|
onDeleteNode?.(node.id);
|
|
738
782
|
}}
|
|
739
|
-
aria-label={node.locked ? "
|
|
783
|
+
aria-label={node.locked ? t("panel.material.lockedNodeCannotDelete") : t("panel.material.deleteNode")}
|
|
740
784
|
>
|
|
741
785
|
<IconDelete />
|
|
742
786
|
</button>
|
|
743
787
|
</TooltipTrigger>
|
|
744
|
-
<TooltipContent>{node.locked ? "
|
|
788
|
+
<TooltipContent>{node.locked ? t("panel.material.lockedNodeCannotDelete") : t("panel.material.deleteNode")}</TooltipContent>
|
|
745
789
|
</Tooltip>
|
|
746
790
|
</div>
|
|
747
791
|
{hasChildren ? (
|
|
@@ -754,9 +798,10 @@ export function MaterialSidebar({
|
|
|
754
798
|
key={`cycle:${childPath}`}
|
|
755
799
|
className="py-1 text-[10px] text-muted-foreground/80"
|
|
756
800
|
style={{ paddingLeft: 6 + (level + 1) * 14 }}
|
|
757
|
-
title="
|
|
801
|
+
title={t("panel.material.circularRefStopped")}
|
|
758
802
|
>
|
|
759
|
-
{getNodeDisplayName(child)}
|
|
803
|
+
{getNodeDisplayName(child)}
|
|
804
|
+
{t("panel.material.circularRefSuffix")}
|
|
760
805
|
</div>
|
|
761
806
|
);
|
|
762
807
|
}
|
|
@@ -796,7 +841,7 @@ export function MaterialSidebar({
|
|
|
796
841
|
<Input
|
|
797
842
|
value={keyword}
|
|
798
843
|
onChange={(e) => setKeyword(e.target.value)}
|
|
799
|
-
placeholder="
|
|
844
|
+
placeholder={t("panel.material.searchPlaceholder")}
|
|
800
845
|
className="h-8 text-xs"
|
|
801
846
|
/>
|
|
802
847
|
</div>
|
|
@@ -808,8 +853,8 @@ export function MaterialSidebar({
|
|
|
808
853
|
>
|
|
809
854
|
<div className="border-b border-border px-2 pb-2 pt-2">
|
|
810
855
|
<TabsList className="grid w-full grid-cols-2">
|
|
811
|
-
<TabsTrigger value="materials"
|
|
812
|
-
<TabsTrigger value="tree"
|
|
856
|
+
<TabsTrigger value="materials">{t("panel.material.tabMaterials")}</TabsTrigger>
|
|
857
|
+
<TabsTrigger value="tree">{t("panel.material.tabTree")}</TabsTrigger>
|
|
813
858
|
</TabsList>
|
|
814
859
|
</div>
|
|
815
860
|
<TabsContent value="materials" className="mt-0 min-h-0 flex-1 overflow-hidden">
|
|
@@ -837,7 +882,9 @@ export function MaterialSidebar({
|
|
|
837
882
|
|
|
838
883
|
<div className={`overflow-auto ${themedScrollbarClass}`}>
|
|
839
884
|
<div className="px-2.5 py-2.5 text-xs font-semibold">
|
|
840
|
-
{isSearching
|
|
885
|
+
{isSearching
|
|
886
|
+
? t("panel.material.searchResults", { count: matchedItems.length })
|
|
887
|
+
: activeCategory.title}
|
|
841
888
|
</div>
|
|
842
889
|
<div className="grid gap-2 px-2.5 pb-3">
|
|
843
890
|
{(isSearching ? matchedItems : activeCategory.items).map((it) => (
|
|
@@ -883,9 +930,9 @@ export function MaterialSidebar({
|
|
|
883
930
|
<path d="m20 20-3.5-3.5" />
|
|
884
931
|
</svg>
|
|
885
932
|
</EmptyIcon>
|
|
886
|
-
<EmptyTitle className="text-xs"
|
|
933
|
+
<EmptyTitle className="text-xs">{t("panel.material.emptyMaterialsTitle")}</EmptyTitle>
|
|
887
934
|
<EmptyDescription className="text-[11px]">
|
|
888
|
-
|
|
935
|
+
{t("panel.material.emptyMaterialsDesc")}
|
|
889
936
|
</EmptyDescription>
|
|
890
937
|
</Empty>
|
|
891
938
|
) : null}
|
|
@@ -899,16 +946,16 @@ export function MaterialSidebar({
|
|
|
899
946
|
<Input
|
|
900
947
|
value={treeKeyword}
|
|
901
948
|
onChange={(e) => setTreeKeyword(e.target.value)}
|
|
902
|
-
placeholder="
|
|
949
|
+
placeholder={t("panel.material.treeSearchPlaceholder")}
|
|
903
950
|
className="h-8 text-xs"
|
|
904
951
|
/>
|
|
905
952
|
</div>
|
|
906
953
|
<div className="mb-2 flex items-center justify-between rounded border border-border bg-card px-2 py-1.5">
|
|
907
|
-
<span className="text-[11px] text-muted-foreground"
|
|
954
|
+
<span className="text-[11px] text-muted-foreground">{t("panel.material.referenceOnlyTree")}</span>
|
|
908
955
|
<Switch
|
|
909
956
|
checked={referenceOnlyTree}
|
|
910
957
|
onCheckedChange={setReferenceOnlyTree}
|
|
911
|
-
aria-label="
|
|
958
|
+
aria-label={t("panel.material.referenceOnlyTree")}
|
|
912
959
|
/>
|
|
913
960
|
</div>
|
|
914
961
|
<Collapsible
|
|
@@ -937,13 +984,13 @@ export function MaterialSidebar({
|
|
|
937
984
|
const dropBlockReason = !draggingNode
|
|
938
985
|
? ""
|
|
939
986
|
: draggingNode.locked
|
|
940
|
-
?
|
|
987
|
+
? messages.nodeMoveLocked
|
|
941
988
|
: draggingSourceLayer?.locked
|
|
942
|
-
?
|
|
989
|
+
? messages.nodeMoveSourceLayerLocked
|
|
943
990
|
: layer.locked
|
|
944
|
-
?
|
|
991
|
+
? messages.nodeMoveTargetLayerLocked
|
|
945
992
|
: draggingNode.layerId === layer.id
|
|
946
|
-
?
|
|
993
|
+
? messages.nodeMoveSameLayer
|
|
947
994
|
: "";
|
|
948
995
|
const canDropIntoLayer = Boolean(draggingNode) && !dropBlockReason;
|
|
949
996
|
const isCurrentDropLayer = dragOverLayerId === layer.id;
|
|
@@ -983,7 +1030,7 @@ export function MaterialSidebar({
|
|
|
983
1030
|
? "bg-primary/15 text-primary"
|
|
984
1031
|
: "bg-destructive/15 text-destructive",
|
|
985
1032
|
].join(" ")}
|
|
986
|
-
title={canDropIntoLayer ? "
|
|
1033
|
+
title={canDropIntoLayer ? t("panel.material.targetLayer") : dropBlockReason}
|
|
987
1034
|
>
|
|
988
1035
|
➜
|
|
989
1036
|
</span>
|
|
@@ -1007,14 +1054,15 @@ export function MaterialSidebar({
|
|
|
1007
1054
|
className="shrink-0 rounded-md border-2 border-violet-600 bg-violet-500/22 px-1.5 py-0.5 text-[10px] font-semibold text-violet-950 shadow-sm dark:border-violet-400 dark:bg-violet-500/35 dark:text-violet-50 dark:shadow-[0_0_12px_-2px_rgba(167,139,250,0.55)]"
|
|
1008
1055
|
title={
|
|
1009
1056
|
layer.mappingBaseLayerId
|
|
1010
|
-
?
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1057
|
+
? t("panel.material.mappingLayerWithBaseShort", {
|
|
1058
|
+
base:
|
|
1059
|
+
layerById.get(layer.mappingBaseLayerId)?.name ??
|
|
1060
|
+
layer.mappingBaseLayerId,
|
|
1061
|
+
})
|
|
1062
|
+
: t("panel.material.mappingLayer")
|
|
1015
1063
|
}
|
|
1016
1064
|
>
|
|
1017
|
-
|
|
1065
|
+
{t("panel.material.mappingLayer")}
|
|
1018
1066
|
</span>
|
|
1019
1067
|
) : null}
|
|
1020
1068
|
{isCurrentDropLayer ? (
|
|
@@ -1026,7 +1074,7 @@ export function MaterialSidebar({
|
|
|
1026
1074
|
: "bg-destructive/15 text-destructive",
|
|
1027
1075
|
].join(" ")}
|
|
1028
1076
|
>
|
|
1029
|
-
{canDropIntoLayer ? "
|
|
1077
|
+
{canDropIntoLayer ? t("panel.material.willMoveToLayer") : dropBlockReason}
|
|
1030
1078
|
</span>
|
|
1031
1079
|
) : null}
|
|
1032
1080
|
</CardHeader>
|
|
@@ -1075,18 +1123,18 @@ export function MaterialSidebar({
|
|
|
1075
1123
|
setDragOverLayerId(null);
|
|
1076
1124
|
}
|
|
1077
1125
|
}}
|
|
1078
|
-
title="
|
|
1126
|
+
title={t("panel.material.dragToThisLayer")}
|
|
1079
1127
|
>
|
|
1080
1128
|
{draggingTreeNodeId
|
|
1081
1129
|
? canDropIntoLayer
|
|
1082
|
-
? "
|
|
1083
|
-
: dropBlockReason || "
|
|
1084
|
-
: "
|
|
1130
|
+
? t("panel.material.releaseToMove")
|
|
1131
|
+
: dropBlockReason || t("panel.material.cannotMoveToLayer")
|
|
1132
|
+
: t("panel.material.dragToLayer")}
|
|
1085
1133
|
</div>
|
|
1086
1134
|
<CollapsibleContent>
|
|
1087
1135
|
{rootNodes.length === 0 ? (
|
|
1088
1136
|
<div className="rounded border border-border/40 bg-muted/15 py-2 pl-3 text-[11px] text-muted-foreground">
|
|
1089
|
-
|
|
1137
|
+
{t("panel.material.emptyLayer")}
|
|
1090
1138
|
</div>
|
|
1091
1139
|
) : (
|
|
1092
1140
|
rootNodes.map((node) =>
|
|
@@ -1123,9 +1171,9 @@ export function MaterialSidebar({
|
|
|
1123
1171
|
<path d="M8 10h8M8 14h5" />
|
|
1124
1172
|
</svg>
|
|
1125
1173
|
</EmptyIcon>
|
|
1126
|
-
<EmptyTitle className="text-xs"
|
|
1174
|
+
<EmptyTitle className="text-xs">{t("panel.material.emptyNodesTitle")}</EmptyTitle>
|
|
1127
1175
|
<EmptyDescription className="text-[11px]">
|
|
1128
|
-
|
|
1176
|
+
{t("panel.material.emptyNodesDesc")}
|
|
1129
1177
|
</EmptyDescription>
|
|
1130
1178
|
</Empty>
|
|
1131
1179
|
</div>
|