@jotx-labs/editor 2.4.193 → 2.4.204
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/components/BlockMenu.d.ts.map +1 -1
- package/dist/components/BlockMenu.js +61 -7
- package/dist/components/BlockMenu.js.map +1 -1
- package/dist/components/ButtonNodeView.d.ts +1 -0
- package/dist/components/ButtonNodeView.d.ts.map +1 -1
- package/dist/components/ButtonNodeView.js +34 -13
- package/dist/components/ButtonNodeView.js.map +1 -1
- package/dist/components/ButtonStripNodeView.d.ts +13 -0
- package/dist/components/ButtonStripNodeView.d.ts.map +1 -0
- package/dist/components/ButtonStripNodeView.js +96 -0
- package/dist/components/ButtonStripNodeView.js.map +1 -0
- package/dist/components/ChartNodeView.d.ts.map +1 -1
- package/dist/components/ChartNodeView.js +504 -203
- package/dist/components/ChartNodeView.js.map +1 -1
- package/dist/components/ColumnNodeView.d.ts +6 -0
- package/dist/components/ColumnNodeView.d.ts.map +1 -0
- package/dist/components/ColumnNodeView.js +13 -0
- package/dist/components/ColumnNodeView.js.map +1 -0
- package/dist/components/ColumnsNodeView.d.ts +14 -0
- package/dist/components/ColumnsNodeView.d.ts.map +1 -0
- package/dist/components/ColumnsNodeView.js +122 -0
- package/dist/components/ColumnsNodeView.js.map +1 -0
- package/dist/components/DocumentHeader/DocumentHeader.d.ts.map +1 -1
- package/dist/components/DocumentHeader/DocumentHeader.js +1 -0
- package/dist/components/DocumentHeader/DocumentHeader.js.map +1 -1
- package/dist/components/DocumentHeader/ExportMenu.d.ts +2 -2
- package/dist/components/DocumentHeader/ExportMenu.d.ts.map +1 -1
- package/dist/components/DocumentHeader/ExportMenu.js +129 -9
- package/dist/components/DocumentHeader/ExportMenu.js.map +1 -1
- package/dist/components/JotxEditor.d.ts.map +1 -1
- package/dist/components/JotxEditor.js +52 -0
- package/dist/components/JotxEditor.js.map +1 -1
- package/dist/components/MathNodeView.d.ts.map +1 -1
- package/dist/components/MathNodeView.js +10 -1
- package/dist/components/MathNodeView.js.map +1 -1
- package/dist/components/PresentationToolbar.d.ts +13 -0
- package/dist/components/PresentationToolbar.d.ts.map +1 -0
- package/dist/components/PresentationToolbar.js +14 -0
- package/dist/components/PresentationToolbar.js.map +1 -0
- package/dist/components/PresentationView.d.ts +18 -0
- package/dist/components/PresentationView.d.ts.map +1 -0
- package/dist/components/PresentationView.js +77 -0
- package/dist/components/PresentationView.js.map +1 -0
- package/dist/components/SlashMenu.d.ts.map +1 -1
- package/dist/components/SlashMenu.js +16 -0
- package/dist/components/SlashMenu.js.map +1 -1
- package/dist/components/SlidePropertiesNodeView.d.ts +3 -0
- package/dist/components/SlidePropertiesNodeView.d.ts.map +1 -0
- package/dist/components/SlidePropertiesNodeView.js +87 -0
- package/dist/components/SlidePropertiesNodeView.js.map +1 -0
- package/dist/extensions/ButtonNode.d.ts.map +1 -1
- package/dist/extensions/ButtonNode.js +13 -9
- package/dist/extensions/ButtonNode.js.map +1 -1
- package/dist/extensions/ButtonStripNode.d.ts +8 -0
- package/dist/extensions/ButtonStripNode.d.ts.map +1 -0
- package/dist/extensions/ButtonStripNode.js +51 -0
- package/dist/extensions/ButtonStripNode.js.map +1 -0
- package/dist/extensions/ColumnNode.d.ts +11 -0
- package/dist/extensions/ColumnNode.d.ts.map +1 -0
- package/dist/extensions/ColumnNode.js +46 -0
- package/dist/extensions/ColumnNode.js.map +1 -0
- package/dist/extensions/ColumnsNode.d.ts +7 -0
- package/dist/extensions/ColumnsNode.d.ts.map +1 -0
- package/dist/extensions/ColumnsNode.js +56 -0
- package/dist/extensions/ColumnsNode.js.map +1 -0
- package/dist/extensions/SlideMarkerPlugin.d.ts +9 -0
- package/dist/extensions/SlideMarkerPlugin.d.ts.map +1 -0
- package/dist/extensions/SlideMarkerPlugin.js +78 -0
- package/dist/extensions/SlideMarkerPlugin.js.map +1 -0
- package/dist/extensions/SlidePropertiesNode.d.ts +8 -0
- package/dist/extensions/SlidePropertiesNode.d.ts.map +1 -0
- package/dist/extensions/SlidePropertiesNode.js +74 -0
- package/dist/extensions/SlidePropertiesNode.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -1
- package/dist/index.js.map +1 -1
- package/dist/styles/ButtonStripNodeView.css +134 -0
- package/dist/styles/ChartNodeView.css +9 -1
- package/dist/styles/ColumnsNodeView.css +89 -0
- package/dist/styles/PresentationToolbar.css +65 -0
- package/dist/styles/ReadonlyBlockRenderer.css +5 -5
- package/dist/styles/SlidePropertiesNodeView.css +116 -0
- package/dist/utils/PresentationConverter.d.ts +41 -0
- package/dist/utils/PresentationConverter.d.ts.map +1 -0
- package/dist/utils/PresentationConverter.js +1506 -0
- package/dist/utils/PresentationConverter.js.map +1 -0
- package/package.json +65 -57
- package/src/styles/ButtonStripNodeView.css +134 -0
- package/src/styles/ChartNodeView.css +9 -1
- package/src/styles/ColumnsNodeView.css +89 -0
- package/src/styles/PresentationToolbar.css +65 -0
- package/src/styles/ReadonlyBlockRenderer.css +5 -5
- package/src/styles/SlidePropertiesNodeView.css +116 -0
|
@@ -1,247 +1,548 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.ChartNodeView = ChartNodeView;
|
|
4
7
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
8
|
const react_1 = require("react");
|
|
9
|
+
const react_dom_1 = __importDefault(require("react-dom"));
|
|
6
10
|
const react_2 = require("@tiptap/react");
|
|
7
|
-
const
|
|
11
|
+
const bar_1 = require("@nivo/bar");
|
|
12
|
+
const line_1 = require("@nivo/line");
|
|
13
|
+
const pie_1 = require("@nivo/pie");
|
|
14
|
+
const radar_1 = require("@nivo/radar");
|
|
15
|
+
const heatmap_1 = require("@nivo/heatmap");
|
|
16
|
+
const treemap_1 = require("@nivo/treemap");
|
|
17
|
+
const scatterplot_1 = require("@nivo/scatterplot");
|
|
18
|
+
const sankey_1 = require("@nivo/sankey");
|
|
8
19
|
const lucide_react_1 = require("lucide-react");
|
|
9
20
|
require("../styles/ChartNodeView.css");
|
|
10
|
-
|
|
21
|
+
// ─── Portal tooltip: bypasses all overflow:hidden ancestors ────────
|
|
22
|
+
// The webview's html > body > #root all have overflow:hidden, clipping
|
|
23
|
+
// Nivo's absolutely-positioned tooltip. This system:
|
|
24
|
+
// 1. Tracks mouse position via a module-level ref (no re-renders)
|
|
25
|
+
// 2. Renders tooltip via createPortal at document.body level
|
|
26
|
+
const mouseRef = { x: 0, y: 0 };
|
|
27
|
+
if (typeof window !== 'undefined') {
|
|
28
|
+
window.addEventListener('mousemove', (e) => {
|
|
29
|
+
mouseRef.x = e.clientX;
|
|
30
|
+
mouseRef.y = e.clientY;
|
|
31
|
+
}, { passive: true });
|
|
32
|
+
}
|
|
33
|
+
function PortalTooltip({ children, bg, color, border }) {
|
|
34
|
+
return react_dom_1.default.createPortal((0, jsx_runtime_1.jsx)("div", { style: {
|
|
35
|
+
position: 'fixed',
|
|
36
|
+
top: mouseRef.y + 14,
|
|
37
|
+
left: mouseRef.x + 14,
|
|
38
|
+
pointerEvents: 'none',
|
|
39
|
+
zIndex: 99999,
|
|
40
|
+
background: bg,
|
|
41
|
+
color: color,
|
|
42
|
+
fontSize: 12,
|
|
43
|
+
borderRadius: 6,
|
|
44
|
+
padding: '8px 12px',
|
|
45
|
+
boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
|
|
46
|
+
border: `1px solid ${border}`,
|
|
47
|
+
maxWidth: 300,
|
|
48
|
+
whiteSpace: 'nowrap',
|
|
49
|
+
}, children: children }), document.body);
|
|
50
|
+
}
|
|
51
|
+
// Helper to get CSS variable with robust fallback
|
|
11
52
|
function getCssVar(name, fallback) {
|
|
53
|
+
if (typeof window === 'undefined')
|
|
54
|
+
return fallback;
|
|
12
55
|
try {
|
|
13
|
-
|
|
14
|
-
|
|
56
|
+
// Check both body and documentElement
|
|
57
|
+
const bodyVal = getComputedStyle(document.body).getPropertyValue(name).trim();
|
|
58
|
+
if (bodyVal)
|
|
59
|
+
return bodyVal;
|
|
60
|
+
const docVal = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
|
61
|
+
if (docVal)
|
|
62
|
+
return docVal;
|
|
63
|
+
return fallback;
|
|
15
64
|
}
|
|
16
65
|
catch {
|
|
17
66
|
return fallback;
|
|
18
67
|
}
|
|
19
68
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
69
|
+
// Custom Teal-oriented palette
|
|
70
|
+
const TEAL_PALETTE = [
|
|
71
|
+
'#2dd4bf', // Teal 400
|
|
72
|
+
'#0d9488', // Teal 600
|
|
73
|
+
'#99f6e4', // Teal 200
|
|
74
|
+
'#115e59', // Teal 800
|
|
75
|
+
'#5eead4', // Teal 300
|
|
76
|
+
'#134e4a', // Teal 900
|
|
77
|
+
'#ccfbf1', // Teal 100
|
|
78
|
+
];
|
|
79
|
+
// ------ Data Transformation Adapters ------
|
|
80
|
+
// 1. Chart.js -> Nivo Bar
|
|
81
|
+
// ChartJS: { labels: ['A', 'B'], datasets: [{label: 'S1', data: [10, 20]}, {label: 'S2', data: [5, 15]}] }
|
|
82
|
+
// Nivo Bar: [ { id: 'A', S1: 10, S2: 5 }, { id: 'B', S1: 20, S2: 15 } ]
|
|
83
|
+
function transformToBar(data) {
|
|
84
|
+
const labels = data?.labels || [];
|
|
85
|
+
const datasets = data?.datasets || [];
|
|
86
|
+
if (!labels.length)
|
|
87
|
+
return { data: [], keys: [] };
|
|
88
|
+
const result = [];
|
|
89
|
+
const keys = [];
|
|
90
|
+
// Collect keys
|
|
91
|
+
datasets.forEach((ds) => {
|
|
92
|
+
if (ds.label)
|
|
93
|
+
keys.push(ds.label);
|
|
94
|
+
});
|
|
95
|
+
// Build data points
|
|
96
|
+
labels.forEach((label, i) => {
|
|
97
|
+
const item = { id: label };
|
|
98
|
+
datasets.forEach((ds) => {
|
|
99
|
+
if (ds.label)
|
|
100
|
+
item[ds.label] = ds.data[i] || 0;
|
|
101
|
+
});
|
|
102
|
+
result.push(item);
|
|
103
|
+
});
|
|
104
|
+
return { data: result, keys };
|
|
28
105
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const step = max <= 10 ? 2 : 10;
|
|
47
|
-
return Math.ceil(max / step) * step;
|
|
48
|
-
}
|
|
49
|
-
catch {
|
|
50
|
-
return undefined;
|
|
51
|
-
}
|
|
106
|
+
// 2. Chart.js -> Nivo Line
|
|
107
|
+
// Nivo Line: [ { id: 'S1', data: [{x: 'A', y: 10}, {x: 'B', y: 20}] } ]
|
|
108
|
+
function transformToLine(data) {
|
|
109
|
+
const labels = data?.labels || [];
|
|
110
|
+
const datasets = data?.datasets || [];
|
|
111
|
+
const result = [];
|
|
112
|
+
datasets.forEach((ds) => {
|
|
113
|
+
const points = labels.map((label, i) => ({
|
|
114
|
+
x: label,
|
|
115
|
+
y: ds.data[i] || 0
|
|
116
|
+
}));
|
|
117
|
+
result.push({
|
|
118
|
+
id: ds.label || 'Series',
|
|
119
|
+
data: points
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
return result;
|
|
52
123
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
124
|
+
// 3. Chart.js -> Nivo Pie
|
|
125
|
+
// Nivo Pie: [ { id: 'A', label: 'A', value: 10 }, { id: 'B', label: 'B', value: 20 } ]
|
|
126
|
+
// We only support the FIRST dataset for Pie charts (standard practice)
|
|
127
|
+
function transformToPie(data) {
|
|
128
|
+
const labels = data?.labels || [];
|
|
129
|
+
const ds = data?.datasets?.[0];
|
|
130
|
+
if (!ds)
|
|
131
|
+
return [];
|
|
132
|
+
return labels.map((label, i) => ({
|
|
133
|
+
id: label,
|
|
134
|
+
label,
|
|
135
|
+
value: ds.data[i] || 0
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
// 4. Chart.js -> Nivo Radar
|
|
139
|
+
// Nivo Radar expects same format as Bar basically (indexBy keys)
|
|
140
|
+
// [ { id: 'Metric A', Series1: 100, Series2: 50 } ]
|
|
141
|
+
function transformToRadar(data) {
|
|
142
|
+
const labels = data?.labels || [];
|
|
143
|
+
const datasets = data?.datasets || [];
|
|
144
|
+
const result = [];
|
|
145
|
+
const keys = [];
|
|
146
|
+
datasets.forEach((ds) => {
|
|
147
|
+
if (ds.label)
|
|
148
|
+
keys.push(ds.label);
|
|
149
|
+
});
|
|
150
|
+
labels.forEach((label, i) => {
|
|
151
|
+
const item = { id: label };
|
|
152
|
+
datasets.forEach((ds) => {
|
|
153
|
+
if (ds.label)
|
|
154
|
+
item[ds.label] = ds.data[i] || 0;
|
|
155
|
+
});
|
|
156
|
+
result.push(item);
|
|
157
|
+
});
|
|
158
|
+
return { data: result, keys };
|
|
159
|
+
}
|
|
160
|
+
// 5. Chart.js -> Nivo Heatmap
|
|
161
|
+
// { labels: ['Mon', 'Tue'], datasets: [{ label: 'Morning', data: [10, 20] }, { label: 'Evening', data: [30, 50] }] }
|
|
162
|
+
// -> [ { id: 'Morning', data: [{x: 'Mon', y: 10}, {x: 'Tue', y: 20}] }, ... ]
|
|
163
|
+
function transformToHeatmap(data) {
|
|
164
|
+
const labels = data?.labels || [];
|
|
165
|
+
const datasets = data?.datasets || [];
|
|
166
|
+
return datasets.map((ds) => ({
|
|
167
|
+
id: ds.label,
|
|
168
|
+
data: labels.map((label, i) => ({
|
|
169
|
+
x: label,
|
|
170
|
+
y: ds.data[i] || 0
|
|
171
|
+
}))
|
|
172
|
+
}));
|
|
173
|
+
}
|
|
174
|
+
// 6. Chart.js -> Nivo Treemap
|
|
175
|
+
// Takes standard "datasets" and makes a simple hierarchy.
|
|
176
|
+
// { labels: ['A', 'B'], datasets: [{ label: 'Size', data: [100, 200] }] }
|
|
177
|
+
// -> { name: 'root', children: [ { name: 'A', loc: 100 }, { name: 'B', loc: 200 } ] }
|
|
178
|
+
function transformToTreemap(data) {
|
|
179
|
+
const labels = data?.labels || [];
|
|
180
|
+
const ds = data?.datasets?.[0];
|
|
181
|
+
if (!ds)
|
|
182
|
+
return { name: 'root', children: [] };
|
|
183
|
+
const children = labels.map((label, i) => ({
|
|
184
|
+
name: label,
|
|
185
|
+
loc: ds.data[i] || 0
|
|
186
|
+
}));
|
|
187
|
+
return { name: 'root', children };
|
|
188
|
+
}
|
|
189
|
+
// 7. Chart.js -> Nivo ScatterPlot
|
|
190
|
+
// { datasets: [{ label: 'Group A', data: [{x: 10, y: 14}, {x: 20, y: 30}] }] }
|
|
191
|
+
// -> [ { id: 'Group A', data: [{x: 10, y: 14}, ...] } ]
|
|
192
|
+
function transformToScatter(data) {
|
|
193
|
+
const datasets = data?.datasets || [];
|
|
194
|
+
return datasets.map((ds) => ({
|
|
195
|
+
id: ds.label,
|
|
196
|
+
data: ds.data?.map((pt) => ({
|
|
197
|
+
x: pt.x || 0,
|
|
198
|
+
y: pt.y || 0
|
|
199
|
+
})) || []
|
|
200
|
+
}));
|
|
201
|
+
}
|
|
202
|
+
// 8. Custom JSON -> Nivo Sankey
|
|
203
|
+
// Sankey doesn't map well to Chart.js structure. We expect direct Nivo structure key 'sankey'.
|
|
204
|
+
// Or we try to adapt:
|
|
205
|
+
// { labels: ['A', 'B', 'C'], datasets: [{ data: [ { source: 'A', target: 'B', value: 10 } ] }] }
|
|
206
|
+
function transformToSankey(data) {
|
|
207
|
+
// If user provides native Sankey structure in a generic way
|
|
208
|
+
if (data.nodes && data.links)
|
|
209
|
+
return data;
|
|
210
|
+
// Otherwise try to adapt from datasets if possible
|
|
211
|
+
const ds = data?.datasets?.[0];
|
|
212
|
+
if (ds && Array.isArray(ds.data)) {
|
|
213
|
+
// Collect all unique nodes from links
|
|
214
|
+
const links = ds.data;
|
|
215
|
+
const nodeIds = new Set();
|
|
216
|
+
links.forEach((l) => {
|
|
217
|
+
nodeIds.add(l.source);
|
|
218
|
+
nodeIds.add(l.target);
|
|
219
|
+
});
|
|
220
|
+
// Check if we have explicit nodes, else exact simple ones
|
|
221
|
+
const nodes = Array.from(nodeIds).map(id => ({ id }));
|
|
222
|
+
return { nodes, links };
|
|
110
223
|
}
|
|
111
|
-
return {
|
|
112
|
-
colors: { primary, text, text2, border, bg },
|
|
113
|
-
data: { ...data, datasets }
|
|
114
|
-
};
|
|
224
|
+
return { nodes: [], links: [] };
|
|
115
225
|
}
|
|
116
226
|
function defaultChartJson(type) {
|
|
117
227
|
if (type === 'pie') {
|
|
118
228
|
return JSON.stringify({
|
|
119
|
-
labels: ['A', 'B', 'C'],
|
|
120
|
-
datasets: [{ label: '
|
|
229
|
+
labels: ['Task A', 'Task B', 'Task C'],
|
|
230
|
+
datasets: [{ label: 'Hours', data: [30, 45, 25] }]
|
|
121
231
|
}, null, 2);
|
|
122
232
|
}
|
|
123
233
|
if (type === 'radar') {
|
|
124
234
|
return JSON.stringify({
|
|
125
|
-
labels: ['Speed', '
|
|
126
|
-
datasets: [{ label: '
|
|
235
|
+
labels: ['Speed', 'Reliability', 'Cost', 'UX', 'Security'],
|
|
236
|
+
datasets: [{ label: 'Product A', data: [90, 80, 60, 85, 70] }]
|
|
237
|
+
}, null, 2);
|
|
238
|
+
}
|
|
239
|
+
if (type === 'heatmap') {
|
|
240
|
+
return JSON.stringify({
|
|
241
|
+
labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
|
|
242
|
+
datasets: [
|
|
243
|
+
{ label: 'Morning', data: [10, 20, 15, 25, 30] },
|
|
244
|
+
{ label: 'Afternoon', data: [35, 40, 30, 20, 45] },
|
|
245
|
+
{ label: 'Evening', data: [15, 25, 40, 50, 60] }
|
|
246
|
+
]
|
|
247
|
+
}, null, 2);
|
|
248
|
+
}
|
|
249
|
+
if (type === 'treemap') {
|
|
250
|
+
return JSON.stringify({
|
|
251
|
+
labels: ['React', 'Vue', 'Angular', 'Svelte', 'Solid'],
|
|
252
|
+
datasets: [{ label: 'Usage', data: [55, 30, 25, 15, 10] }]
|
|
253
|
+
}, null, 2);
|
|
254
|
+
}
|
|
255
|
+
if (type === 'scatterplot') {
|
|
256
|
+
return JSON.stringify({
|
|
257
|
+
datasets: [
|
|
258
|
+
{
|
|
259
|
+
label: 'Group A',
|
|
260
|
+
data: [{ x: 10, y: 14 }, { x: 20, y: 30 }, { x: 30, y: 45 }, { x: 40, y: 60 }]
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
label: 'Group B',
|
|
264
|
+
data: [{ x: 15, y: 24 }, { x: 25, y: 35 }, { x: 35, y: 20 }, { x: 45, y: 50 }]
|
|
265
|
+
}
|
|
266
|
+
]
|
|
267
|
+
}, null, 2);
|
|
268
|
+
}
|
|
269
|
+
if (type === 'sankey') {
|
|
270
|
+
return JSON.stringify({
|
|
271
|
+
datasets: [{
|
|
272
|
+
data: [
|
|
273
|
+
{ source: 'A', target: 'B', value: 50 },
|
|
274
|
+
{ source: 'A', target: 'C', value: 30 },
|
|
275
|
+
{ source: 'B', target: 'D', value: 40 },
|
|
276
|
+
{ source: 'C', target: 'D', value: 20 }
|
|
277
|
+
]
|
|
278
|
+
}]
|
|
127
279
|
}, null, 2);
|
|
128
280
|
}
|
|
129
|
-
// bar default
|
|
281
|
+
// bar/line default
|
|
130
282
|
return JSON.stringify({
|
|
131
|
-
labels: ['
|
|
132
|
-
datasets: [{ label: '
|
|
283
|
+
labels: ['Q1', 'Q2', 'Q3', 'Q4'],
|
|
284
|
+
datasets: [{ label: 'Revenue', data: [1200, 1900, 800, 1500] }]
|
|
133
285
|
}, null, 2);
|
|
134
286
|
}
|
|
287
|
+
// --- Component ---
|
|
135
288
|
function ChartNodeView({ node, updateAttributes, selected }) {
|
|
136
|
-
const canvasRef = (0, react_1.useRef)(null);
|
|
137
|
-
const chartRef = (0, react_1.useRef)(null);
|
|
138
|
-
// Determine if source should be shown. Default to attribute, but if it's a new empty/default node, show it.
|
|
139
|
-
// Read directly from node.attrs to make it reactive
|
|
140
289
|
const showSource = node.attrs.showSource || false;
|
|
141
|
-
(0, react_1.useEffect)(() => {
|
|
142
|
-
const currentJson = (node.attrs.json || '').toString();
|
|
143
|
-
const isNew = !currentJson || currentJson === defaultChartJson((node.attrs.chartType || 'bar').toString());
|
|
144
|
-
if (isNew && !showSource) {
|
|
145
|
-
updateAttributes({ showSource: true });
|
|
146
|
-
}
|
|
147
|
-
}, []);
|
|
148
|
-
const setShowSource = (val) => {
|
|
149
|
-
const current = node.attrs.showSource || false;
|
|
150
|
-
const next = typeof val === 'function' ? val(current) : val;
|
|
151
|
-
updateAttributes({ showSource: next });
|
|
152
|
-
};
|
|
153
|
-
const [error, setError] = (0, react_1.useState)(null);
|
|
154
290
|
const chartType = (node.attrs.chartType || 'bar').toString();
|
|
155
291
|
const json = (node.attrs.json || '').toString() || defaultChartJson(chartType);
|
|
156
|
-
const
|
|
292
|
+
const [error, setError] = (0, react_1.useState)(null);
|
|
293
|
+
// Tooltip style state for portal tooltips
|
|
294
|
+
const [tooltipStyle, setTooltipStyle] = (0, react_1.useState)({ bg: '#252526', color: '#ffffff', border: '#444444' });
|
|
295
|
+
// Track dark mode for Sankey link colors
|
|
296
|
+
const [isDark, setIsDark] = (0, react_1.useState)(true);
|
|
297
|
+
// Theme state
|
|
298
|
+
const [theme, setTheme] = (0, react_1.useState)({
|
|
299
|
+
axis: {
|
|
300
|
+
ticks: { text: { fill: '#888' } },
|
|
301
|
+
legend: { text: { fill: '#888' } }
|
|
302
|
+
},
|
|
303
|
+
labels: { text: { fill: '#888' } },
|
|
304
|
+
legends: { text: { fill: '#888' } }
|
|
305
|
+
});
|
|
306
|
+
// Robust theme extraction
|
|
307
|
+
const updateTheme = () => {
|
|
308
|
+
// Try to detect if we are in dark mode to choose a good default
|
|
309
|
+
const isDarkMode = document.body.classList.contains('vscode-dark') ||
|
|
310
|
+
document.body.getAttribute('data-vscode-theme-kind') === 'vscode-dark';
|
|
311
|
+
setIsDark(isDarkMode);
|
|
312
|
+
// Core colors
|
|
313
|
+
const textPrimary = getCssVar('--jotx-text', isDarkMode ? '#e0e0e0' : '#333333');
|
|
314
|
+
const textSecondary = getCssVar('--vscode-descriptionForeground', isDarkMode ? '#aaaaaa' : '#666666');
|
|
315
|
+
const gridColor = getCssVar('--jotx-border', isDarkMode ? '#444444' : '#e0e0e0');
|
|
316
|
+
const tooltipBg = getCssVar('--jotx-background', isDarkMode ? '#252526' : '#ffffff');
|
|
317
|
+
const tooltipColor = getCssVar('--jotx-text', isDarkMode ? '#ffffff' : '#000000');
|
|
318
|
+
// Store tooltip style for portal tooltips
|
|
319
|
+
setTooltipStyle({ bg: tooltipBg, color: tooltipColor, border: gridColor });
|
|
320
|
+
setTheme({
|
|
321
|
+
background: 'transparent',
|
|
322
|
+
textColor: textPrimary,
|
|
323
|
+
axis: {
|
|
324
|
+
domain: {
|
|
325
|
+
line: {
|
|
326
|
+
stroke: gridColor,
|
|
327
|
+
strokeWidth: 1
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
ticks: {
|
|
331
|
+
line: {
|
|
332
|
+
stroke: gridColor,
|
|
333
|
+
strokeWidth: 1
|
|
334
|
+
},
|
|
335
|
+
text: {
|
|
336
|
+
fill: textSecondary, // Use slightly muted color for axis ticks
|
|
337
|
+
fontSize: 11
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
legend: {
|
|
341
|
+
text: {
|
|
342
|
+
fill: textPrimary, // Use primary color for titles
|
|
343
|
+
fontSize: 12,
|
|
344
|
+
fontWeight: 600
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
grid: {
|
|
349
|
+
line: {
|
|
350
|
+
stroke: gridColor,
|
|
351
|
+
strokeWidth: 1,
|
|
352
|
+
strokeDasharray: '4 4'
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
labels: {
|
|
356
|
+
text: {
|
|
357
|
+
fill: textPrimary,
|
|
358
|
+
fontSize: 12,
|
|
359
|
+
fontWeight: 600
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
tooltip: {
|
|
363
|
+
container: {
|
|
364
|
+
background: tooltipBg,
|
|
365
|
+
color: tooltipColor,
|
|
366
|
+
fontSize: 12,
|
|
367
|
+
borderRadius: 4,
|
|
368
|
+
boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.25)',
|
|
369
|
+
border: `1px solid ${gridColor}`,
|
|
370
|
+
zIndex: 1000 // Ensure tooltip is above everything
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
legends: {
|
|
374
|
+
text: {
|
|
375
|
+
fill: textSecondary,
|
|
376
|
+
fontSize: 12
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
};
|
|
381
|
+
// Initial and dynamic theme update
|
|
157
382
|
(0, react_1.useEffect)(() => {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
383
|
+
updateTheme();
|
|
384
|
+
// Observe body for class changes (theme switching in VS Code)
|
|
385
|
+
const observer = new MutationObserver((mutations) => {
|
|
386
|
+
updateTheme();
|
|
387
|
+
});
|
|
388
|
+
observer.observe(document.body, {
|
|
389
|
+
attributes: true,
|
|
390
|
+
attributeFilter: ['class', 'data-vscode-theme-kind']
|
|
391
|
+
});
|
|
392
|
+
return () => observer.disconnect();
|
|
163
393
|
}, []);
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if (!el)
|
|
167
|
-
return;
|
|
168
|
-
const ctx = el.getContext('2d');
|
|
169
|
-
if (!ctx)
|
|
170
|
-
return;
|
|
394
|
+
// Parse JSON
|
|
395
|
+
const chartData = (0, react_1.useMemo)(() => {
|
|
171
396
|
try {
|
|
172
|
-
const
|
|
173
|
-
const themed = applyDefaultStyles(chartType, parsedRaw);
|
|
174
|
-
const parsed = themed.data;
|
|
175
|
-
const { text, text2, border } = themed.colors;
|
|
176
|
-
const suggestedMax = chartType === 'radar' ? computeSuggestedMax(parsed) : undefined;
|
|
397
|
+
const parsed = JSON.parse(json);
|
|
177
398
|
setError(null);
|
|
178
|
-
|
|
179
|
-
chartRef.current = new chart_js_1.Chart(ctx, {
|
|
180
|
-
type: chartType,
|
|
181
|
-
data: parsed,
|
|
182
|
-
options: {
|
|
183
|
-
responsive: true,
|
|
184
|
-
maintainAspectRatio: false,
|
|
185
|
-
animation: { duration: 180 },
|
|
186
|
-
layout: { padding: chartType === 'radar' ? 16 : 10 },
|
|
187
|
-
plugins: {
|
|
188
|
-
legend: {
|
|
189
|
-
display: true,
|
|
190
|
-
labels: {
|
|
191
|
-
color: text2,
|
|
192
|
-
boxWidth: 14,
|
|
193
|
-
boxHeight: 8,
|
|
194
|
-
// Pie legend should be solid color swatches (no borders). Point-style looks odd here.
|
|
195
|
-
usePointStyle: chartType === 'pie' ? false : true
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
},
|
|
199
|
-
...(chartType === 'radar'
|
|
200
|
-
? {
|
|
201
|
-
scales: {
|
|
202
|
-
r: {
|
|
203
|
-
beginAtZero: true,
|
|
204
|
-
suggestedMax,
|
|
205
|
-
grid: {
|
|
206
|
-
color: hexToRgba(border, 0.45),
|
|
207
|
-
circular: true
|
|
208
|
-
},
|
|
209
|
-
angleLines: {
|
|
210
|
-
color: hexToRgba(border, 0.55)
|
|
211
|
-
},
|
|
212
|
-
pointLabels: {
|
|
213
|
-
color: text,
|
|
214
|
-
font: { size: 12, weight: '600' },
|
|
215
|
-
padding: 6
|
|
216
|
-
},
|
|
217
|
-
ticks: {
|
|
218
|
-
display: false
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
},
|
|
222
|
-
elements: {
|
|
223
|
-
line: { tension: 0.25 }
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
: {})
|
|
227
|
-
}
|
|
228
|
-
});
|
|
399
|
+
return parsed;
|
|
229
400
|
}
|
|
230
401
|
catch (e) {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
setError(e?.message || 'Invalid chart JSON');
|
|
402
|
+
setError(e.message);
|
|
403
|
+
return null;
|
|
234
404
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
405
|
+
}, [json]);
|
|
406
|
+
// Render correct chart type (Memoized to prevent flickering/lag on hover)
|
|
407
|
+
// Note: Nivo charts are heavy/complex, avoiding re-renders helps responsiveness.
|
|
408
|
+
const chartComponent = (0, react_1.useMemo)(() => {
|
|
409
|
+
if (!chartData)
|
|
410
|
+
return null;
|
|
411
|
+
switch (chartType) {
|
|
412
|
+
case 'bar':
|
|
413
|
+
const barData = transformToBar(chartData);
|
|
414
|
+
return ((0, jsx_runtime_1.jsx)(bar_1.ResponsiveBar, { data: barData.data, keys: barData.keys, indexBy: "id", margin: { top: 20, right: 20, bottom: 50, left: 60 }, padding: 0.3, valueScale: { type: 'linear' }, colors: TEAL_PALETTE, theme: theme, animate: true, labelSkipWidth: 12, labelSkipHeight: 12, labelTextColor: { from: 'color', modifiers: [['darker', 2]] }, tooltip: ({ id, value, color }) => ((0, jsx_runtime_1.jsxs)(PortalTooltip, { bg: tooltipStyle.bg, color: tooltipStyle.color, border: tooltipStyle.border, children: [(0, jsx_runtime_1.jsx)("span", { style: { color }, children: "\u25CF" }), " ", id, ": ", (0, jsx_runtime_1.jsx)("strong", { children: value })] })), axisBottom: {
|
|
415
|
+
tickSize: 5,
|
|
416
|
+
tickPadding: 5,
|
|
417
|
+
tickRotation: 0,
|
|
418
|
+
legend: 'Category',
|
|
419
|
+
legendPosition: 'middle',
|
|
420
|
+
legendOffset: 36
|
|
421
|
+
}, axisLeft: {
|
|
422
|
+
tickSize: 5,
|
|
423
|
+
tickPadding: 5,
|
|
424
|
+
tickRotation: 0,
|
|
425
|
+
legend: 'Value',
|
|
426
|
+
legendPosition: 'middle',
|
|
427
|
+
legendOffset: -45
|
|
428
|
+
} }));
|
|
429
|
+
case 'line':
|
|
430
|
+
const lineData = transformToLine(chartData);
|
|
431
|
+
return ((0, jsx_runtime_1.jsx)(line_1.ResponsiveLine, { data: lineData, margin: { top: 20, right: 30, bottom: 50, left: 60 }, xScale: { type: 'point' }, yScale: { type: 'linear', min: 'auto', max: 'auto', stacked: false, reverse: false }, axisTop: null, axisRight: null, axisBottom: {
|
|
432
|
+
tickSize: 5,
|
|
433
|
+
tickPadding: 5,
|
|
434
|
+
tickRotation: 0,
|
|
435
|
+
legend: 'Time',
|
|
436
|
+
legendOffset: 36,
|
|
437
|
+
legendPosition: 'middle'
|
|
438
|
+
}, axisLeft: {
|
|
439
|
+
tickSize: 5,
|
|
440
|
+
tickPadding: 5,
|
|
441
|
+
tickRotation: 0,
|
|
442
|
+
legend: 'Value',
|
|
443
|
+
legendOffset: -45,
|
|
444
|
+
legendPosition: 'middle'
|
|
445
|
+
}, pointSize: 10, pointColor: { theme: 'background' }, pointBorderWidth: 2, pointBorderColor: { from: 'serieColor' }, pointLabelYOffset: -12, useMesh: true, theme: theme, colors: TEAL_PALETTE, tooltip: ({ point }) => ((0, jsx_runtime_1.jsxs)(PortalTooltip, { bg: tooltipStyle.bg, color: tooltipStyle.color, border: tooltipStyle.border, children: [(0, jsx_runtime_1.jsx)("span", { style: { color: point.serieColor }, children: "\u25CF" }), " ", point.serieId, ": ", (0, jsx_runtime_1.jsx)("strong", { children: point.data.yFormatted })] })) }));
|
|
446
|
+
case 'pie':
|
|
447
|
+
case 'doughnut': // Adapt doughnut as Pie with innerRadius
|
|
448
|
+
const pieData = transformToPie(chartData);
|
|
449
|
+
return ((0, jsx_runtime_1.jsx)(pie_1.ResponsivePie, { data: pieData, margin: { top: 40, right: 80, bottom: 80, left: 80 }, innerRadius: chartType === 'doughnut' ? 0.5 : 0, padAngle: 0.7, cornerRadius: 3, activeOuterRadiusOffset: 8, borderWidth: 1, borderColor: { from: 'color', modifiers: [['darker', 0.2]] }, arcLinkLabelsSkipAngle: 10, arcLinkLabelsTextColor: { from: 'color', modifiers: [] }, arcLinkLabelsThickness: 2, arcLinkLabelsColor: { from: 'color' }, arcLabelsSkipAngle: 10, arcLabelsTextColor: { from: 'color', modifiers: [['darker', 2]] }, theme: theme, colors: TEAL_PALETTE, tooltip: ({ datum }) => ((0, jsx_runtime_1.jsxs)(PortalTooltip, { bg: tooltipStyle.bg, color: tooltipStyle.color, border: tooltipStyle.border, children: [(0, jsx_runtime_1.jsx)("span", { style: { color: datum.color }, children: "\u25CF" }), " ", datum.id, ": ", (0, jsx_runtime_1.jsx)("strong", { children: datum.value })] })) }));
|
|
450
|
+
case 'radar':
|
|
451
|
+
const radarData = transformToRadar(chartData);
|
|
452
|
+
return ((0, jsx_runtime_1.jsx)(radar_1.ResponsiveRadar, { data: radarData.data, keys: radarData.keys, indexBy: "id", margin: { top: 70, right: 80, bottom: 40, left: 80 }, borderColor: { from: 'color' }, gridLabelOffset: 36, dotSize: 10, dotColor: { theme: 'background' }, dotBorderWidth: 2, colors: TEAL_PALETTE, blendMode: "normal", motionConfig: "wobbly", theme: theme, sliceTooltip: ({ index, data }) => ((0, jsx_runtime_1.jsxs)(PortalTooltip, { bg: tooltipStyle.bg, color: tooltipStyle.color, border: tooltipStyle.border, children: [(0, jsx_runtime_1.jsx)("strong", { children: index }), data.map((d) => ((0, jsx_runtime_1.jsxs)("div", { style: { display: 'flex', gap: 6, alignItems: 'center' }, children: [(0, jsx_runtime_1.jsx)("span", { style: { color: d.color }, children: "\u25CF" }), " ", d.id, ": ", d.value] }, d.id)))] })) }));
|
|
453
|
+
case 'heatmap':
|
|
454
|
+
const heatData = transformToHeatmap(chartData);
|
|
455
|
+
return ((0, jsx_runtime_1.jsx)(heatmap_1.ResponsiveHeatMap, { data: heatData, margin: { top: 60, right: 90, bottom: 60, left: 90 }, valueFormat: ">-.2s", axisTop: {
|
|
456
|
+
tickSize: 5,
|
|
457
|
+
tickPadding: 5,
|
|
458
|
+
tickRotation: -90,
|
|
459
|
+
legend: '',
|
|
460
|
+
legendOffset: 46
|
|
461
|
+
}, axisRight: {
|
|
462
|
+
tickSize: 5,
|
|
463
|
+
tickPadding: 5,
|
|
464
|
+
tickRotation: 0,
|
|
465
|
+
legend: '',
|
|
466
|
+
legendPosition: 'middle',
|
|
467
|
+
legendOffset: 70
|
|
468
|
+
}, axisLeft: {
|
|
469
|
+
tickSize: 5,
|
|
470
|
+
tickPadding: 5,
|
|
471
|
+
tickRotation: 0,
|
|
472
|
+
legend: '',
|
|
473
|
+
legendPosition: 'middle',
|
|
474
|
+
legendOffset: -72
|
|
475
|
+
}, colors: {
|
|
476
|
+
type: 'sequential',
|
|
477
|
+
scheme: 'blues' // Matches our teal theme roughly
|
|
478
|
+
}, emptyColor: "#555555", theme: theme, tooltip: ({ cell }) => ((0, jsx_runtime_1.jsxs)(PortalTooltip, { bg: tooltipStyle.bg, color: tooltipStyle.color, border: tooltipStyle.border, children: [cell.serieId, " / ", cell.data.x, ": ", (0, jsx_runtime_1.jsx)("strong", { children: cell.data.y ?? cell.value })] })) }));
|
|
479
|
+
case 'treemap':
|
|
480
|
+
const treeData = transformToTreemap(chartData);
|
|
481
|
+
return ((0, jsx_runtime_1.jsx)(treemap_1.ResponsiveTreeMap, { data: treeData, identity: "name", value: "loc", valueFormat: ".02s", margin: { top: 10, right: 10, bottom: 10, left: 10 }, labelSkipSize: 12, labelTextColor: { from: 'color', modifiers: [['darker', 2]] }, parentLabelPosition: "left", parentLabelTextColor: { from: 'color', modifiers: [['darker', 2]] }, borderColor: { from: 'color', modifiers: [['darker', 0.1]] }, colors: TEAL_PALETTE, theme: theme, tooltip: ({ node }) => ((0, jsx_runtime_1.jsxs)(PortalTooltip, { bg: tooltipStyle.bg, color: tooltipStyle.color, border: tooltipStyle.border, children: [(0, jsx_runtime_1.jsx)("span", { style: { color: node.color }, children: "\u25CF" }), " ", node.id, ": ", (0, jsx_runtime_1.jsx)("strong", { children: node.formattedValue })] })) }));
|
|
482
|
+
case 'scatterplot':
|
|
483
|
+
const scatterData = transformToScatter(chartData);
|
|
484
|
+
return ((0, jsx_runtime_1.jsx)(scatterplot_1.ResponsiveScatterPlot, { data: scatterData, margin: { top: 60, right: 140, bottom: 70, left: 90 }, xScale: { type: 'linear', min: 0, max: 'auto' }, xFormat: ">-.2f", yScale: { type: 'linear', min: 0, max: 'auto' }, yFormat: ">-.2f", blendMode: "normal", colors: TEAL_PALETTE, theme: theme, tooltip: ({ node }) => ((0, jsx_runtime_1.jsxs)(PortalTooltip, { bg: tooltipStyle.bg, color: tooltipStyle.color, border: tooltipStyle.border, children: [(0, jsx_runtime_1.jsx)("span", { style: { color: node.color }, children: "\u25CF" }), " ", node.serieId, ": (", node.formattedX, ", ", node.formattedY, ")"] })), axisTop: null, axisRight: null, axisBottom: {
|
|
485
|
+
tickSize: 5,
|
|
486
|
+
tickPadding: 5,
|
|
487
|
+
tickRotation: 0,
|
|
488
|
+
legend: 'X',
|
|
489
|
+
legendPosition: 'middle',
|
|
490
|
+
legendOffset: 46
|
|
491
|
+
}, axisLeft: {
|
|
492
|
+
tickSize: 5,
|
|
493
|
+
tickPadding: 5,
|
|
494
|
+
tickRotation: 0,
|
|
495
|
+
legend: 'Y',
|
|
496
|
+
legendPosition: 'middle',
|
|
497
|
+
legendOffset: -60
|
|
498
|
+
} }));
|
|
499
|
+
case 'sankey':
|
|
500
|
+
const sankeyData = transformToSankey(chartData);
|
|
501
|
+
// Inject link colors based on theme - startColor/endColor on each link
|
|
502
|
+
// Using SOLID colors (no rgba) + linkOpacity=1 so there's no double-transparency
|
|
503
|
+
const flowColor = isDark ? '#A8C4DC' : '#666666';
|
|
504
|
+
const themedSankeyData = {
|
|
505
|
+
...sankeyData,
|
|
506
|
+
links: sankeyData.links.map((l) => ({
|
|
507
|
+
...l,
|
|
508
|
+
startColor: l.startColor || flowColor,
|
|
509
|
+
endColor: l.endColor || flowColor,
|
|
510
|
+
}))
|
|
511
|
+
};
|
|
512
|
+
return ((0, jsx_runtime_1.jsx)(sankey_1.ResponsiveSankey, { data: themedSankeyData, margin: { top: 40, right: 160, bottom: 40, left: 50 }, align: "justify", colors: TEAL_PALETTE, nodeOpacity: 1, nodeHoverOthersOpacity: 0.35, nodeThickness: 18, nodeSpacing: 24, nodeBorderWidth: 0, nodeBorderColor: { from: 'color', modifiers: [['darker', 0.8]] }, linkOpacity: 0.5, linkHoverOthersOpacity: 0.1, linkContract: 3, linkBlendMode: "normal", enableLinkGradient: true, labelPosition: "outside", labelOrientation: "vertical", labelPadding: 16, labelTextColor: { from: 'color', modifiers: [['darker', 1]] }, nodeTooltip: ({ node }) => ((0, jsx_runtime_1.jsxs)(PortalTooltip, { bg: tooltipStyle.bg, color: tooltipStyle.color, border: tooltipStyle.border, children: [(0, jsx_runtime_1.jsx)("span", { style: { color: node.color }, children: "\u25CF" }), " ", node.label, ": ", (0, jsx_runtime_1.jsx)("strong", { children: node.value })] })), linkTooltip: ({ link }) => ((0, jsx_runtime_1.jsxs)(PortalTooltip, { bg: tooltipStyle.bg, color: tooltipStyle.color, border: tooltipStyle.border, children: [link.source.label, " \u2192 ", link.target.label, ": ", (0, jsx_runtime_1.jsx)("strong", { children: link.value })] })), theme: {
|
|
513
|
+
...theme,
|
|
514
|
+
labels: { text: { fill: theme.textColor } }
|
|
515
|
+
} }));
|
|
516
|
+
default:
|
|
517
|
+
return (0, jsx_runtime_1.jsxs)("div", { children: ["Unknown chart type: ", chartType] });
|
|
518
|
+
}
|
|
519
|
+
}, [chartData, chartType, theme, tooltipStyle, isDark]);
|
|
520
|
+
// Confirmation state for switching types
|
|
521
|
+
const [pendingType, setPendingType] = (0, react_1.useState)(null);
|
|
522
|
+
const handleTypeSwitch = (newType) => {
|
|
523
|
+
if (newType === chartType)
|
|
524
|
+
return; // Already this type
|
|
525
|
+
// Always show confirmation before switching
|
|
526
|
+
setPendingType(newType);
|
|
527
|
+
};
|
|
528
|
+
const confirmSwitch = () => {
|
|
529
|
+
if (pendingType) {
|
|
530
|
+
updateAttributes({ chartType: pendingType, json: defaultChartJson(pendingType) });
|
|
531
|
+
setPendingType(null);
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
const cancelSwitch = () => {
|
|
535
|
+
setPendingType(null);
|
|
536
|
+
};
|
|
537
|
+
return ((0, jsx_runtime_1.jsxs)(react_2.NodeViewWrapper, { className: "jotx-chart", "data-type": "chart", "data-chart-type": chartType, "data-selected": selected ? 'true' : 'false', children: [(0, jsx_runtime_1.jsxs)("div", { className: "jotx-chart-header", children: [(0, jsx_runtime_1.jsx)("div", { className: "jotx-chart-title", children: "Chart" }), (0, jsx_runtime_1.jsx)("input", { type: "text", className: "jotx-block-title-input", placeholder: "Title (shown in graph)...", value: (node.attrs.title || '').toString(), onChange: (e) => updateAttributes({ title: e.target.value }) }), (0, jsx_runtime_1.jsx)("div", { className: "jotx-chart-type", children: ['bar', 'line', 'pie', 'radar', 'heatmap', 'treemap', 'scatterplot', 'sankey'].map(type => ((0, jsx_runtime_1.jsxs)("button", { className: `jotx-chart-type-btn ${chartType === type ? 'active' : ''}`, onMouseDown: (e) => e.preventDefault(), onClick: () => handleTypeSwitch(type), title: type.charAt(0).toUpperCase() + type.slice(1), children: [type === 'bar' && (0, jsx_runtime_1.jsx)(lucide_react_1.BarChart3, { size: 16 }), type === 'line' && (0, jsx_runtime_1.jsx)(lucide_react_1.Activity, { size: 16 }), type === 'pie' && (0, jsx_runtime_1.jsx)(lucide_react_1.PieChart, { size: 16 }), type === 'radar' && (0, jsx_runtime_1.jsx)(lucide_react_1.Radar, { size: 16 }), type === 'heatmap' && (0, jsx_runtime_1.jsx)(lucide_react_1.Grid, { size: 16 }), type === 'treemap' && (0, jsx_runtime_1.jsx)(lucide_react_1.LayoutDashboard, { size: 16 }), type === 'scatterplot' && (0, jsx_runtime_1.jsx)(lucide_react_1.ScatterChart, { size: 16 }), type === 'sankey' && (0, jsx_runtime_1.jsx)(lucide_react_1.GitMerge, { size: 16 })] }, type))) }), (0, jsx_runtime_1.jsx)("button", { className: "jotx-chart-settings", onMouseDown: (e) => e.preventDefault(), onClick: () => {
|
|
538
|
+
const next = !showSource;
|
|
539
|
+
updateAttributes({ showSource: next });
|
|
540
|
+
}, title: showSource ? "Hide Source" : "Show Source", children: showSource ? (0, jsx_runtime_1.jsx)(lucide_react_1.EyeOff, { size: 16 }) : (0, jsx_runtime_1.jsx)(lucide_react_1.Eye, { size: 16 }) })] }), pendingType && ((0, jsx_runtime_1.jsxs)("div", { className: "jotx-chart-confirm-overlay", style: {
|
|
541
|
+
position: 'absolute', top: 0, left: 0, right: 0, bottom: 0,
|
|
542
|
+
background: 'rgba(0,0,0,0.8)', zIndex: 50,
|
|
543
|
+
display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
|
|
544
|
+
color: 'white', padding: '20px', textAlign: 'center',
|
|
545
|
+
borderRadius: '8px'
|
|
546
|
+
}, children: [(0, jsx_runtime_1.jsxs)("div", { style: { marginBottom: '16px', fontWeight: 600 }, children: ["Switching to ", pendingType, " will reset your data."] }), (0, jsx_runtime_1.jsxs)("div", { style: { display: 'flex', gap: '10px' }, children: [(0, jsx_runtime_1.jsx)("button", { onClick: confirmSwitch, style: { padding: '6px 12px', background: '#d93025', color: 'white', border: 'none', borderRadius: '4px', cursor: 'pointer' }, children: "Reset & Switch" }), (0, jsx_runtime_1.jsx)("button", { onClick: cancelSwitch, style: { padding: '6px 12px', background: '#555', color: 'white', border: 'none', borderRadius: '4px', cursor: 'pointer' }, children: "Cancel" })] })] })), error ? (0, jsx_runtime_1.jsx)("div", { className: "jotx-chart-error", children: error }) : null, (0, jsx_runtime_1.jsx)("div", { className: "jotx-chart-canvasWrap", style: { height: '320px', width: '100%', position: 'relative', zIndex: 1, overflow: 'visible' }, children: chartComponent }), showSource ? ((0, jsx_runtime_1.jsxs)("div", { className: "jotx-chart-editor", children: [(0, jsx_runtime_1.jsx)("div", { className: "jotx-chart-editorHint", children: "Edit JSON (labels + datasets)" }), (0, jsx_runtime_1.jsx)("textarea", { value: json, onChange: (e) => updateAttributes({ json: e.target.value }), spellCheck: false })] })) : null] }));
|
|
246
547
|
}
|
|
247
548
|
//# sourceMappingURL=ChartNodeView.js.map
|