@arronqzy/react-view 1.0.5 → 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 +519 -9
- package/package.json +12 -4
- package/src/core/template.tsx +12 -2
- package/src/panel/ReactViewOnlinePreview.tsx +18 -8
- package/src/panel/ReactViewPanel.tsx +386 -170
- 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 +6 -2
- package/src/panel/components/ElementsLayer.tsx +37 -18
- package/src/panel/components/MaterialSidebar.tsx +157 -109
- package/src/panel/components/MoveableLayer.tsx +7 -6
- package/src/panel/components/PanelConfigSidebar.tsx +434 -389
- package/src/panel/components/ViewElementScopePanel.tsx +9 -5
- 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/ScopeTemplatePreviewHost.tsx +11 -7
- package/src/panel/components/scope-config/ScopeTemplateUsageHint.tsx +6 -18
- package/src/panel/components/scope-config/ScopeTemplateWarningsPanel.tsx +5 -2
- 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/useWorkspaceProjects.ts +11 -8
- package/src/panel/types.ts +4 -0
- package/src/panel/utils/panelElementDefaults.ts +69 -39
- package/src/panel/utils/scope-field-labels.ts +51 -38
- package/src/panel/utils/scope-template-preview.ts +10 -6
- package/src/panel/utils/scope-template-spread.ts +44 -21
- package/src/panel/utils/scope-template-warnings.ts +28 -16
- package/src/panel/utils/scope-template.ts +82 -2
- package/src/tailwind.css +17 -9
|
@@ -1,44 +1,57 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"chart.
|
|
10
|
-
"chart.
|
|
11
|
-
"chart.
|
|
12
|
-
"chart.
|
|
13
|
-
"chart.
|
|
14
|
-
"chart.
|
|
15
|
-
"chart.
|
|
16
|
-
"chart.
|
|
17
|
-
"chart.
|
|
18
|
-
"chart.
|
|
19
|
-
"chart.
|
|
20
|
-
"chart.
|
|
21
|
-
"chart.
|
|
22
|
-
"chart.
|
|
23
|
-
"chart.
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"style.
|
|
27
|
-
"style.
|
|
28
|
-
"style.
|
|
29
|
-
"style.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
1
|
+
import type { TranslateFn } from "@arronqzy/i18n";
|
|
2
|
+
|
|
3
|
+
const FIELD_KEY_MAP: Record<string, string> = {
|
|
4
|
+
name: "panel.scope.fieldName",
|
|
5
|
+
textHtml: "panel.scope.fieldTextHtml",
|
|
6
|
+
textFontFamily: "panel.scope.fieldTextFontFamily",
|
|
7
|
+
textFontWeight: "panel.scope.fieldTextFontWeight",
|
|
8
|
+
textColor: "panel.scope.fieldTextColor",
|
|
9
|
+
"chart.title": "panel.scope.fieldChartTitle",
|
|
10
|
+
"chart.color": "panel.scope.fieldChartColor",
|
|
11
|
+
"chart.gradientFrom": "panel.scope.fieldChartGradientFrom",
|
|
12
|
+
"chart.gradientTo": "panel.scope.fieldChartGradientTo",
|
|
13
|
+
"chart.xAxisName": "panel.scope.fieldChartXAxisName",
|
|
14
|
+
"chart.yAxisName": "panel.scope.fieldChartYAxisName",
|
|
15
|
+
"chart.xAxisTickColor": "panel.scope.fieldChartXAxisTickColor",
|
|
16
|
+
"chart.yAxisTickColor": "panel.scope.fieldChartYAxisTickColor",
|
|
17
|
+
"chart.xAxisSplitLineColor": "panel.scope.fieldChartXAxisSplitLineColor",
|
|
18
|
+
"chart.yAxisSplitLineColor": "panel.scope.fieldChartYAxisSplitLineColor",
|
|
19
|
+
"chart.xAxisLabelColor": "panel.scope.fieldChartXAxisLabelColor",
|
|
20
|
+
"chart.yAxisLabelColor": "panel.scope.fieldChartYAxisLabelColor",
|
|
21
|
+
"chart.tooltipBackgroundColor": "panel.scope.fieldChartTooltipBackgroundColor",
|
|
22
|
+
"chart.tooltipTextColor": "panel.scope.fieldChartTooltipTextColor",
|
|
23
|
+
"chart.tooltipFormatter": "panel.scope.fieldChartTooltipFormatter",
|
|
24
|
+
"chart.labelsText": "panel.scope.fieldChartLabelsText",
|
|
25
|
+
"chart.valuesText": "panel.scope.fieldChartValuesText",
|
|
26
|
+
"style.backgroundColor": "panel.scope.fieldStyleBackgroundColor",
|
|
27
|
+
"style.backgroundImage": "panel.scope.fieldStyleBackgroundImage",
|
|
28
|
+
"style.backgroundImageRemoteUrl": "panel.scope.fieldStyleBackgroundImageRemoteUrl",
|
|
29
|
+
"style.backgroundSize": "panel.scope.fieldStyleBackgroundSize",
|
|
30
|
+
"style.backgroundPosition": "panel.scope.fieldStyleBackgroundPosition",
|
|
31
|
+
"style.borderColor": "panel.scope.fieldStyleBorderColor",
|
|
32
|
+
audioSrc: "panel.scope.fieldAudioSrc",
|
|
33
|
+
audioRemoteUrl: "panel.scope.fieldAudioRemoteUrl",
|
|
34
|
+
videoSrc: "panel.scope.fieldVideoSrc",
|
|
35
|
+
videoRemoteUrl: "panel.scope.fieldVideoRemoteUrl",
|
|
36
|
+
geometryColor: "panel.scope.fieldGeometryColor",
|
|
37
|
+
geometryScript: "panel.scope.fieldGeometryScript",
|
|
36
38
|
};
|
|
37
39
|
|
|
38
|
-
export function
|
|
39
|
-
|
|
40
|
+
export function getScopeFieldLabels(t: TranslateFn): Record<string, string> {
|
|
41
|
+
const labels: Record<string, string> = {};
|
|
42
|
+
for (const [fieldId, key] of Object.entries(FIELD_KEY_MAP)) {
|
|
43
|
+
labels[fieldId] = t(key);
|
|
44
|
+
}
|
|
45
|
+
return labels;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function resolveScopeFieldLabel(fieldId: string, t?: TranslateFn): string {
|
|
49
|
+
if (t) {
|
|
50
|
+
const key = FIELD_KEY_MAP[fieldId];
|
|
51
|
+
if (key) return t(key);
|
|
52
|
+
}
|
|
40
53
|
const leaf = fieldId.split(".").pop() ?? fieldId;
|
|
41
|
-
return
|
|
54
|
+
return leaf;
|
|
42
55
|
}
|
|
43
56
|
|
|
44
57
|
export function scopeFieldDomId(fieldId: string): string {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { TranslateFn } from "@arronqzy/i18n";
|
|
2
|
+
import { tForLocale } from "@arronqzy/i18n";
|
|
1
3
|
import type { PanelElement } from "../types";
|
|
2
4
|
import {
|
|
3
5
|
materializeChartLabelsText,
|
|
@@ -22,7 +24,8 @@ function shouldSkipField(fieldId: string): boolean {
|
|
|
22
24
|
function walkElementTemplateFields(
|
|
23
25
|
value: unknown,
|
|
24
26
|
fieldId: string,
|
|
25
|
-
results: ScopeTemplateField[]
|
|
27
|
+
results: ScopeTemplateField[],
|
|
28
|
+
t: TranslateFn
|
|
26
29
|
) {
|
|
27
30
|
if (shouldSkipField(fieldId)) return;
|
|
28
31
|
|
|
@@ -30,7 +33,7 @@ function walkElementTemplateFields(
|
|
|
30
33
|
if (!hasScopeTemplate(value)) return;
|
|
31
34
|
results.push({
|
|
32
35
|
fieldId,
|
|
33
|
-
fieldLabel: resolveScopeFieldLabel(fieldId),
|
|
36
|
+
fieldLabel: resolveScopeFieldLabel(fieldId, t),
|
|
34
37
|
template: value,
|
|
35
38
|
});
|
|
36
39
|
return;
|
|
@@ -38,7 +41,7 @@ function walkElementTemplateFields(
|
|
|
38
41
|
|
|
39
42
|
if (Array.isArray(value)) {
|
|
40
43
|
value.forEach((item, index) => {
|
|
41
|
-
walkElementTemplateFields(item, `${fieldId}.${index}`, results);
|
|
44
|
+
walkElementTemplateFields(item, `${fieldId}.${index}`, results, t);
|
|
42
45
|
});
|
|
43
46
|
return;
|
|
44
47
|
}
|
|
@@ -46,16 +49,17 @@ function walkElementTemplateFields(
|
|
|
46
49
|
if (value && typeof value === "object") {
|
|
47
50
|
for (const [key, nested] of Object.entries(value)) {
|
|
48
51
|
const nextId = fieldId ? `${fieldId}.${key}` : key;
|
|
49
|
-
walkElementTemplateFields(nested, nextId, results);
|
|
52
|
+
walkElementTemplateFields(nested, nextId, results, t);
|
|
50
53
|
}
|
|
51
54
|
}
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
export function collectElementScopeTemplateFields(
|
|
55
|
-
element: PanelElement
|
|
58
|
+
element: PanelElement,
|
|
59
|
+
t: TranslateFn = tForLocale("zh-CN")
|
|
56
60
|
): ScopeTemplateField[] {
|
|
57
61
|
const results: ScopeTemplateField[] = [];
|
|
58
|
-
walkElementTemplateFields(element, "", results);
|
|
62
|
+
walkElementTemplateFields(element, "", results, t);
|
|
59
63
|
return results;
|
|
60
64
|
}
|
|
61
65
|
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/** 数组展开模版:[...{scope?.a?.b}] 从 scope 中定位数组并提取每项字段,输出逗号分隔值 */
|
|
2
2
|
|
|
3
|
+
import type { TranslateFn } from "@arronqzy/i18n";
|
|
4
|
+
import { tForLocale } from "@arronqzy/i18n";
|
|
5
|
+
|
|
3
6
|
export const SCOPE_SPREAD_TEMPLATE_RE = /\[\.\.\.\{([^}]+)\}\]/g;
|
|
4
7
|
|
|
5
8
|
export const SCOPE_SPREAD_TEMPLATE_DEMO = {
|
|
@@ -8,15 +11,22 @@ export const SCOPE_SPREAD_TEMPLATE_DEMO = {
|
|
|
8
11
|
output: "1,2",
|
|
9
12
|
} as const;
|
|
10
13
|
|
|
11
|
-
export
|
|
12
|
-
scope
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
export function getScopeSpreadDeepTemplateDemo(t: TranslateFn = tForLocale("zh-CN")) {
|
|
15
|
+
const inProgress = t("panel.scope.demoInProgress");
|
|
16
|
+
const done = t("panel.scope.demoDone");
|
|
17
|
+
return {
|
|
18
|
+
scope: {
|
|
19
|
+
data: {
|
|
20
|
+
data: [{ statusName: inProgress }, { statusName: done }],
|
|
21
|
+
},
|
|
15
22
|
},
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
23
|
+
template: "[...{scope?.data?.data?.statusName}]",
|
|
24
|
+
output: t("panel.scope.demoJoined"),
|
|
25
|
+
} as const;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** @deprecated Prefer getScopeSpreadDeepTemplateDemo(t) */
|
|
29
|
+
export const SCOPE_SPREAD_DEEP_TEMPLATE_DEMO = getScopeSpreadDeepTemplateDemo();
|
|
20
30
|
|
|
21
31
|
export type ScopeSpreadEvalIssue =
|
|
22
32
|
| "invalid-expression"
|
|
@@ -205,25 +215,38 @@ export function buildSpreadNotArrayWarningMessage(
|
|
|
205
215
|
fieldLabel: string,
|
|
206
216
|
template: string,
|
|
207
217
|
expression: string,
|
|
208
|
-
arrayPath: string
|
|
218
|
+
arrayPath: string,
|
|
219
|
+
t: TranslateFn = tForLocale("zh-CN")
|
|
209
220
|
): string {
|
|
221
|
+
void expression;
|
|
210
222
|
const demoScope = JSON.stringify(SCOPE_SPREAD_TEMPLATE_DEMO.scope);
|
|
211
|
-
const deepDemo =
|
|
212
|
-
return (
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
223
|
+
const deepDemo = getScopeSpreadDeepTemplateDemo(t);
|
|
224
|
+
return t("panel.scope.warnSpreadNotArray", {
|
|
225
|
+
fieldLabel,
|
|
226
|
+
template,
|
|
227
|
+
arrayPath,
|
|
228
|
+
demoScope,
|
|
229
|
+
demoTemplate: SCOPE_SPREAD_TEMPLATE_DEMO.template,
|
|
230
|
+
demoOutput: SCOPE_SPREAD_TEMPLATE_DEMO.output,
|
|
231
|
+
deepTemplate: deepDemo.template,
|
|
232
|
+
deepScope: JSON.stringify(deepDemo.scope),
|
|
233
|
+
deepOutput: deepDemo.output,
|
|
234
|
+
});
|
|
217
235
|
}
|
|
218
236
|
|
|
219
237
|
export function buildSpreadInvalidExpressionWarningMessage(
|
|
220
238
|
fieldLabel: string,
|
|
221
|
-
template: string
|
|
239
|
+
template: string,
|
|
240
|
+
t: TranslateFn = tForLocale("zh-CN")
|
|
222
241
|
): string {
|
|
223
242
|
const demoScope = JSON.stringify(SCOPE_SPREAD_TEMPLATE_DEMO.scope);
|
|
224
|
-
const deepDemo =
|
|
225
|
-
return (
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
243
|
+
const deepDemo = getScopeSpreadDeepTemplateDemo(t);
|
|
244
|
+
return t("panel.scope.warnSpreadInvalid", {
|
|
245
|
+
fieldLabel,
|
|
246
|
+
template,
|
|
247
|
+
demoTemplate: SCOPE_SPREAD_TEMPLATE_DEMO.template,
|
|
248
|
+
deepTemplate: deepDemo.template,
|
|
249
|
+
demoScope,
|
|
250
|
+
demoOutput: SCOPE_SPREAD_TEMPLATE_DEMO.output,
|
|
251
|
+
});
|
|
229
252
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { TranslateFn } from "@arronqzy/i18n";
|
|
2
|
+
import { tForLocale } from "@arronqzy/i18n";
|
|
1
3
|
import type { PanelElement } from "../types";
|
|
2
4
|
import { hasScopeTemplate } from "./scope-template";
|
|
3
5
|
import { resolveScopeFieldLabel } from "./scope-field-labels";
|
|
@@ -59,10 +61,11 @@ function pathExistsInScope(scope: unknown, path: string): boolean {
|
|
|
59
61
|
function analyzeSpreadTemplateValue(
|
|
60
62
|
fieldId: string,
|
|
61
63
|
value: string,
|
|
62
|
-
scope: unknown
|
|
64
|
+
scope: unknown,
|
|
65
|
+
t: TranslateFn
|
|
63
66
|
): ScopeTemplateWarning[] {
|
|
64
67
|
const warnings: ScopeTemplateWarning[] = [];
|
|
65
|
-
const fieldLabel = resolveScopeFieldLabel(fieldId);
|
|
68
|
+
const fieldLabel = resolveScopeFieldLabel(fieldId, t);
|
|
66
69
|
const spreadRe = new RegExp(SCOPE_SPREAD_TEMPLATE_RE.source, "g");
|
|
67
70
|
let match: RegExpExecArray | null;
|
|
68
71
|
|
|
@@ -81,7 +84,8 @@ function analyzeSpreadTemplateValue(
|
|
|
81
84
|
kind: "spread-invalid-expression",
|
|
82
85
|
message: buildSpreadInvalidExpressionWarningMessage(
|
|
83
86
|
fieldLabel,
|
|
84
|
-
spreadTemplate
|
|
87
|
+
spreadTemplate,
|
|
88
|
+
t
|
|
85
89
|
),
|
|
86
90
|
});
|
|
87
91
|
continue;
|
|
@@ -100,7 +104,8 @@ function analyzeSpreadTemplateValue(
|
|
|
100
104
|
kind: "spread-invalid-expression",
|
|
101
105
|
message: buildSpreadInvalidExpressionWarningMessage(
|
|
102
106
|
fieldLabel,
|
|
103
|
-
spreadTemplate
|
|
107
|
+
spreadTemplate,
|
|
108
|
+
t
|
|
104
109
|
),
|
|
105
110
|
});
|
|
106
111
|
continue;
|
|
@@ -118,7 +123,10 @@ function analyzeSpreadTemplateValue(
|
|
|
118
123
|
expression,
|
|
119
124
|
missingPath: arrayPath,
|
|
120
125
|
kind: "missing-path",
|
|
121
|
-
message:
|
|
126
|
+
message: t("panel.scope.warnMissingPath", {
|
|
127
|
+
fieldLabel,
|
|
128
|
+
path: arrayPath,
|
|
129
|
+
}),
|
|
122
130
|
});
|
|
123
131
|
}
|
|
124
132
|
continue;
|
|
@@ -136,7 +144,8 @@ function analyzeSpreadTemplateValue(
|
|
|
136
144
|
fieldLabel,
|
|
137
145
|
spreadTemplate,
|
|
138
146
|
expression,
|
|
139
|
-
arrayPath
|
|
147
|
+
arrayPath,
|
|
148
|
+
t
|
|
140
149
|
),
|
|
141
150
|
});
|
|
142
151
|
}
|
|
@@ -148,14 +157,15 @@ function analyzeSpreadTemplateValue(
|
|
|
148
157
|
function analyzeTemplateValue(
|
|
149
158
|
fieldId: string,
|
|
150
159
|
value: string,
|
|
151
|
-
scope: unknown
|
|
160
|
+
scope: unknown,
|
|
161
|
+
t: TranslateFn
|
|
152
162
|
): ScopeTemplateWarning[] {
|
|
153
163
|
if (!hasScopeTemplate(value)) return [];
|
|
154
164
|
|
|
155
165
|
const warnings: ScopeTemplateWarning[] = [
|
|
156
|
-
...analyzeSpreadTemplateValue(fieldId, value, scope),
|
|
166
|
+
...analyzeSpreadTemplateValue(fieldId, value, scope, t),
|
|
157
167
|
];
|
|
158
|
-
const fieldLabel = resolveScopeFieldLabel(fieldId);
|
|
168
|
+
const fieldLabel = resolveScopeFieldLabel(fieldId, t);
|
|
159
169
|
const expressionRe = /\{([^}]+)\}/g;
|
|
160
170
|
let match: RegExpExecArray | null;
|
|
161
171
|
|
|
@@ -172,7 +182,7 @@ function analyzeTemplateValue(
|
|
|
172
182
|
expression,
|
|
173
183
|
missingPath: path,
|
|
174
184
|
kind: "missing-path",
|
|
175
|
-
message:
|
|
185
|
+
message: t("panel.scope.warnMissingPath", { fieldLabel, path }),
|
|
176
186
|
});
|
|
177
187
|
}
|
|
178
188
|
}
|
|
@@ -188,18 +198,19 @@ function walkElementStrings(
|
|
|
188
198
|
value: unknown,
|
|
189
199
|
fieldId: string,
|
|
190
200
|
scope: unknown,
|
|
191
|
-
warnings: ScopeTemplateWarning[]
|
|
201
|
+
warnings: ScopeTemplateWarning[],
|
|
202
|
+
t: TranslateFn
|
|
192
203
|
) {
|
|
193
204
|
if (shouldSkipField(fieldId)) return;
|
|
194
205
|
|
|
195
206
|
if (typeof value === "string") {
|
|
196
|
-
warnings.push(...analyzeTemplateValue(fieldId, value, scope));
|
|
207
|
+
warnings.push(...analyzeTemplateValue(fieldId, value, scope, t));
|
|
197
208
|
return;
|
|
198
209
|
}
|
|
199
210
|
|
|
200
211
|
if (Array.isArray(value)) {
|
|
201
212
|
value.forEach((item, index) => {
|
|
202
|
-
walkElementStrings(item, `${fieldId}.${index}`, scope, warnings);
|
|
213
|
+
walkElementStrings(item, `${fieldId}.${index}`, scope, warnings, t);
|
|
203
214
|
});
|
|
204
215
|
return;
|
|
205
216
|
}
|
|
@@ -207,19 +218,20 @@ function walkElementStrings(
|
|
|
207
218
|
if (value && typeof value === "object") {
|
|
208
219
|
for (const [key, nested] of Object.entries(value)) {
|
|
209
220
|
const nextId = fieldId ? `${fieldId}.${key}` : key;
|
|
210
|
-
walkElementStrings(nested, nextId, scope, warnings);
|
|
221
|
+
walkElementStrings(nested, nextId, scope, warnings, t);
|
|
211
222
|
}
|
|
212
223
|
}
|
|
213
224
|
}
|
|
214
225
|
|
|
215
226
|
export function collectElementScopeWarnings(
|
|
216
227
|
element: PanelElement,
|
|
217
|
-
scope: unknown
|
|
228
|
+
scope: unknown,
|
|
229
|
+
t: TranslateFn = tForLocale("zh-CN")
|
|
218
230
|
): ScopeTemplateWarning[] {
|
|
219
231
|
if (scope === undefined) return [];
|
|
220
232
|
|
|
221
233
|
const warnings: ScopeTemplateWarning[] = [];
|
|
222
|
-
walkElementStrings(element, "", scope, warnings);
|
|
234
|
+
walkElementStrings(element, "", scope, warnings, t);
|
|
223
235
|
|
|
224
236
|
const seen = new Set<string>();
|
|
225
237
|
return warnings.filter((warning) => {
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import type { PanelElement } from "../types";
|
|
2
|
+
import type { PanelTableConfig } from "@arronqzy/view-table";
|
|
3
|
+
import { isUsableTableRawData } from "@arronqzy/view-table";
|
|
2
4
|
import { materializeChartLabelsFromScope } from "./scope-template-chart";
|
|
3
5
|
import {
|
|
4
6
|
SCOPE_SPREAD_TEMPLATE_RE,
|
|
5
7
|
evaluateSpreadScopeExpression,
|
|
8
|
+
extractSpreadScopeExpression,
|
|
9
|
+
getValueAtScopePath,
|
|
10
|
+
isSpreadOnlyScopeTemplate,
|
|
11
|
+
resolveSpreadScopePath,
|
|
6
12
|
} from "./scope-template-spread";
|
|
7
13
|
|
|
8
14
|
const SCOPE_TEMPLATE_RE = /\{[^}]+\}|\[\.\.\.\{[^}]+\}\]/;
|
|
@@ -33,13 +39,13 @@ export function evaluateScopeTemplate(
|
|
|
33
39
|
|
|
34
40
|
const withSpread = template.replace(
|
|
35
41
|
SCOPE_SPREAD_TEMPLATE_RE,
|
|
36
|
-
(_, rawExpr: string) => {
|
|
42
|
+
(_: string, rawExpr: string) => {
|
|
37
43
|
const result = evaluateSpreadScopeExpression(rawExpr, scope);
|
|
38
44
|
return result.ok ? result.value : "";
|
|
39
45
|
}
|
|
40
46
|
);
|
|
41
47
|
|
|
42
|
-
return withSpread.replace(/\{([^}]+)\}/g, (_, rawExpr: string) => {
|
|
48
|
+
return withSpread.replace(/\{([^}]+)\}/g, (_: string, rawExpr: string) => {
|
|
43
49
|
const value = evaluateScopeExpression(rawExpr, scope);
|
|
44
50
|
if (value === null || value === undefined) return "";
|
|
45
51
|
if (typeof value === "string") return value;
|
|
@@ -54,6 +60,65 @@ export function evaluateScopeTemplate(
|
|
|
54
60
|
});
|
|
55
61
|
}
|
|
56
62
|
|
|
63
|
+
function tryParseJson(text: string): unknown | undefined {
|
|
64
|
+
try {
|
|
65
|
+
return JSON.parse(text);
|
|
66
|
+
} catch {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function materializeTemplateValue(template: string, scope: unknown): unknown {
|
|
72
|
+
const trimmed = template.trim();
|
|
73
|
+
if (!trimmed) return undefined;
|
|
74
|
+
|
|
75
|
+
if (isSpreadOnlyScopeTemplate(trimmed)) {
|
|
76
|
+
const expression = extractSpreadScopeExpression(trimmed);
|
|
77
|
+
if (!expression) return undefined;
|
|
78
|
+
const resolved = resolveSpreadScopePath(expression, scope);
|
|
79
|
+
if (!resolved.ok) return undefined;
|
|
80
|
+
if (resolved.itemPathSegments.length === 0) {
|
|
81
|
+
return resolved.arrayValue;
|
|
82
|
+
}
|
|
83
|
+
return resolved.arrayValue.map((item) =>
|
|
84
|
+
getValueAtScopePath(item, resolved.itemPathSegments)
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const singleMatch = /^\{([^}]+)\}$/.exec(trimmed);
|
|
89
|
+
if (singleMatch) {
|
|
90
|
+
return evaluateScopeExpression(singleMatch[1]!, scope);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (hasScopeTemplate(trimmed)) {
|
|
94
|
+
const text = evaluateScopeTemplate(trimmed, scope);
|
|
95
|
+
const parsed = tryParseJson(text);
|
|
96
|
+
return parsed !== undefined ? parsed : text;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return tryParseJson(trimmed);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function materializeTableSourceFromScope(
|
|
103
|
+
table: PanelTableConfig | undefined,
|
|
104
|
+
scope: unknown
|
|
105
|
+
): unknown | undefined {
|
|
106
|
+
if (!table) return undefined;
|
|
107
|
+
|
|
108
|
+
if (typeof table.source === "string" && table.source.trim()) {
|
|
109
|
+
return materializeTemplateValue(table.source, scope);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (typeof table.rowsText === "string" && table.rowsText.trim()) {
|
|
113
|
+
const trimmed = table.rowsText.trim();
|
|
114
|
+
if (hasScopeTemplate(trimmed) || isSpreadOnlyScopeTemplate(trimmed)) {
|
|
115
|
+
return materializeTemplateValue(trimmed, scope);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
|
|
57
122
|
function resolveScopedValue<T>(value: T, scope: unknown): T {
|
|
58
123
|
if (typeof value === "string" && hasScopeTemplate(value)) {
|
|
59
124
|
return evaluateScopeTemplate(value, scope) as T;
|
|
@@ -84,6 +149,21 @@ export function resolvePanelElementScope(
|
|
|
84
149
|
delete resolved.chart.labelsText;
|
|
85
150
|
}
|
|
86
151
|
}
|
|
152
|
+
if (resolved.table) {
|
|
153
|
+
const hasDynamicSource =
|
|
154
|
+
typeof element.table?.source === "string" && element.table.source.trim().length > 0;
|
|
155
|
+
if (hasDynamicSource) {
|
|
156
|
+
const raw = materializeTableSourceFromScope(element.table, scope);
|
|
157
|
+
if (isUsableTableRawData(raw)) {
|
|
158
|
+
resolved.table = { ...resolved.table, rows: raw };
|
|
159
|
+
} else {
|
|
160
|
+
// Dynamic missing/empty → fall back to static rowsText (do not keep empty rows)
|
|
161
|
+
const next = { ...resolved.table };
|
|
162
|
+
delete next.rows;
|
|
163
|
+
resolved.table = next;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
87
167
|
return resolved;
|
|
88
168
|
}
|
|
89
169
|
|
package/src/tailwind.css
CHANGED
|
@@ -26,17 +26,24 @@
|
|
|
26
26
|
background: hsl(var(--foreground) / 0.45);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
/*
|
|
29
|
+
/*
|
|
30
|
+
* Moveable 控制框:
|
|
31
|
+
* - 根节点必须高于画布元素 zIndex(节点默认 ≥1),否则多选组框线会被节点盖住,只剩锚点可见
|
|
32
|
+
* - 勿把 .moveable-group 压到 z-index:1(会覆盖根节点的 9999)
|
|
33
|
+
*/
|
|
30
34
|
.rv-moveable-layer {
|
|
31
35
|
--moveable-color: #1990ff;
|
|
32
36
|
z-index: 9999 !important;
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
.rv-moveable-layer.moveable-group,
|
|
36
|
-
.rv-moveable-layer
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
.rv-moveable-layer.moveable-control-box {
|
|
41
|
+
z-index: 9999 !important;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* Moveable 默认 .group { z-index: -1 },必须覆盖,避免整组掉到节点下方 */
|
|
45
|
+
.rv-moveable-layer.moveable-group {
|
|
46
|
+
z-index: 9999 !important;
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
.rv-moveable-layer .moveable-line {
|
|
@@ -44,13 +51,13 @@
|
|
|
44
51
|
background: var(--moveable-color) !important;
|
|
45
52
|
}
|
|
46
53
|
|
|
47
|
-
/*
|
|
48
|
-
.rv-moveable-layer .moveable-line.moveable-direction {
|
|
54
|
+
/* 组选/边线:保持颜色;厚度仍由 moveable zoom 的 scaleY 补偿 */
|
|
55
|
+
.rv-moveable-layer .moveable-line.moveable-direction:not(.moveable-edge) {
|
|
49
56
|
background: var(--moveable-color) !important;
|
|
50
57
|
}
|
|
51
58
|
|
|
52
|
-
|
|
53
|
-
.rv-moveable-layer.moveable-group .moveable-control-box {
|
|
59
|
+
/* 子 control-box 相对组内层叠,勿压低根节点 */
|
|
60
|
+
.rv-moveable-layer.moveable-group > .moveable-control-box {
|
|
54
61
|
z-index: 2 !important;
|
|
55
62
|
}
|
|
56
63
|
|
|
@@ -68,4 +75,5 @@
|
|
|
68
75
|
border: 2px solid hsl(var(--background)) !important;
|
|
69
76
|
background: #1990ff !important;
|
|
70
77
|
background: var(--moveable-color) !important;
|
|
78
|
+
z-index: 10 !important;
|
|
71
79
|
}
|