@arronqzy/react-view 1.0.12 → 1.0.13
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 +6 -6
- package/src/panel/ReactViewOnlinePreview.tsx +41 -18
- package/src/panel/ReactViewPanel.tsx +52 -2
- package/src/panel/components/PanelConfigSidebar.tsx +442 -352
- package/src/panel/components/WorkspaceConfigSidebar.tsx +8 -0
- package/src/panel/utils/chartOptionBuilder.ts +7 -1
- package/src/panel/utils/outputScale.ts +69 -0
- package/src/panel/utils/panelElementDefaults.ts +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arronqzy/react-view",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.13",
|
|
4
4
|
"files": [
|
|
5
5
|
"src",
|
|
6
6
|
"dist",
|
|
@@ -30,13 +30,13 @@
|
|
|
30
30
|
"@codemirror/lang-json": "^6.0.1",
|
|
31
31
|
"@codemirror/theme-one-dark": "^6.1.2",
|
|
32
32
|
"@codemirror/view": "^6.36.2",
|
|
33
|
-
"@arronqzy/blueprint-dsl": "1.0.5",
|
|
34
33
|
"@arronqzy/ui": "1.0.5",
|
|
35
|
-
"@arronqzy/
|
|
36
|
-
"@arronqzy/webllm-assistant": "0.1.6",
|
|
37
|
-
"@arronqzy/react-blueprint": "1.0.6",
|
|
34
|
+
"@arronqzy/react-blueprint": "1.0.7",
|
|
38
35
|
"@arronqzy/view-table": "0.1.3",
|
|
39
|
-
"@arronqzy/rx-store": "1.0.4"
|
|
36
|
+
"@arronqzy/rx-store": "1.0.4",
|
|
37
|
+
"@arronqzy/webllm-assistant": "0.1.7",
|
|
38
|
+
"@arronqzy/blueprint-dsl": "1.0.6",
|
|
39
|
+
"@arronqzy/i18n": "0.1.5"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@types/react": "^19.0.1",
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
parsePanelLayers,
|
|
36
36
|
resolvePreviewLayerElements,
|
|
37
37
|
} from "./utils/panelStateIO";
|
|
38
|
+
import { applyPreviewSceneFill, readOutputScale } from "./utils/outputScale";
|
|
38
39
|
|
|
39
40
|
const PREVIEW_BOOT_PHASES: PageLifecyclePhase[] = ["mounted"];
|
|
40
41
|
|
|
@@ -76,6 +77,8 @@ export function ReactViewOnlinePreview({
|
|
|
76
77
|
>([]);
|
|
77
78
|
const [layoutRevision, setLayoutRevision] = useState(0);
|
|
78
79
|
const [layoutReady, setLayoutReady] = useState(false);
|
|
80
|
+
const [outputScale, setOutputScale] = useState(() => readOutputScale());
|
|
81
|
+
const [fillScale, setFillScale] = useState({ scaleX: 1, scaleY: 1 });
|
|
79
82
|
|
|
80
83
|
const sceneRef = useRef<HTMLDivElement | null>(null);
|
|
81
84
|
|
|
@@ -179,25 +182,38 @@ export function ReactViewOnlinePreview({
|
|
|
179
182
|
|
|
180
183
|
const displayElements = useMemo(
|
|
181
184
|
() =>
|
|
182
|
-
scopedLayerElements.map((el) =>
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
185
|
+
scopedLayerElements.map((el) => {
|
|
186
|
+
const x = el.x - sceneBounds.minX;
|
|
187
|
+
const y = el.y - sceneBounds.minY;
|
|
188
|
+
if (outputScale) return { ...el, x, y };
|
|
189
|
+
return {
|
|
190
|
+
...el,
|
|
191
|
+
x: x * fillScale.scaleX,
|
|
192
|
+
y: y * fillScale.scaleY,
|
|
193
|
+
width: el.width * fillScale.scaleX,
|
|
194
|
+
height: el.height * fillScale.scaleY,
|
|
195
|
+
};
|
|
196
|
+
}),
|
|
197
|
+
[
|
|
198
|
+
fillScale.scaleX,
|
|
199
|
+
fillScale.scaleY,
|
|
200
|
+
outputScale,
|
|
201
|
+
sceneBounds.minX,
|
|
202
|
+
sceneBounds.minY,
|
|
203
|
+
scopedLayerElements,
|
|
204
|
+
]
|
|
188
205
|
);
|
|
189
206
|
|
|
190
207
|
const applySceneFit = useCallback(() => {
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
scene.style.transform = `scale(${scaleX}, ${scaleY})`;
|
|
208
|
+
const enabled = readOutputScale();
|
|
209
|
+
setOutputScale(enabled);
|
|
210
|
+
const fill = applyPreviewSceneFill(
|
|
211
|
+
sceneRef.current,
|
|
212
|
+
sceneBounds.width,
|
|
213
|
+
sceneBounds.height,
|
|
214
|
+
enabled
|
|
215
|
+
);
|
|
216
|
+
setFillScale({ scaleX: fill.scaleX, scaleY: fill.scaleY });
|
|
201
217
|
setLayoutRevision((v) => v + 1);
|
|
202
218
|
notifyPreviewLayoutChanged();
|
|
203
219
|
}, [sceneBounds.height, sceneBounds.width]);
|
|
@@ -304,9 +320,16 @@ export function ReactViewOnlinePreview({
|
|
|
304
320
|
id="preview-scene"
|
|
305
321
|
className="relative shrink-0 origin-top-left"
|
|
306
322
|
style={{
|
|
307
|
-
width:
|
|
308
|
-
|
|
323
|
+
width: outputScale
|
|
324
|
+
? sceneBounds.width
|
|
325
|
+
: sceneBounds.width * fillScale.scaleX,
|
|
326
|
+
height: outputScale
|
|
327
|
+
? sceneBounds.height
|
|
328
|
+
: sceneBounds.height * fillScale.scaleY,
|
|
309
329
|
transformOrigin: "left top",
|
|
330
|
+
transform: outputScale
|
|
331
|
+
? `scale(${fillScale.scaleX}, ${fillScale.scaleY})`
|
|
332
|
+
: "none",
|
|
310
333
|
}}
|
|
311
334
|
>
|
|
312
335
|
<ElementsLayer
|
|
@@ -14,6 +14,7 @@ import { ElementsLayer } from "./components/ElementsLayer";
|
|
|
14
14
|
import { SelectLayer } from "./components/SelectLayer";
|
|
15
15
|
import { MoveableLayer } from "./components/MoveableLayer";
|
|
16
16
|
import { buildChartOption, CHART_TYPES } from "./utils/chartOptionBuilder";
|
|
17
|
+
import { readOutputScale, writeOutputScale } from "./utils/outputScale";
|
|
17
18
|
import { MaterialSidebar } from "./components/MaterialSidebar";
|
|
18
19
|
import { AssistantChatPanel } from "./ai/AssistantChatPanel";
|
|
19
20
|
import {
|
|
@@ -94,6 +95,7 @@ import {
|
|
|
94
95
|
EmptyTitle,
|
|
95
96
|
Input,
|
|
96
97
|
Menubar,
|
|
98
|
+
MenubarCheckboxItem,
|
|
97
99
|
MenubarContent,
|
|
98
100
|
MenubarItem,
|
|
99
101
|
MenubarMenu,
|
|
@@ -460,6 +462,7 @@ export function ReactViewPanel({
|
|
|
460
462
|
const [titleIconZoom, setTitleIconZoom] = useState(1.6);
|
|
461
463
|
const [pendingPreviewLayerId, setPendingPreviewLayerId] = useState<string | null>(null);
|
|
462
464
|
const [panelFontSize, setPanelFontSize] = useState<"sm" | "md" | "lg">("md");
|
|
465
|
+
const [outputScale, setOutputScale] = useState(() => readOutputScale());
|
|
463
466
|
const [blueprintOpen, setBlueprintOpen] = useState(false);
|
|
464
467
|
const [assistantOpen, setAssistantOpen] = useState(false);
|
|
465
468
|
const [blueprintGraph, setBlueprintGraph] = useState(() => BlueprintGraph.empty());
|
|
@@ -570,6 +573,10 @@ export function ReactViewPanel({
|
|
|
570
573
|
}
|
|
571
574
|
}, [panelFontSize]);
|
|
572
575
|
|
|
576
|
+
useEffect(() => {
|
|
577
|
+
writeOutputScale(outputScale);
|
|
578
|
+
}, [outputScale]);
|
|
579
|
+
|
|
573
580
|
useEffect(() => {
|
|
574
581
|
try {
|
|
575
582
|
const stored = localStorage.getItem(TITLE_ICON_STORAGE_KEY) ?? "";
|
|
@@ -1784,16 +1791,49 @@ export function ReactViewPanel({
|
|
|
1784
1791
|
});
|
|
1785
1792
|
}
|
|
1786
1793
|
var scene = document.getElementById("preview-scene");
|
|
1794
|
+
var root = document.getElementById("preview-root");
|
|
1795
|
+
var outputScale = ${outputScale ? "true" : "false"};
|
|
1796
|
+
function rememberBox(node) {
|
|
1797
|
+
if (node.dataset.origLeft != null) return;
|
|
1798
|
+
node.dataset.origLeft = String(parseFloat(node.style.left) || 0);
|
|
1799
|
+
node.dataset.origTop = String(parseFloat(node.style.top) || 0);
|
|
1800
|
+
node.dataset.origWidth = String(parseFloat(node.style.width) || node.offsetWidth || 0);
|
|
1801
|
+
node.dataset.origHeight = String(parseFloat(node.style.height) || node.offsetHeight || 0);
|
|
1802
|
+
}
|
|
1787
1803
|
function fitScene() {
|
|
1788
1804
|
if (!scene) return;
|
|
1789
1805
|
var vw = window.innerWidth || 1;
|
|
1790
1806
|
var vh = window.innerHeight || 1;
|
|
1791
1807
|
var sw = ${sceneWidth} || 1;
|
|
1792
1808
|
var sh = ${sceneHeight} || 1;
|
|
1793
|
-
// 铺满预览窗口:宽高分别缩放,保证内容贴住四边
|
|
1794
1809
|
var scaleX = vw / sw;
|
|
1795
1810
|
var scaleY = vh / sh;
|
|
1796
|
-
|
|
1811
|
+
if (root) root.style.overflow = "hidden";
|
|
1812
|
+
var nodes = scene.querySelectorAll("[data-element-id]");
|
|
1813
|
+
if (outputScale) {
|
|
1814
|
+
scene.style.width = sw + "px";
|
|
1815
|
+
scene.style.height = sh + "px";
|
|
1816
|
+
scene.style.transform = "translate(0px,0px) scale(" + scaleX + "," + scaleY + ")";
|
|
1817
|
+
nodes.forEach(function (node) {
|
|
1818
|
+
rememberBox(node);
|
|
1819
|
+
node.style.left = node.dataset.origLeft + "px";
|
|
1820
|
+
node.style.top = node.dataset.origTop + "px";
|
|
1821
|
+
node.style.width = node.dataset.origWidth + "px";
|
|
1822
|
+
node.style.height = node.dataset.origHeight + "px";
|
|
1823
|
+
});
|
|
1824
|
+
return;
|
|
1825
|
+
}
|
|
1826
|
+
// 块仍按视口撑满,但不对整棵内容做 transform,字体保持原尺寸
|
|
1827
|
+
scene.style.width = vw + "px";
|
|
1828
|
+
scene.style.height = vh + "px";
|
|
1829
|
+
scene.style.transform = "none";
|
|
1830
|
+
nodes.forEach(function (node) {
|
|
1831
|
+
rememberBox(node);
|
|
1832
|
+
node.style.left = (Number(node.dataset.origLeft) * scaleX) + "px";
|
|
1833
|
+
node.style.top = (Number(node.dataset.origTop) * scaleY) + "px";
|
|
1834
|
+
node.style.width = (Number(node.dataset.origWidth) * scaleX) + "px";
|
|
1835
|
+
node.style.height = (Number(node.dataset.origHeight) * scaleY) + "px";
|
|
1836
|
+
});
|
|
1797
1837
|
}
|
|
1798
1838
|
fitScene();
|
|
1799
1839
|
window.addEventListener("resize", fitScene);
|
|
@@ -2013,6 +2053,14 @@ export function ReactViewPanel({
|
|
|
2013
2053
|
<MenubarRadioItem value="zh-CN">{t("panel.theme.zhCN")}</MenubarRadioItem>
|
|
2014
2054
|
<MenubarRadioItem value="en-US">{t("panel.theme.enUS")}</MenubarRadioItem>
|
|
2015
2055
|
</MenubarRadioGroup>
|
|
2056
|
+
<MenubarSeparator />
|
|
2057
|
+
<MenubarCheckboxItem
|
|
2058
|
+
checked={outputScale}
|
|
2059
|
+
onCheckedChange={(checked) => setOutputScale(checked === true)}
|
|
2060
|
+
title={t("panel.menubar.outputScaleHint")}
|
|
2061
|
+
>
|
|
2062
|
+
{t("panel.menubar.outputScale")}
|
|
2063
|
+
</MenubarCheckboxItem>
|
|
2016
2064
|
</MenubarContent>
|
|
2017
2065
|
</MenubarMenu>
|
|
2018
2066
|
</Menubar>
|
|
@@ -3176,6 +3224,8 @@ export function ReactViewPanel({
|
|
|
3176
3224
|
configFocus={sidebarConfigFocus}
|
|
3177
3225
|
executionLog={executionLogView}
|
|
3178
3226
|
selectedBlueprintNode={selectedBlueprintNode}
|
|
3227
|
+
blueprintGraphNodes={blueprintGraph.document.nodes}
|
|
3228
|
+
blueprintGraphEdges={blueprintGraph.document.edges}
|
|
3179
3229
|
allowFalseSignalPropagation={
|
|
3180
3230
|
blueprintGraph.document.allowFalseSignalPropagation ?? false
|
|
3181
3231
|
}
|
|
@@ -104,8 +104,8 @@ export function PanelConfigSidebar({
|
|
|
104
104
|
"scrollbar-thin [&::-webkit-scrollbar]:h-2 [&::-webkit-scrollbar]:w-2 [&::-webkit-scrollbar-track]:bg-muted/40 [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-border/80 [&::-webkit-scrollbar-thumb]:hover:bg-border";
|
|
105
105
|
const [expandedSections, setExpandedSections] = useState<Record<string, boolean>>({
|
|
106
106
|
nodeInfo: true,
|
|
107
|
-
styleBackground:
|
|
108
|
-
styleBorder:
|
|
107
|
+
styleBackground: false,
|
|
108
|
+
styleBorder: false,
|
|
109
109
|
chartBasic: true,
|
|
110
110
|
chartAdvanced: false,
|
|
111
111
|
chartAdvancedHighFreq: true,
|
|
@@ -421,9 +421,21 @@ export function PanelConfigSidebar({
|
|
|
421
421
|
|
|
422
422
|
useEffect(() => {
|
|
423
423
|
return subscribeRevealPanelConfig((detail) => {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
424
|
+
setExpandedSections((prev) => {
|
|
425
|
+
const next = { ...prev };
|
|
426
|
+
for (const section of detail.sections) next[section] = true;
|
|
427
|
+
next.nodeMore = true;
|
|
428
|
+
next.styleBgLayout = true;
|
|
429
|
+
next.chartDisplayMore = true;
|
|
430
|
+
next.chartTooltip = true;
|
|
431
|
+
next.chartAxes = true;
|
|
432
|
+
next.textInputAbility = true;
|
|
433
|
+
next.audioDisplayStyle = true;
|
|
434
|
+
next.geometryCanvasScript = true;
|
|
435
|
+
next.geometrySketch = true;
|
|
436
|
+
next.referenceCopyStrategy = true;
|
|
437
|
+
return next;
|
|
438
|
+
});
|
|
427
439
|
window.requestAnimationFrame(() => {
|
|
428
440
|
const field = detail.fields[0];
|
|
429
441
|
if (field) highlightConfigField(field);
|
|
@@ -475,16 +487,48 @@ export function PanelConfigSidebar({
|
|
|
475
487
|
const renderFieldGroup = (
|
|
476
488
|
title: string,
|
|
477
489
|
children: React.ReactNode,
|
|
478
|
-
hint?: React.ReactNode
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
490
|
+
hint?: React.ReactNode,
|
|
491
|
+
options?: { groupKey?: string; defaultOpen?: boolean }
|
|
492
|
+
) => {
|
|
493
|
+
const groupKey = options?.groupKey;
|
|
494
|
+
const defaultOpen = options?.defaultOpen ?? true;
|
|
495
|
+
const header = (
|
|
496
|
+
<>
|
|
482
497
|
<div className="text-[11px] font-semibold text-muted-foreground">{title}</div>
|
|
483
498
|
{hint ? <ConfigHintIcon label={title}>{hint}</ConfigHintIcon> : null}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
499
|
+
</>
|
|
500
|
+
);
|
|
501
|
+
if (!groupKey) {
|
|
502
|
+
return (
|
|
503
|
+
<div className="space-y-2.5 rounded-lg border border-border/55 bg-background/80 p-2.5">
|
|
504
|
+
<div className="flex items-center gap-1">{header}</div>
|
|
505
|
+
{children}
|
|
506
|
+
</div>
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
const open = hasSearch ? true : isSectionExpanded(groupKey, defaultOpen);
|
|
510
|
+
return (
|
|
511
|
+
<Collapsible
|
|
512
|
+
open={open}
|
|
513
|
+
onOpenChange={(next) => setSectionExpanded(groupKey, next)}
|
|
514
|
+
className="space-y-2.5 rounded-lg border border-border/55 bg-background/80 p-2.5"
|
|
515
|
+
>
|
|
516
|
+
<div className="flex items-center gap-1.5">
|
|
517
|
+
<CollapsibleTrigger asChild>
|
|
518
|
+
<button
|
|
519
|
+
type="button"
|
|
520
|
+
className="flex h-5 w-5 items-center justify-center rounded text-[11px] hover:bg-accent"
|
|
521
|
+
aria-label={open ? t("panel.config.collapseGroup", { title }) : t("panel.config.expandGroup", { title })}
|
|
522
|
+
>
|
|
523
|
+
{open ? "▾" : "▸"}
|
|
524
|
+
</button>
|
|
525
|
+
</CollapsibleTrigger>
|
|
526
|
+
{header}
|
|
527
|
+
</div>
|
|
528
|
+
<CollapsibleContent className="space-y-2.5">{children}</CollapsibleContent>
|
|
529
|
+
</Collapsible>
|
|
530
|
+
);
|
|
531
|
+
};
|
|
488
532
|
const isNodeCardExpanded = useCallback(
|
|
489
533
|
(id: string) => expandedNodeCards[id] ?? true,
|
|
490
534
|
[expandedNodeCards]
|
|
@@ -1369,15 +1413,19 @@ export function PanelConfigSidebar({
|
|
|
1369
1413
|
className="h-7"
|
|
1370
1414
|
type="number"
|
|
1371
1415
|
min={1}
|
|
1372
|
-
|
|
1373
|
-
|
|
1416
|
+
placeholder={t("panel.config.barWidthAuto")}
|
|
1417
|
+
value={el.chart?.barWidth ?? ""}
|
|
1418
|
+
onChange={(e) => {
|
|
1419
|
+
const raw = e.target.value.trim();
|
|
1374
1420
|
updateElement(el.id, {
|
|
1375
1421
|
chart: {
|
|
1376
1422
|
...(el.chart ?? {}),
|
|
1377
|
-
barWidth:
|
|
1423
|
+
barWidth: raw
|
|
1424
|
+
? Math.max(1, Number(raw) || 1)
|
|
1425
|
+
: undefined,
|
|
1378
1426
|
},
|
|
1379
|
-
})
|
|
1380
|
-
}
|
|
1427
|
+
});
|
|
1428
|
+
}}
|
|
1381
1429
|
/>
|
|
1382
1430
|
</label>
|
|
1383
1431
|
) : null}
|
|
@@ -1879,21 +1927,6 @@ export function PanelConfigSidebar({
|
|
|
1879
1927
|
{readonlyReason}
|
|
1880
1928
|
</div>
|
|
1881
1929
|
) : null}
|
|
1882
|
-
<div className="rounded-lg border border-border/60 bg-card/80 px-2.5 py-2 text-xs">
|
|
1883
|
-
<label className="flex items-center gap-2">
|
|
1884
|
-
<Checkbox
|
|
1885
|
-
checked={selectedElement.locked === true}
|
|
1886
|
-
disabled={!canToggleNodeLock}
|
|
1887
|
-
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"
|
|
1888
|
-
onCheckedChange={(checked) =>
|
|
1889
|
-
updateElement(selectedElement.id, {
|
|
1890
|
-
locked: checked === true,
|
|
1891
|
-
})
|
|
1892
|
-
}
|
|
1893
|
-
/>
|
|
1894
|
-
<span>{t("panel.config.lockNode")}</span>
|
|
1895
|
-
</label>
|
|
1896
|
-
</div>
|
|
1897
1930
|
<fieldset disabled={!isNodeEditable} className={!isNodeEditable ? "opacity-60" : ""}>
|
|
1898
1931
|
<div className="space-y-3.5 text-xs">
|
|
1899
1932
|
{renderSection(
|
|
@@ -1913,7 +1946,7 @@ export function PanelConfigSidebar({
|
|
|
1913
1946
|
className="h-7"
|
|
1914
1947
|
/>
|
|
1915
1948
|
</label>
|
|
1916
|
-
<div className="grid grid-cols-
|
|
1949
|
+
<div className="grid grid-cols-2 gap-2">
|
|
1917
1950
|
<label className="block space-y-1" data-config-field="x">
|
|
1918
1951
|
<div>X</div>
|
|
1919
1952
|
<Input
|
|
@@ -1941,20 +1974,7 @@ export function PanelConfigSidebar({
|
|
|
1941
1974
|
/>
|
|
1942
1975
|
</label>
|
|
1943
1976
|
</div>
|
|
1944
|
-
<div className="grid grid-cols-
|
|
1945
|
-
<label className="block space-y-1" data-config-field="rotate">
|
|
1946
|
-
<div>{t("panel.config.rotate")}</div>
|
|
1947
|
-
<Input
|
|
1948
|
-
type="number"
|
|
1949
|
-
value={selectedElement.rotate ?? 0}
|
|
1950
|
-
onChange={(e) =>
|
|
1951
|
-
updateElement(selectedElement.id, {
|
|
1952
|
-
rotate: Number(e.target.value) || 0,
|
|
1953
|
-
})
|
|
1954
|
-
}
|
|
1955
|
-
className="h-7"
|
|
1956
|
-
/>
|
|
1957
|
-
</label>
|
|
1977
|
+
<div className="grid grid-cols-2 gap-2">
|
|
1958
1978
|
<label className="block space-y-1" data-config-field="width">
|
|
1959
1979
|
<div>{t("panel.config.width")}</div>
|
|
1960
1980
|
<Input
|
|
@@ -1985,6 +2005,24 @@ export function PanelConfigSidebar({
|
|
|
1985
2005
|
/>
|
|
1986
2006
|
</label>
|
|
1987
2007
|
</div>
|
|
2008
|
+
{renderFieldGroup(
|
|
2009
|
+
t("panel.config.groupNodeMore"),
|
|
2010
|
+
<>
|
|
2011
|
+
<div className="grid grid-cols-2 gap-2">
|
|
2012
|
+
<label className="block space-y-1" data-config-field="rotate">
|
|
2013
|
+
<div>{t("panel.config.rotate")}</div>
|
|
2014
|
+
<Input
|
|
2015
|
+
type="number"
|
|
2016
|
+
value={selectedElement.rotate ?? 0}
|
|
2017
|
+
onChange={(e) =>
|
|
2018
|
+
updateElement(selectedElement.id, {
|
|
2019
|
+
rotate: Number(e.target.value) || 0,
|
|
2020
|
+
})
|
|
2021
|
+
}
|
|
2022
|
+
className="h-7"
|
|
2023
|
+
/>
|
|
2024
|
+
</label>
|
|
2025
|
+
</div>
|
|
1988
2026
|
<div className="space-y-1.5">
|
|
1989
2027
|
<div className="text-[11px] text-muted-foreground">{t("panel.config.nodeZOrder")}</div>
|
|
1990
2028
|
<div className="text-[11px] text-muted-foreground/90">
|
|
@@ -2023,205 +2061,177 @@ export function PanelConfigSidebar({
|
|
|
2023
2061
|
</div>
|
|
2024
2062
|
<div className="truncate text-muted-foreground">ID: {selectedElement.id}</div>
|
|
2025
2063
|
<div className="text-muted-foreground">{t("panel.config.type")}: {selectedElement.materialType ?? selectedElement.id}</div>
|
|
2064
|
+
</>,
|
|
2065
|
+
undefined,
|
|
2066
|
+
{ groupKey: "nodeMore", defaultOpen: false }
|
|
2067
|
+
)}
|
|
2026
2068
|
</>,
|
|
2027
2069
|
true,
|
|
2028
|
-
[t("panel.config.name"), "id", t("panel.config.type"), t("panel.layers.lockShort"), "locked", "name"]
|
|
2070
|
+
[t("panel.config.name"), "id", t("panel.config.type"), t("panel.config.rotate"), t("panel.config.nodeZOrder"), t("panel.layers.lockShort"), "locked", "name"]
|
|
2029
2071
|
)}
|
|
2072
|
+
</div>
|
|
2073
|
+
</fieldset>
|
|
2074
|
+
<div className="rounded-lg border border-border/60 bg-card/80 px-2.5 py-2 text-xs">
|
|
2075
|
+
<label className="flex items-center gap-2">
|
|
2076
|
+
<Checkbox
|
|
2077
|
+
checked={selectedElement.locked === true}
|
|
2078
|
+
disabled={!canToggleNodeLock}
|
|
2079
|
+
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"
|
|
2080
|
+
onCheckedChange={(checked) =>
|
|
2081
|
+
updateElement(selectedElement.id, {
|
|
2082
|
+
locked: checked === true,
|
|
2083
|
+
})
|
|
2084
|
+
}
|
|
2085
|
+
/>
|
|
2086
|
+
<span>{t("panel.config.lockNode")}</span>
|
|
2087
|
+
</label>
|
|
2088
|
+
</div>
|
|
2089
|
+
<fieldset disabled={!isNodeEditable} className={!isNodeEditable ? "opacity-60" : ""}>
|
|
2090
|
+
<div className="space-y-3.5 text-xs">
|
|
2030
2091
|
|
|
2031
|
-
{
|
|
2032
|
-
"styleBackground",
|
|
2033
|
-
t("panel.config.sectionStyleBackground"),
|
|
2092
|
+
{isChartElement ? (
|
|
2034
2093
|
<>
|
|
2035
|
-
{
|
|
2036
|
-
|
|
2094
|
+
{renderSection(
|
|
2095
|
+
"chartBasic",
|
|
2096
|
+
t("panel.config.sectionChartBasic"),
|
|
2037
2097
|
<>
|
|
2038
|
-
{
|
|
2039
|
-
t("panel.config.
|
|
2040
|
-
|
|
2041
|
-
|
|
2098
|
+
{renderFieldGroup(
|
|
2099
|
+
t("panel.config.groupBasicDisplay"),
|
|
2100
|
+
<>
|
|
2101
|
+
<label className="block space-y-1.5" data-config-field="chart.title">
|
|
2102
|
+
<div>{t("panel.config.title")}</div>
|
|
2103
|
+
<Input
|
|
2104
|
+
value={selectedElement.chart?.title ?? ""}
|
|
2105
|
+
onChange={(e) => updateSelectedChart({ title: e.target.value })}
|
|
2106
|
+
className="h-7"
|
|
2107
|
+
/>
|
|
2108
|
+
</label>
|
|
2109
|
+
|
|
2110
|
+
{renderColorField(
|
|
2111
|
+
t("panel.config.primaryColor"),
|
|
2112
|
+
selectedElement.chart?.color ?? "#3b82f6",
|
|
2113
|
+
(next) => updateSelectedChart({ color: next || "#3b82f6" })
|
|
2114
|
+
)}
|
|
2115
|
+
</>
|
|
2042
2116
|
)}
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
className="hidden"
|
|
2059
|
-
onChange={async (e) => {
|
|
2060
|
-
const file = e.target.files?.[0];
|
|
2061
|
-
e.currentTarget.value = "";
|
|
2062
|
-
if (!file) return;
|
|
2063
|
-
await handleUploadBackgroundImage(file);
|
|
2064
|
-
}}
|
|
2065
|
-
/>
|
|
2066
|
-
</label>
|
|
2067
|
-
{uploadStatus ? (
|
|
2068
|
-
<span className="text-[11px] text-muted-foreground">{uploadStatus}</span>
|
|
2069
|
-
) : null}
|
|
2070
|
-
</div>
|
|
2071
|
-
</>
|
|
2072
|
-
)}
|
|
2073
|
-
{renderFieldGroup(
|
|
2074
|
-
t("panel.config.groupBgLayout"),
|
|
2075
|
-
<div className="grid grid-cols-2 gap-2">
|
|
2076
|
-
<label className="block space-y-1">
|
|
2077
|
-
<div>{t("panel.config.backgroundSize")}</div>
|
|
2078
|
-
<Select
|
|
2079
|
-
value={selectedElement.style?.backgroundSize ?? "__none__"}
|
|
2080
|
-
onValueChange={(value) =>
|
|
2081
|
-
updateSelectedStyle({
|
|
2082
|
-
backgroundSize: value === "__none__" ? undefined : value,
|
|
2083
|
-
})
|
|
2084
|
-
}
|
|
2085
|
-
>
|
|
2086
|
-
<SelectTrigger className="h-7">
|
|
2087
|
-
<SelectValue placeholder={t("panel.config.selectBackgroundSize")} />
|
|
2088
|
-
</SelectTrigger>
|
|
2089
|
-
<SelectContent>
|
|
2090
|
-
<SelectItem value="__none__">{t("common.default")}</SelectItem>
|
|
2091
|
-
<SelectItem value="cover">cover</SelectItem>
|
|
2092
|
-
<SelectItem value="contain">contain</SelectItem>
|
|
2093
|
-
<SelectItem value="100% 100%">100% 100%</SelectItem>
|
|
2094
|
-
<SelectItem value="auto">auto</SelectItem>
|
|
2095
|
-
</SelectContent>
|
|
2096
|
-
</Select>
|
|
2097
|
-
</label>
|
|
2098
|
-
<label className="block space-y-1">
|
|
2099
|
-
<div>{t("panel.config.backgroundPosition")}</div>
|
|
2100
|
-
<Select
|
|
2101
|
-
value={selectedElement.style?.backgroundPosition ?? "__none__"}
|
|
2102
|
-
onValueChange={(value) =>
|
|
2103
|
-
updateSelectedStyle({
|
|
2104
|
-
backgroundPosition: value === "__none__" ? undefined : value,
|
|
2105
|
-
})
|
|
2106
|
-
}
|
|
2107
|
-
>
|
|
2108
|
-
<SelectTrigger className="h-7">
|
|
2109
|
-
<SelectValue placeholder={t("panel.config.selectBackgroundPosition")} />
|
|
2110
|
-
</SelectTrigger>
|
|
2111
|
-
<SelectContent>
|
|
2112
|
-
<SelectItem value="__none__">{t("common.default")}</SelectItem>
|
|
2113
|
-
<SelectItem value="center">center</SelectItem>
|
|
2114
|
-
<SelectItem value="top">top</SelectItem>
|
|
2115
|
-
<SelectItem value="bottom">bottom</SelectItem>
|
|
2116
|
-
<SelectItem value="left">left</SelectItem>
|
|
2117
|
-
<SelectItem value="right">right</SelectItem>
|
|
2118
|
-
<SelectItem value="top left">top left</SelectItem>
|
|
2119
|
-
<SelectItem value="top right">top right</SelectItem>
|
|
2120
|
-
<SelectItem value="bottom left">bottom left</SelectItem>
|
|
2121
|
-
<SelectItem value="bottom right">bottom right</SelectItem>
|
|
2122
|
-
</SelectContent>
|
|
2123
|
-
</Select>
|
|
2124
|
-
</label>
|
|
2125
|
-
</div>
|
|
2126
|
-
)}
|
|
2127
|
-
</>,
|
|
2128
|
-
true,
|
|
2129
|
-
[t("panel.config.backgroundColor"), t("panel.config.backgroundImage"), "background", "backgroundSize", "backgroundPosition", t("panel.config.searchKwLayout")]
|
|
2130
|
-
)}
|
|
2117
|
+
{renderFieldGroup(
|
|
2118
|
+
t("panel.config.groupData"),
|
|
2119
|
+
<>
|
|
2120
|
+
<label className="block space-y-1">
|
|
2121
|
+
<div>{t("panel.config.labelsCsv")}</div>
|
|
2122
|
+
<Input
|
|
2123
|
+
value={getChartLabelsDisplayText(selectedElement.chart)}
|
|
2124
|
+
onChange={(e) =>
|
|
2125
|
+
updateSelectedChart({
|
|
2126
|
+
labelsText: e.target.value,
|
|
2127
|
+
})
|
|
2128
|
+
}
|
|
2129
|
+
className="h-7"
|
|
2130
|
+
/>
|
|
2131
|
+
</label>
|
|
2131
2132
|
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
t("panel.config.sectionStyleBorder"),
|
|
2135
|
-
<>
|
|
2136
|
-
{renderFieldGroup(
|
|
2137
|
-
t("panel.config.groupBorderGeometry"),
|
|
2138
|
-
<div className="grid grid-cols-2 gap-2">
|
|
2139
|
-
<label className="block space-y-1">
|
|
2140
|
-
<div>{t("panel.config.borderWidth")}</div>
|
|
2141
|
-
<Input
|
|
2142
|
-
type="number"
|
|
2143
|
-
min={0}
|
|
2144
|
-
value={selectedElement.style?.borderWidth ?? 0}
|
|
2145
|
-
onChange={(e) =>
|
|
2146
|
-
updateSelectedStyle({ borderWidth: Math.max(0, Number(e.target.value) || 0) })
|
|
2147
|
-
}
|
|
2148
|
-
className="h-7"
|
|
2149
|
-
/>
|
|
2150
|
-
</label>
|
|
2151
|
-
<label className="block space-y-1">
|
|
2152
|
-
<div>{t("panel.config.borderRadius")}</div>
|
|
2153
|
-
<Input
|
|
2154
|
-
type="number"
|
|
2155
|
-
min={0}
|
|
2156
|
-
value={selectedElement.style?.borderRadius ?? 0}
|
|
2157
|
-
onChange={(e) =>
|
|
2158
|
-
updateSelectedStyle({ borderRadius: Math.max(0, Number(e.target.value) || 0) })
|
|
2159
|
-
}
|
|
2160
|
-
className="h-7"
|
|
2161
|
-
/>
|
|
2162
|
-
</label>
|
|
2163
|
-
</div>
|
|
2164
|
-
)}
|
|
2165
|
-
{renderFieldGroup(
|
|
2166
|
-
t("panel.config.groupBorderVisual"),
|
|
2167
|
-
<div className="grid grid-cols-2 gap-2">
|
|
2168
|
-
<label className="block space-y-1">
|
|
2169
|
-
<div>{t("panel.config.borderStyle")}</div>
|
|
2170
|
-
<Select
|
|
2171
|
-
value={selectedElement.style?.borderStyle ?? "solid"}
|
|
2172
|
-
onValueChange={(value) =>
|
|
2173
|
-
updateSelectedStyle({
|
|
2174
|
-
borderStyle: value as NonNullable<PanelElementStyle["borderStyle"]>,
|
|
2175
|
-
})
|
|
2176
|
-
}
|
|
2177
|
-
>
|
|
2178
|
-
<SelectTrigger className="h-7">
|
|
2179
|
-
<SelectValue placeholder={t("panel.config.selectBorderStyle")} />
|
|
2180
|
-
</SelectTrigger>
|
|
2181
|
-
<SelectContent>
|
|
2182
|
-
<SelectItem value="none">none</SelectItem>
|
|
2183
|
-
<SelectItem value="solid">solid</SelectItem>
|
|
2184
|
-
<SelectItem value="dashed">dashed</SelectItem>
|
|
2185
|
-
<SelectItem value="dotted">dotted</SelectItem>
|
|
2186
|
-
<SelectItem value="double">double</SelectItem>
|
|
2187
|
-
</SelectContent>
|
|
2188
|
-
</Select>
|
|
2189
|
-
</label>
|
|
2190
|
-
{renderColorField(
|
|
2191
|
-
t("panel.config.borderColor"),
|
|
2192
|
-
selectedElement.style?.borderColor ?? "",
|
|
2193
|
-
(next) => updateSelectedStyle({ borderColor: next || undefined })
|
|
2194
|
-
)}
|
|
2195
|
-
</div>
|
|
2196
|
-
)}
|
|
2197
|
-
</>,
|
|
2198
|
-
true,
|
|
2199
|
-
[t("panel.config.searchKwBorder"), "border", t("panel.config.searchKwWidth"), t("panel.config.searchKwRadius"), t("panel.config.color"), t("panel.config.searchKwStyle")]
|
|
2200
|
-
)}
|
|
2201
|
-
|
|
2202
|
-
{isChartElement ? (
|
|
2203
|
-
<>
|
|
2204
|
-
{renderSection(
|
|
2205
|
-
"chartBasic",
|
|
2206
|
-
t("panel.config.sectionChartBasic"),
|
|
2207
|
-
<>
|
|
2208
|
-
{renderFieldGroup(
|
|
2209
|
-
t("panel.config.groupBasicDisplay"),
|
|
2210
|
-
<>
|
|
2211
|
-
<label className="block space-y-1.5" data-config-field="chart.title">
|
|
2212
|
-
<div>{t("panel.config.title")}</div>
|
|
2133
|
+
<label className="block space-y-1">
|
|
2134
|
+
<div>{t("panel.config.valuesCsv")}</div>
|
|
2213
2135
|
<Input
|
|
2214
|
-
value={selectedElement.chart
|
|
2215
|
-
onChange={(e) =>
|
|
2136
|
+
value={getChartValuesDisplayText(selectedElement.chart)}
|
|
2137
|
+
onChange={(e) =>
|
|
2138
|
+
updateSelectedChart({
|
|
2139
|
+
valuesText: e.target.value,
|
|
2140
|
+
})
|
|
2141
|
+
}
|
|
2216
2142
|
className="h-7"
|
|
2217
2143
|
/>
|
|
2218
2144
|
</label>
|
|
2145
|
+
</>
|
|
2146
|
+
)}
|
|
2219
2147
|
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2148
|
+
{selectedChartType === "bar" ||
|
|
2149
|
+
selectedChartType === "line" ||
|
|
2150
|
+
selectedChartType === "area" ||
|
|
2151
|
+
selectedChartType === "pie" ? (
|
|
2152
|
+
renderFieldGroup(
|
|
2153
|
+
t("panel.config.groupSeries"),
|
|
2154
|
+
<>
|
|
2155
|
+
{selectedChartType === "bar" ? (
|
|
2156
|
+
<label className="block space-y-1">
|
|
2157
|
+
<div>{t("panel.config.barWidthPx")}</div>
|
|
2158
|
+
<Input
|
|
2159
|
+
type="number"
|
|
2160
|
+
min={1}
|
|
2161
|
+
placeholder={t("panel.config.barWidthAuto")}
|
|
2162
|
+
value={selectedElement.chart?.barWidth ?? ""}
|
|
2163
|
+
onChange={(e) => {
|
|
2164
|
+
const raw = e.target.value.trim();
|
|
2165
|
+
updateSelectedChart({
|
|
2166
|
+
barWidth: raw
|
|
2167
|
+
? Math.max(1, Number(raw) || 1)
|
|
2168
|
+
: undefined,
|
|
2169
|
+
});
|
|
2170
|
+
}}
|
|
2171
|
+
className="h-7"
|
|
2172
|
+
/>
|
|
2173
|
+
</label>
|
|
2174
|
+
) : null}
|
|
2175
|
+
|
|
2176
|
+
{selectedChartType === "line" || selectedChartType === "area" ? (
|
|
2177
|
+
<label className="flex items-center gap-2">
|
|
2178
|
+
<Checkbox
|
|
2179
|
+
checked={selectedElement.chart?.smooth ?? true}
|
|
2180
|
+
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"
|
|
2181
|
+
onCheckedChange={(checked) =>
|
|
2182
|
+
updateSelectedChart({ smooth: checked === true })
|
|
2183
|
+
}
|
|
2184
|
+
/>
|
|
2185
|
+
<span>{t("panel.config.smooth")}</span>
|
|
2186
|
+
</label>
|
|
2187
|
+
) : null}
|
|
2188
|
+
|
|
2189
|
+
{selectedChartType === "pie" ? (
|
|
2190
|
+
<div className="grid grid-cols-2 gap-2">
|
|
2191
|
+
<label className="block space-y-1">
|
|
2192
|
+
<div>{t("panel.config.pieInnerRadiusPct")}</div>
|
|
2193
|
+
<Input
|
|
2194
|
+
type="number"
|
|
2195
|
+
min={0}
|
|
2196
|
+
max={99}
|
|
2197
|
+
value={selectedElement.chart?.pieInnerRadius ?? 30}
|
|
2198
|
+
onChange={(e) =>
|
|
2199
|
+
updateSelectedChart({
|
|
2200
|
+
pieInnerRadius: Math.max(
|
|
2201
|
+
0,
|
|
2202
|
+
Math.min(99, Number(e.target.value) || 0)
|
|
2203
|
+
),
|
|
2204
|
+
})
|
|
2205
|
+
}
|
|
2206
|
+
className="h-7"
|
|
2207
|
+
/>
|
|
2208
|
+
</label>
|
|
2209
|
+
<label className="block space-y-1">
|
|
2210
|
+
<div>{t("panel.config.pieOuterRadiusPct")}</div>
|
|
2211
|
+
<Input
|
|
2212
|
+
type="number"
|
|
2213
|
+
min={1}
|
|
2214
|
+
max={100}
|
|
2215
|
+
value={selectedElement.chart?.pieOuterRadius ?? 65}
|
|
2216
|
+
onChange={(e) =>
|
|
2217
|
+
updateSelectedChart({
|
|
2218
|
+
pieOuterRadius: Math.max(
|
|
2219
|
+
1,
|
|
2220
|
+
Math.min(100, Number(e.target.value) || 1)
|
|
2221
|
+
),
|
|
2222
|
+
})
|
|
2223
|
+
}
|
|
2224
|
+
className="h-7"
|
|
2225
|
+
/>
|
|
2226
|
+
</label>
|
|
2227
|
+
</div>
|
|
2228
|
+
) : null}
|
|
2229
|
+
</>
|
|
2230
|
+
)
|
|
2231
|
+
) : null}
|
|
2232
|
+
{renderFieldGroup(
|
|
2233
|
+
t("panel.config.groupChartDisplayMore"),
|
|
2234
|
+
<>
|
|
2225
2235
|
<label className="flex items-center gap-2">
|
|
2226
2236
|
<Checkbox
|
|
2227
2237
|
checked={selectedElement.chart?.colorMode === "gradient"}
|
|
@@ -2304,7 +2314,9 @@ export function PanelConfigSidebar({
|
|
|
2304
2314
|
</SelectContent>
|
|
2305
2315
|
</Select>
|
|
2306
2316
|
</label>
|
|
2307
|
-
|
|
2317
|
+
</>,
|
|
2318
|
+
undefined,
|
|
2319
|
+
{ groupKey: "chartDisplayMore", defaultOpen: false }
|
|
2308
2320
|
)}
|
|
2309
2321
|
{renderFieldGroup(
|
|
2310
2322
|
t("panel.config.groupTooltip"),
|
|
@@ -2361,39 +2373,10 @@ export function PanelConfigSidebar({
|
|
|
2361
2373
|
className="h-7"
|
|
2362
2374
|
/>
|
|
2363
2375
|
</label>
|
|
2364
|
-
|
|
2376
|
+
</>,
|
|
2377
|
+
undefined,
|
|
2378
|
+
{ groupKey: "chartTooltip", defaultOpen: false }
|
|
2365
2379
|
)}
|
|
2366
|
-
{renderFieldGroup(
|
|
2367
|
-
t("panel.config.groupData"),
|
|
2368
|
-
<>
|
|
2369
|
-
<label className="block space-y-1">
|
|
2370
|
-
<div>{t("panel.config.labelsCsv")}</div>
|
|
2371
|
-
<Input
|
|
2372
|
-
value={getChartLabelsDisplayText(selectedElement.chart)}
|
|
2373
|
-
onChange={(e) =>
|
|
2374
|
-
updateSelectedChart({
|
|
2375
|
-
labelsText: e.target.value,
|
|
2376
|
-
})
|
|
2377
|
-
}
|
|
2378
|
-
className="h-7"
|
|
2379
|
-
/>
|
|
2380
|
-
</label>
|
|
2381
|
-
|
|
2382
|
-
<label className="block space-y-1">
|
|
2383
|
-
<div>{t("panel.config.valuesCsv")}</div>
|
|
2384
|
-
<Input
|
|
2385
|
-
value={getChartValuesDisplayText(selectedElement.chart)}
|
|
2386
|
-
onChange={(e) =>
|
|
2387
|
-
updateSelectedChart({
|
|
2388
|
-
valuesText: e.target.value,
|
|
2389
|
-
})
|
|
2390
|
-
}
|
|
2391
|
-
className="h-7"
|
|
2392
|
-
/>
|
|
2393
|
-
</label>
|
|
2394
|
-
</>
|
|
2395
|
-
)}
|
|
2396
|
-
|
|
2397
2380
|
{selectedChartType === "bar" ||
|
|
2398
2381
|
selectedChartType === "line" ||
|
|
2399
2382
|
selectedChartType === "area" ||
|
|
@@ -2555,88 +2538,12 @@ export function PanelConfigSidebar({
|
|
|
2555
2538
|
<span>{t("panel.config.yAxisLabelAutoEllipsis")}</span>
|
|
2556
2539
|
</label>
|
|
2557
2540
|
</div>
|
|
2558
|
-
|
|
2541
|
+
</>,
|
|
2542
|
+
undefined,
|
|
2543
|
+
{ groupKey: "chartAxes", defaultOpen: false }
|
|
2559
2544
|
)
|
|
2560
2545
|
) : null}
|
|
2561
2546
|
|
|
2562
|
-
{selectedChartType === "bar" ||
|
|
2563
|
-
selectedChartType === "line" ||
|
|
2564
|
-
selectedChartType === "area" ||
|
|
2565
|
-
selectedChartType === "pie" ? (
|
|
2566
|
-
renderFieldGroup(
|
|
2567
|
-
t("panel.config.groupSeries"),
|
|
2568
|
-
<>
|
|
2569
|
-
{selectedChartType === "bar" ? (
|
|
2570
|
-
<label className="block space-y-1">
|
|
2571
|
-
<div>{t("panel.config.barWidthPx")}</div>
|
|
2572
|
-
<Input
|
|
2573
|
-
type="number"
|
|
2574
|
-
min={1}
|
|
2575
|
-
value={selectedElement.chart?.barWidth ?? 24}
|
|
2576
|
-
onChange={(e) =>
|
|
2577
|
-
updateSelectedChart({ barWidth: Math.max(1, Number(e.target.value) || 1) })
|
|
2578
|
-
}
|
|
2579
|
-
className="h-7"
|
|
2580
|
-
/>
|
|
2581
|
-
</label>
|
|
2582
|
-
) : null}
|
|
2583
|
-
|
|
2584
|
-
{selectedChartType === "line" || selectedChartType === "area" ? (
|
|
2585
|
-
<label className="flex items-center gap-2">
|
|
2586
|
-
<Checkbox
|
|
2587
|
-
checked={selectedElement.chart?.smooth ?? true}
|
|
2588
|
-
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"
|
|
2589
|
-
onCheckedChange={(checked) =>
|
|
2590
|
-
updateSelectedChart({ smooth: checked === true })
|
|
2591
|
-
}
|
|
2592
|
-
/>
|
|
2593
|
-
<span>{t("panel.config.smooth")}</span>
|
|
2594
|
-
</label>
|
|
2595
|
-
) : null}
|
|
2596
|
-
|
|
2597
|
-
{selectedChartType === "pie" ? (
|
|
2598
|
-
<div className="grid grid-cols-2 gap-2">
|
|
2599
|
-
<label className="block space-y-1">
|
|
2600
|
-
<div>{t("panel.config.pieInnerRadiusPct")}</div>
|
|
2601
|
-
<Input
|
|
2602
|
-
type="number"
|
|
2603
|
-
min={0}
|
|
2604
|
-
max={99}
|
|
2605
|
-
value={selectedElement.chart?.pieInnerRadius ?? 30}
|
|
2606
|
-
onChange={(e) =>
|
|
2607
|
-
updateSelectedChart({
|
|
2608
|
-
pieInnerRadius: Math.max(
|
|
2609
|
-
0,
|
|
2610
|
-
Math.min(99, Number(e.target.value) || 0)
|
|
2611
|
-
),
|
|
2612
|
-
})
|
|
2613
|
-
}
|
|
2614
|
-
className="h-7"
|
|
2615
|
-
/>
|
|
2616
|
-
</label>
|
|
2617
|
-
<label className="block space-y-1">
|
|
2618
|
-
<div>{t("panel.config.pieOuterRadiusPct")}</div>
|
|
2619
|
-
<Input
|
|
2620
|
-
type="number"
|
|
2621
|
-
min={1}
|
|
2622
|
-
max={100}
|
|
2623
|
-
value={selectedElement.chart?.pieOuterRadius ?? 65}
|
|
2624
|
-
onChange={(e) =>
|
|
2625
|
-
updateSelectedChart({
|
|
2626
|
-
pieOuterRadius: Math.max(
|
|
2627
|
-
1,
|
|
2628
|
-
Math.min(100, Number(e.target.value) || 1)
|
|
2629
|
-
),
|
|
2630
|
-
})
|
|
2631
|
-
}
|
|
2632
|
-
className="h-7"
|
|
2633
|
-
/>
|
|
2634
|
-
</label>
|
|
2635
|
-
</div>
|
|
2636
|
-
) : null}
|
|
2637
|
-
</>
|
|
2638
|
-
)
|
|
2639
|
-
) : null}
|
|
2640
2547
|
</>,
|
|
2641
2548
|
true,
|
|
2642
2549
|
[
|
|
@@ -3086,7 +2993,9 @@ export function PanelConfigSidebar({
|
|
|
3086
2993
|
}
|
|
3087
2994
|
/>
|
|
3088
2995
|
<span>{t("panel.config.allowCanvasInput")}</span>
|
|
3089
|
-
</label
|
|
2996
|
+
</label>,
|
|
2997
|
+
undefined,
|
|
2998
|
+
{ groupKey: "textInputAbility", defaultOpen: false }
|
|
3090
2999
|
)}
|
|
3091
3000
|
</>,
|
|
3092
3001
|
true,
|
|
@@ -3269,7 +3178,9 @@ export function PanelConfigSidebar({
|
|
|
3269
3178
|
{t("panel.config.audioPosterHint")}
|
|
3270
3179
|
</ConfigHintIcon>
|
|
3271
3180
|
</div>
|
|
3272
|
-
|
|
3181
|
+
</>,
|
|
3182
|
+
undefined,
|
|
3183
|
+
{ groupKey: "audioDisplayStyle", defaultOpen: false }
|
|
3273
3184
|
)}
|
|
3274
3185
|
</>,
|
|
3275
3186
|
true,
|
|
@@ -3442,7 +3353,8 @@ export function PanelConfigSidebar({
|
|
|
3442
3353
|
</>,
|
|
3443
3354
|
<>
|
|
3444
3355
|
{t("panel.config.canvasScriptHint")}
|
|
3445
|
-
|
|
3356
|
+
</>,
|
|
3357
|
+
{ groupKey: "geometryCanvasScript", defaultOpen: false }
|
|
3446
3358
|
)}
|
|
3447
3359
|
{renderFieldGroup(
|
|
3448
3360
|
t("panel.config.groupSketchOverlay"),
|
|
@@ -3539,7 +3451,9 @@ export function PanelConfigSidebar({
|
|
|
3539
3451
|
{t("panel.config.clearSketch")}
|
|
3540
3452
|
</button>
|
|
3541
3453
|
</div>
|
|
3542
|
-
|
|
3454
|
+
</>,
|
|
3455
|
+
undefined,
|
|
3456
|
+
{ groupKey: "geometrySketch", defaultOpen: false }
|
|
3543
3457
|
)}
|
|
3544
3458
|
</>,
|
|
3545
3459
|
true,
|
|
@@ -3699,17 +3613,193 @@ export function PanelConfigSidebar({
|
|
|
3699
3613
|
</>,
|
|
3700
3614
|
<>
|
|
3701
3615
|
{t("panel.config.copyStrategyHint")}
|
|
3702
|
-
|
|
3616
|
+
</>,
|
|
3617
|
+
{ groupKey: "referenceCopyStrategy", defaultOpen: false }
|
|
3703
3618
|
)}
|
|
3704
3619
|
</>,
|
|
3705
3620
|
true,
|
|
3706
3621
|
[t("panel.config.searchKwRef"), "ref", t("panel.config.layer"), t("panel.material.shallowCopy"), t("panel.material.deepCopy"), "snapshot"]
|
|
3707
3622
|
)
|
|
3708
|
-
) : (
|
|
3623
|
+
) : selectedElement.materialType === "image" ? null : (
|
|
3709
3624
|
<div className="text-xs leading-6 text-muted-foreground">
|
|
3710
3625
|
{t("panel.config.notChartType")}
|
|
3711
3626
|
</div>
|
|
3712
3627
|
)}
|
|
3628
|
+
{renderSection(
|
|
3629
|
+
"styleBackground",
|
|
3630
|
+
t("panel.config.sectionStyleBackground"),
|
|
3631
|
+
<>
|
|
3632
|
+
{renderFieldGroup(
|
|
3633
|
+
t("panel.config.groupBgFill"),
|
|
3634
|
+
<>
|
|
3635
|
+
{renderColorField(
|
|
3636
|
+
t("panel.config.backgroundColor"),
|
|
3637
|
+
selectedElement.style?.backgroundColor ?? "",
|
|
3638
|
+
(next) => updateSelectedStyle({ backgroundColor: next || undefined })
|
|
3639
|
+
)}
|
|
3640
|
+
</>
|
|
3641
|
+
)}
|
|
3642
|
+
{renderFieldGroup(
|
|
3643
|
+
t("panel.config.groupBgLayout"),
|
|
3644
|
+
<>
|
|
3645
|
+
<label className="block space-y-1">
|
|
3646
|
+
<div>{t("panel.config.backgroundImage")}</div>
|
|
3647
|
+
<Input
|
|
3648
|
+
value={selectedElement.style?.backgroundImage ?? ""}
|
|
3649
|
+
onChange={(e) => updateSelectedStyle({ backgroundImage: e.target.value || undefined })}
|
|
3650
|
+
placeholder='url("https://...") / linear-gradient(...)'
|
|
3651
|
+
className="h-7"
|
|
3652
|
+
/>
|
|
3653
|
+
</label>
|
|
3654
|
+
<div className="flex items-center gap-2">
|
|
3655
|
+
<label className="inline-flex cursor-pointer items-center rounded border border-border px-2 py-1 text-[11px] hover:bg-accent">
|
|
3656
|
+
{t("panel.config.uploadImage")}
|
|
3657
|
+
<Input
|
|
3658
|
+
type="file"
|
|
3659
|
+
accept="image/*"
|
|
3660
|
+
className="hidden"
|
|
3661
|
+
onChange={async (e) => {
|
|
3662
|
+
const file = e.target.files?.[0];
|
|
3663
|
+
e.currentTarget.value = "";
|
|
3664
|
+
if (!file) return;
|
|
3665
|
+
await handleUploadBackgroundImage(file);
|
|
3666
|
+
}}
|
|
3667
|
+
/>
|
|
3668
|
+
</label>
|
|
3669
|
+
{uploadStatus ? (
|
|
3670
|
+
<span className="text-[11px] text-muted-foreground">{uploadStatus}</span>
|
|
3671
|
+
) : null}
|
|
3672
|
+
</div>
|
|
3673
|
+
<div className="grid grid-cols-2 gap-2">
|
|
3674
|
+
<label className="block space-y-1">
|
|
3675
|
+
<div>{t("panel.config.backgroundSize")}</div>
|
|
3676
|
+
<Select
|
|
3677
|
+
value={selectedElement.style?.backgroundSize ?? "__none__"}
|
|
3678
|
+
onValueChange={(value) =>
|
|
3679
|
+
updateSelectedStyle({
|
|
3680
|
+
backgroundSize: value === "__none__" ? undefined : value,
|
|
3681
|
+
})
|
|
3682
|
+
}
|
|
3683
|
+
>
|
|
3684
|
+
<SelectTrigger className="h-7">
|
|
3685
|
+
<SelectValue placeholder={t("panel.config.selectBackgroundSize")} />
|
|
3686
|
+
</SelectTrigger>
|
|
3687
|
+
<SelectContent>
|
|
3688
|
+
<SelectItem value="__none__">{t("common.default")}</SelectItem>
|
|
3689
|
+
<SelectItem value="cover">cover</SelectItem>
|
|
3690
|
+
<SelectItem value="contain">contain</SelectItem>
|
|
3691
|
+
<SelectItem value="100% 100%">100% 100%</SelectItem>
|
|
3692
|
+
<SelectItem value="auto">auto</SelectItem>
|
|
3693
|
+
</SelectContent>
|
|
3694
|
+
</Select>
|
|
3695
|
+
</label>
|
|
3696
|
+
<label className="block space-y-1">
|
|
3697
|
+
<div>{t("panel.config.backgroundPosition")}</div>
|
|
3698
|
+
<Select
|
|
3699
|
+
value={selectedElement.style?.backgroundPosition ?? "__none__"}
|
|
3700
|
+
onValueChange={(value) =>
|
|
3701
|
+
updateSelectedStyle({
|
|
3702
|
+
backgroundPosition: value === "__none__" ? undefined : value,
|
|
3703
|
+
})
|
|
3704
|
+
}
|
|
3705
|
+
>
|
|
3706
|
+
<SelectTrigger className="h-7">
|
|
3707
|
+
<SelectValue placeholder={t("panel.config.selectBackgroundPosition")} />
|
|
3708
|
+
</SelectTrigger>
|
|
3709
|
+
<SelectContent>
|
|
3710
|
+
<SelectItem value="__none__">{t("common.default")}</SelectItem>
|
|
3711
|
+
<SelectItem value="center">center</SelectItem>
|
|
3712
|
+
<SelectItem value="top">top</SelectItem>
|
|
3713
|
+
<SelectItem value="bottom">bottom</SelectItem>
|
|
3714
|
+
<SelectItem value="left">left</SelectItem>
|
|
3715
|
+
<SelectItem value="right">right</SelectItem>
|
|
3716
|
+
<SelectItem value="top left">top left</SelectItem>
|
|
3717
|
+
<SelectItem value="top right">top right</SelectItem>
|
|
3718
|
+
<SelectItem value="bottom left">bottom left</SelectItem>
|
|
3719
|
+
<SelectItem value="bottom right">bottom right</SelectItem>
|
|
3720
|
+
</SelectContent>
|
|
3721
|
+
</Select>
|
|
3722
|
+
</label>
|
|
3723
|
+
</div>
|
|
3724
|
+
</>,
|
|
3725
|
+
undefined,
|
|
3726
|
+
{ groupKey: "styleBgLayout", defaultOpen: false }
|
|
3727
|
+
)}
|
|
3728
|
+
</>,
|
|
3729
|
+
false,
|
|
3730
|
+
[t("panel.config.backgroundColor"), t("panel.config.backgroundImage"), "background", "backgroundSize", "backgroundPosition", t("panel.config.searchKwLayout")]
|
|
3731
|
+
)}
|
|
3732
|
+
|
|
3733
|
+
{renderSection(
|
|
3734
|
+
"styleBorder",
|
|
3735
|
+
t("panel.config.sectionStyleBorder"),
|
|
3736
|
+
<>
|
|
3737
|
+
{renderFieldGroup(
|
|
3738
|
+
t("panel.config.groupBorderGeometry"),
|
|
3739
|
+
<div className="grid grid-cols-2 gap-2">
|
|
3740
|
+
<label className="block space-y-1">
|
|
3741
|
+
<div>{t("panel.config.borderWidth")}</div>
|
|
3742
|
+
<Input
|
|
3743
|
+
type="number"
|
|
3744
|
+
min={0}
|
|
3745
|
+
value={selectedElement.style?.borderWidth ?? 0}
|
|
3746
|
+
onChange={(e) =>
|
|
3747
|
+
updateSelectedStyle({ borderWidth: Math.max(0, Number(e.target.value) || 0) })
|
|
3748
|
+
}
|
|
3749
|
+
className="h-7"
|
|
3750
|
+
/>
|
|
3751
|
+
</label>
|
|
3752
|
+
<label className="block space-y-1">
|
|
3753
|
+
<div>{t("panel.config.borderRadius")}</div>
|
|
3754
|
+
<Input
|
|
3755
|
+
type="number"
|
|
3756
|
+
min={0}
|
|
3757
|
+
value={selectedElement.style?.borderRadius ?? 0}
|
|
3758
|
+
onChange={(e) =>
|
|
3759
|
+
updateSelectedStyle({ borderRadius: Math.max(0, Number(e.target.value) || 0) })
|
|
3760
|
+
}
|
|
3761
|
+
className="h-7"
|
|
3762
|
+
/>
|
|
3763
|
+
</label>
|
|
3764
|
+
</div>
|
|
3765
|
+
)}
|
|
3766
|
+
{renderFieldGroup(
|
|
3767
|
+
t("panel.config.groupBorderVisual"),
|
|
3768
|
+
<div className="grid grid-cols-2 gap-2">
|
|
3769
|
+
<label className="block space-y-1">
|
|
3770
|
+
<div>{t("panel.config.borderStyle")}</div>
|
|
3771
|
+
<Select
|
|
3772
|
+
value={selectedElement.style?.borderStyle ?? "solid"}
|
|
3773
|
+
onValueChange={(value) =>
|
|
3774
|
+
updateSelectedStyle({
|
|
3775
|
+
borderStyle: value as NonNullable<PanelElementStyle["borderStyle"]>,
|
|
3776
|
+
})
|
|
3777
|
+
}
|
|
3778
|
+
>
|
|
3779
|
+
<SelectTrigger className="h-7">
|
|
3780
|
+
<SelectValue placeholder={t("panel.config.selectBorderStyle")} />
|
|
3781
|
+
</SelectTrigger>
|
|
3782
|
+
<SelectContent>
|
|
3783
|
+
<SelectItem value="none">none</SelectItem>
|
|
3784
|
+
<SelectItem value="solid">solid</SelectItem>
|
|
3785
|
+
<SelectItem value="dashed">dashed</SelectItem>
|
|
3786
|
+
<SelectItem value="dotted">dotted</SelectItem>
|
|
3787
|
+
<SelectItem value="double">double</SelectItem>
|
|
3788
|
+
</SelectContent>
|
|
3789
|
+
</Select>
|
|
3790
|
+
</label>
|
|
3791
|
+
{renderColorField(
|
|
3792
|
+
t("panel.config.borderColor"),
|
|
3793
|
+
selectedElement.style?.borderColor ?? "",
|
|
3794
|
+
(next) => updateSelectedStyle({ borderColor: next || undefined })
|
|
3795
|
+
)}
|
|
3796
|
+
</div>
|
|
3797
|
+
)}
|
|
3798
|
+
</>,
|
|
3799
|
+
false,
|
|
3800
|
+
[t("panel.config.searchKwBorder"), "border", t("panel.config.searchKwWidth"), t("panel.config.searchKwRadius"), t("panel.config.color"), t("panel.config.searchKwStyle")]
|
|
3801
|
+
)}
|
|
3802
|
+
|
|
3713
3803
|
{hasSearch && renderedSectionCount === 0 ? (
|
|
3714
3804
|
<div className="rounded border border-border/60 bg-background px-2 py-1.5 text-[11px] text-muted-foreground">
|
|
3715
3805
|
{t("panel.config.noMatch")}
|
|
@@ -4,6 +4,7 @@ import { useI18n } from "@arronqzy/i18n/react";
|
|
|
4
4
|
import {
|
|
5
5
|
BlueprintExecutionLogPanel,
|
|
6
6
|
BlueprintNodeConfigSidebar,
|
|
7
|
+
type BlueprintGraphEdge,
|
|
7
8
|
type BlueprintGraphNode,
|
|
8
9
|
type ExecutionLogSettings,
|
|
9
10
|
type ExecutionTraceEntry,
|
|
@@ -41,6 +42,8 @@ export type WorkspaceConfigSidebarProps = Omit<
|
|
|
41
42
|
configFocus: WorkspaceConfigFocus;
|
|
42
43
|
executionLog?: BlueprintExecutionLogViewProps;
|
|
43
44
|
selectedBlueprintNode: BlueprintGraphNode | null;
|
|
45
|
+
blueprintGraphNodes?: BlueprintGraphNode[];
|
|
46
|
+
blueprintGraphEdges?: BlueprintGraphEdge[];
|
|
44
47
|
allowFalseSignalPropagation?: boolean;
|
|
45
48
|
onUpdateAllowFalseSignalPropagation?: (value: boolean) => void;
|
|
46
49
|
onUpdateBlueprintNode: (
|
|
@@ -74,6 +77,8 @@ export function WorkspaceConfigSidebar({
|
|
|
74
77
|
configFocus,
|
|
75
78
|
executionLog,
|
|
76
79
|
selectedBlueprintNode,
|
|
80
|
+
blueprintGraphNodes = [],
|
|
81
|
+
blueprintGraphEdges = [],
|
|
77
82
|
allowFalseSignalPropagation = false,
|
|
78
83
|
onUpdateAllowFalseSignalPropagation,
|
|
79
84
|
onUpdateBlueprintNode,
|
|
@@ -151,6 +156,9 @@ export function WorkspaceConfigSidebar({
|
|
|
151
156
|
return (
|
|
152
157
|
<BlueprintNodeConfigSidebar
|
|
153
158
|
node={selectedBlueprintNode}
|
|
159
|
+
graphNodes={blueprintGraphNodes}
|
|
160
|
+
graphEdges={blueprintGraphEdges}
|
|
161
|
+
traceEntries={executionLog?.entries}
|
|
154
162
|
allowFalseSignalPropagation={allowFalseSignalPropagation}
|
|
155
163
|
onUpdateAllowFalseSignalPropagation={onUpdateAllowFalseSignalPropagation}
|
|
156
164
|
viewElementOptions={viewElementOptions}
|
|
@@ -84,6 +84,11 @@ function deepMerge<T extends Record<string, any>>(base: T, patch?: Record<string
|
|
|
84
84
|
return next as T;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
function resolveBarWidth(chart?: PanelChartConfig): number | undefined {
|
|
88
|
+
const width = chart?.barWidth;
|
|
89
|
+
return typeof width === "number" && Number.isFinite(width) && width > 0 ? width : undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
87
92
|
function mixHex(a: string, b: string, t: number): string {
|
|
88
93
|
const pa = parseHex(a);
|
|
89
94
|
const pb = parseHex(b);
|
|
@@ -354,7 +359,8 @@ export function buildChartOption(element: PanelElement): EChartsOption {
|
|
|
354
359
|
chartType === "line" || chartType === "area"
|
|
355
360
|
? (element.chart?.smooth ?? true)
|
|
356
361
|
: undefined,
|
|
357
|
-
barWidth:
|
|
362
|
+
barWidth:
|
|
363
|
+
chartType === "bar" ? resolveBarWidth(element.chart) : undefined,
|
|
358
364
|
areaStyle: chartType === "area" ? { opacity: 0.25 } : undefined,
|
|
359
365
|
itemStyle: { color: chartColor as any },
|
|
360
366
|
lineStyle: { color: chartColor as any },
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** 产物预览是否缩放内部内容(字体等)。默认 false:块仍按视口撑满,内容保持原尺寸以免失真。 */
|
|
2
|
+
export const OUTPUT_SCALE_STORAGE_KEY = "panel:outputScale";
|
|
3
|
+
|
|
4
|
+
export function readOutputScale(): boolean {
|
|
5
|
+
try {
|
|
6
|
+
return localStorage.getItem(OUTPUT_SCALE_STORAGE_KEY) === "1";
|
|
7
|
+
} catch {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function writeOutputScale(enabled: boolean) {
|
|
13
|
+
try {
|
|
14
|
+
localStorage.setItem(OUTPUT_SCALE_STORAGE_KEY, enabled ? "1" : "0");
|
|
15
|
+
} catch {
|
|
16
|
+
// ignore storage errors
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type PreviewFillScale = {
|
|
21
|
+
vw: number;
|
|
22
|
+
vh: number;
|
|
23
|
+
scaleX: number;
|
|
24
|
+
scaleY: number;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export function getPreviewFillScale(
|
|
28
|
+
sceneWidth: number,
|
|
29
|
+
sceneHeight: number
|
|
30
|
+
): PreviewFillScale {
|
|
31
|
+
const vw = window.innerWidth || 1;
|
|
32
|
+
const vh = window.innerHeight || 1;
|
|
33
|
+
const sw = Math.max(1, sceneWidth);
|
|
34
|
+
const sh = Math.max(1, sceneHeight);
|
|
35
|
+
return {
|
|
36
|
+
vw,
|
|
37
|
+
vh,
|
|
38
|
+
scaleX: vw / sw,
|
|
39
|
+
scaleY: vh / sh,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 预览始终按视口宽高比把场景块撑满。
|
|
45
|
+
* outputScale=true:CSS transform 缩放整棵内容(含字体)。
|
|
46
|
+
* outputScale=false:只把场景盒拉到视口大小,节点几何由调用方按 scale 映射,字体不跟着拉伸。
|
|
47
|
+
*/
|
|
48
|
+
export function applyPreviewSceneFill(
|
|
49
|
+
scene: HTMLElement | null,
|
|
50
|
+
sceneWidth: number,
|
|
51
|
+
sceneHeight: number,
|
|
52
|
+
outputScale: boolean
|
|
53
|
+
): PreviewFillScale {
|
|
54
|
+
const fill = getPreviewFillScale(sceneWidth, sceneHeight);
|
|
55
|
+
if (!Number.isFinite(fill.scaleX) || !Number.isFinite(fill.scaleY)) return fill;
|
|
56
|
+
if (!scene) return fill;
|
|
57
|
+
const root = scene.parentElement;
|
|
58
|
+
if (root) root.style.overflow = "hidden";
|
|
59
|
+
if (outputScale) {
|
|
60
|
+
scene.style.width = `${Math.max(1, sceneWidth)}px`;
|
|
61
|
+
scene.style.height = `${Math.max(1, sceneHeight)}px`;
|
|
62
|
+
scene.style.transform = `translate(0px,0px) scale(${fill.scaleX},${fill.scaleY})`;
|
|
63
|
+
} else {
|
|
64
|
+
scene.style.width = `${fill.vw}px`;
|
|
65
|
+
scene.style.height = `${fill.vh}px`;
|
|
66
|
+
scene.style.transform = "none";
|
|
67
|
+
}
|
|
68
|
+
return fill;
|
|
69
|
+
}
|