@pixerate/editor-react 0.35.0 → 0.36.0
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/chart/index.cjs +263 -28
- package/dist/chart/index.cjs.map +1 -1
- package/dist/chart/index.d.cts +87 -6
- package/dist/chart/index.d.ts +87 -6
- package/dist/chart/index.js +308 -60
- package/dist/chart/index.js.map +1 -1
- package/package.json +7 -7
package/dist/chart/index.cjs
CHANGED
|
@@ -25,6 +25,10 @@ __export(chart_exports, {
|
|
|
25
25
|
ChartDataGrid: () => ChartDataGrid,
|
|
26
26
|
ChartEditor: () => ChartEditor,
|
|
27
27
|
ChartInspector: () => ChartInspector,
|
|
28
|
+
ChartPresenceLayer: () => ChartPresenceLayer,
|
|
29
|
+
ChartSceneSvg: () => ChartSceneSvg,
|
|
30
|
+
ChartSparkline: () => ChartSparkline,
|
|
31
|
+
ChartStreamingView: () => ChartStreamingView,
|
|
28
32
|
chartButtonClass: () => chartButtonClass,
|
|
29
33
|
chartCellClass: () => chartCellClass,
|
|
30
34
|
chartIconButtonClass: () => chartIconButtonClass,
|
|
@@ -32,10 +36,13 @@ __export(chart_exports, {
|
|
|
32
36
|
chartLabelClass: () => chartLabelClass,
|
|
33
37
|
chartSectionClass: () => chartSectionClass,
|
|
34
38
|
chartSegmentClass: () => chartSegmentClass,
|
|
39
|
+
renderChartMark: () => renderChartMark,
|
|
35
40
|
useChartCommands: () => useChartCommands,
|
|
36
41
|
useChartController: () => useChartController,
|
|
42
|
+
useChartDirector: () => useChartDirector,
|
|
37
43
|
useChartEditor: () => useChartEditor,
|
|
38
|
-
useChartState: () => useChartState
|
|
44
|
+
useChartState: () => useChartState,
|
|
45
|
+
useChartToolProvider: () => useChartToolProvider
|
|
39
46
|
});
|
|
40
47
|
module.exports = __toCommonJS(chart_exports);
|
|
41
48
|
|
|
@@ -79,6 +86,26 @@ function useChartCommands(registry, controller, options = {}) {
|
|
|
79
86
|
return registry.register((0, import_chart.createChartCommands)(controller, { prefix, group, scope }));
|
|
80
87
|
}, [registry, controller, prefix, group, scope]);
|
|
81
88
|
}
|
|
89
|
+
function useChartToolProvider(registry, controller, options = {}) {
|
|
90
|
+
const latest = (0, import_react.useRef)(options);
|
|
91
|
+
latest.current = options;
|
|
92
|
+
const { id, namespace } = options;
|
|
93
|
+
(0, import_react.useEffect)(() => {
|
|
94
|
+
if (!registry) return;
|
|
95
|
+
return registry.register((0, import_chart.createChartToolProvider)(controller, { ...latest.current, id, namespace }));
|
|
96
|
+
}, [registry, controller, id, namespace]);
|
|
97
|
+
}
|
|
98
|
+
function useChartDirector(controller, options = {}) {
|
|
99
|
+
const sceneRef = (0, import_react.useRef)(null);
|
|
100
|
+
const ref = (0, import_react.useRef)(null);
|
|
101
|
+
if (!ref.current) {
|
|
102
|
+
const presence = new import_chart.ChartPresence();
|
|
103
|
+
const size = options.size ?? { width: 640, height: 400 };
|
|
104
|
+
const director = new import_chart.ChartDirector(controller, presence, { scene: () => sceneRef.current ?? (0, import_chart.layoutChart)(controller.getSpec(), size), speed: options.speed });
|
|
105
|
+
ref.current = { presence, director };
|
|
106
|
+
}
|
|
107
|
+
return { ...ref.current, sceneRef };
|
|
108
|
+
}
|
|
82
109
|
|
|
83
110
|
// src/chart/styles.ts
|
|
84
111
|
var chartInputClass = "h-7 w-full min-w-0 rounded-md border border-input bg-background px-2 text-xs text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary disabled:cursor-not-allowed disabled:opacity-50";
|
|
@@ -93,13 +120,13 @@ var chartCellClass = "h-7 w-full min-w-[4.5rem] bg-transparent px-2 text-right t
|
|
|
93
120
|
var import_react2 = require("react");
|
|
94
121
|
var import_chart2 = require("@pixerate/editor/chart");
|
|
95
122
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
96
|
-
function
|
|
123
|
+
function renderChartMark(mark, fontFamily, interactive) {
|
|
97
124
|
const common = {
|
|
98
125
|
"data-role": mark.role,
|
|
99
126
|
"data-chart-key": mark.target ? mark.key : void 0,
|
|
100
127
|
"data-selected": mark.selected ? "" : void 0,
|
|
101
128
|
opacity: mark.opacity,
|
|
102
|
-
pointerEvents: mark.role === "selection" || !mark.target && mark.role !== "background" ? "none" : void 0,
|
|
129
|
+
pointerEvents: mark.role === "selection" || mark.role === "drag-readout" || !mark.target && mark.role !== "background" ? "none" : void 0,
|
|
103
130
|
cursor: interactive && mark.target && mark.role !== "background" ? "pointer" : void 0
|
|
104
131
|
};
|
|
105
132
|
switch (mark.kind) {
|
|
@@ -139,12 +166,26 @@ function renderMark(mark, fontFamily, interactive) {
|
|
|
139
166
|
);
|
|
140
167
|
}
|
|
141
168
|
}
|
|
142
|
-
function
|
|
169
|
+
function ChartPresenceLayer({ presence }) {
|
|
170
|
+
const { actors } = (0, import_react2.useSyncExternalStore)(presence.subscribe, presence.getState, presence.getState);
|
|
171
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("g", { "data-chart-presence": "", pointerEvents: "none", children: actors.map((a) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { transform: `translate(${a.x} ${a.y})`, "data-actor": a.id, children: [
|
|
172
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M0,0 L0,14 L4,10.5 L7,17 L9.5,16 L6.5,9.5 L11.5,9.5 Z", style: { fill: a.color, stroke: "#ffffff", strokeWidth: 1 }, transform: a.pressed ? "scale(0.9)" : void 0 }),
|
|
173
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { transform: "translate(13 16)", children: [
|
|
174
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: 0, y: 0, rx: 4, height: 18, width: Math.max(28, (a.message ?? a.name).length * 6.4 + 12), style: { fill: a.color } }),
|
|
175
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { x: 6, y: 9, dominantBaseline: "central", style: { fill: "#ffffff", fontSize: 11, fontWeight: 600, fontFamily: "inherit" }, children: a.message ?? a.name })
|
|
176
|
+
] })
|
|
177
|
+
] }, a.id)) });
|
|
178
|
+
}
|
|
179
|
+
function ChartSceneSvg({ scene, label, className }) {
|
|
180
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: scene.width, height: scene.height, viewBox: `0 0 ${scene.width} ${scene.height}`, role: "img", "aria-label": label, className: `block max-w-full ${className ?? ""}`, children: scene.marks.map((m) => renderChartMark(m, scene.theme.fontFamily, false)) });
|
|
181
|
+
}
|
|
182
|
+
function ChartCanvas({ controller, width = 640, height = 400, responsive = true, theme, editable = true, measureText, emptyMessage, className, presence, sceneRef }) {
|
|
143
183
|
const state = useChartState(controller);
|
|
144
184
|
const wrapper = (0, import_react2.useRef)(null);
|
|
145
185
|
const svg = (0, import_react2.useRef)(null);
|
|
146
186
|
const drag = (0, import_react2.useRef)(null);
|
|
147
187
|
const [measured, setMeasured] = (0, import_react2.useState)(null);
|
|
188
|
+
const [frozen, setFrozen] = (0, import_react2.useState)(null);
|
|
148
189
|
(0, import_react2.useEffect)(() => {
|
|
149
190
|
const el = wrapper.current;
|
|
150
191
|
if (!responsive || !el || typeof ResizeObserver === "undefined") return;
|
|
@@ -158,9 +199,21 @@ function ChartCanvas({ controller, width = 640, height = 400, responsive = true,
|
|
|
158
199
|
const sceneWidth = responsive && measured || width;
|
|
159
200
|
const fullTheme = (0, import_react2.useMemo)(() => ({ ...import_chart2.CSS_VARIABLE_CHART_THEME, ...theme }), [theme]);
|
|
160
201
|
const scene = (0, import_react2.useMemo)(
|
|
161
|
-
() => (0, import_chart2.layoutChart)(state.spec, {
|
|
162
|
-
|
|
202
|
+
() => (0, import_chart2.layoutChart)(state.spec, {
|
|
203
|
+
width: sceneWidth,
|
|
204
|
+
height,
|
|
205
|
+
theme: fullTheme,
|
|
206
|
+
selection: editable ? state.selection : null,
|
|
207
|
+
measureText,
|
|
208
|
+
emptyMessage,
|
|
209
|
+
valueDomain: frozen?.valueDomain,
|
|
210
|
+
valueDomain2: frozen?.valueDomain2,
|
|
211
|
+
xDomain: frozen?.xDomain,
|
|
212
|
+
readout: frozen?.readout
|
|
213
|
+
}),
|
|
214
|
+
[state.spec, state.selection, sceneWidth, height, fullTheme, editable, measureText, emptyMessage, frozen]
|
|
163
215
|
);
|
|
216
|
+
if (sceneRef) sceneRef.current = scene;
|
|
164
217
|
const byKey = (0, import_react2.useMemo)(() => new Map(scene.marks.map((m) => [m.key, m])), [scene]);
|
|
165
218
|
(0, import_react2.useEffect)(() => () => drag.current?.end(), []);
|
|
166
219
|
const scenePoint = (e) => (0, import_chart2.chartClientToScene)(scene, svg.current.getBoundingClientRect(), e.clientX, e.clientY);
|
|
@@ -170,20 +223,32 @@ function ChartCanvas({ controller, width = 640, height = 400, responsive = true,
|
|
|
170
223
|
const target = key ? byKey.get(key)?.target : void 0;
|
|
171
224
|
controller.select(target ?? null);
|
|
172
225
|
drag.current = (0, import_chart2.startChartPointDrag)(controller, scene, target, scenePoint(e));
|
|
173
|
-
if (drag.current)
|
|
226
|
+
if (drag.current) {
|
|
227
|
+
const d = drag.current;
|
|
228
|
+
setFrozen({ valueDomain: d.valueDomain, valueDomain2: d.valueDomain2 ?? null, xDomain: d.xDomain ?? null, readout: d.target });
|
|
229
|
+
svg.current?.setPointerCapture?.(e.pointerId);
|
|
230
|
+
}
|
|
174
231
|
};
|
|
175
232
|
const onPointerMove = (e) => {
|
|
176
|
-
if (drag.current) drag.current.move(scenePoint(e));
|
|
233
|
+
if (drag.current) drag.current.move(scenePoint(e), { fine: e.altKey, lockAxis: e.shiftKey, noSnap: e.ctrlKey || e.metaKey });
|
|
177
234
|
};
|
|
178
235
|
const endDrag = () => {
|
|
179
236
|
drag.current?.end();
|
|
180
237
|
drag.current = null;
|
|
238
|
+
setFrozen(null);
|
|
181
239
|
};
|
|
182
240
|
const onKeyDown = (e) => {
|
|
241
|
+
if (drag.current && e.key === "Escape") {
|
|
242
|
+
drag.current.cancel();
|
|
243
|
+
drag.current = null;
|
|
244
|
+
setFrozen(null);
|
|
245
|
+
e.preventDefault();
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
183
248
|
if (editable && (0, import_chart2.handleChartKey)(controller, scene, e)) e.preventDefault();
|
|
184
249
|
};
|
|
185
250
|
const label = state.spec.title || "Chart";
|
|
186
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: wrapper, className: `w-full ${className ?? ""}`, "data-chart-canvas": "", children: /* @__PURE__ */ (0, import_jsx_runtime.
|
|
251
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: wrapper, className: `w-full ${className ?? ""}`, "data-chart-canvas": "", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
187
252
|
"svg",
|
|
188
253
|
{
|
|
189
254
|
ref: svg,
|
|
@@ -199,7 +264,10 @@ function ChartCanvas({ controller, width = 640, height = 400, responsive = true,
|
|
|
199
264
|
onPointerUp: endDrag,
|
|
200
265
|
onPointerCancel: endDrag,
|
|
201
266
|
onKeyDown,
|
|
202
|
-
children:
|
|
267
|
+
children: [
|
|
268
|
+
scene.marks.map((m) => renderChartMark(m, scene.theme.fontFamily, editable)),
|
|
269
|
+
presence && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChartPresenceLayer, { presence })
|
|
270
|
+
]
|
|
203
271
|
}
|
|
204
272
|
) });
|
|
205
273
|
}
|
|
@@ -366,43 +434,60 @@ function Toggle({ label, checked, onChange, name }) {
|
|
|
366
434
|
function Select({ id, value, options, onChange, name, labels }) {
|
|
367
435
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("select", { id, className: chartInputClass, value, "data-option": name, onChange: (e) => onChange(e.currentTarget.value), children: options.map((o) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: o, children: labels?.[o] ?? cap(o) }, o)) });
|
|
368
436
|
}
|
|
369
|
-
function AxisSection({ controller, axis, value, idBase, horizontal }) {
|
|
437
|
+
function AxisSection({ controller, axis, value, idBase, horizontal, numeric = false }) {
|
|
370
438
|
const set = (patch) => controller.setAxis(axis, patch);
|
|
371
439
|
const setFormat = (patch) => set({ format: { ...value.format, ...patch } });
|
|
372
|
-
const bound = (
|
|
440
|
+
const bound = (key2) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
373
441
|
ChartCommitInput,
|
|
374
442
|
{
|
|
375
|
-
id: `${idBase}-${axis}-${
|
|
376
|
-
value: value[
|
|
443
|
+
id: `${idBase}-${axis}-${key2}`,
|
|
444
|
+
value: value[key2] === null ? "" : String(value[key2]),
|
|
377
445
|
placeholder: "Auto",
|
|
378
446
|
inputMode: "decimal",
|
|
379
|
-
"data-option": `${axis}Axis.${
|
|
447
|
+
"data-option": `${axis}Axis.${key2}`,
|
|
380
448
|
className: chartInputClass,
|
|
381
|
-
onCommit: (text) => set({ [
|
|
449
|
+
onCommit: (text) => set({ [key2]: text.trim() === "" ? null : Number(text) }).ok
|
|
382
450
|
}
|
|
383
451
|
);
|
|
384
|
-
const name = axis === "x" ? "Category axis" : "Value axis";
|
|
452
|
+
const name = axis === "x" ? numeric ? "X axis" : "Category axis" : axis === "y" ? "Value axis" : "Secondary axis";
|
|
453
|
+
const valueAxis = axis !== "x" || numeric;
|
|
454
|
+
const key = axis === "y2" ? "yAxis2" : `${axis}Axis`;
|
|
385
455
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: chartSectionClass, "data-section": `axis-${axis}`, children: [
|
|
386
456
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
387
457
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("h4", { className: "text-xs font-semibold text-foreground", children: [
|
|
388
458
|
name,
|
|
389
459
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "ml-1 font-normal text-muted-foreground", children: [
|
|
390
460
|
"(",
|
|
391
|
-
axis === "x" ? horizontal ? "left" : "bottom" : horizontal ? "bottom" : "left",
|
|
461
|
+
axis === "x" ? horizontal ? "left" : "bottom" : axis === "y2" ? horizontal ? "top" : "right" : horizontal ? "bottom" : "left",
|
|
392
462
|
")"
|
|
393
463
|
] })
|
|
394
464
|
] }),
|
|
395
465
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Toggle, { label: "Show", name: `${axis}Axis.show`, checked: value.show, onChange: (show) => set({ show }) })
|
|
396
466
|
] }),
|
|
397
467
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Field, { label: "Title", htmlFor: `${idBase}-${axis}-title`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChartCommitInput, { id: `${idBase}-${axis}-title`, value: value.title, "data-option": `${axis}Axis.title`, className: chartInputClass, onCommit: (title) => void set({ title }) }) }),
|
|
398
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.
|
|
399
|
-
|
|
468
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex flex-wrap gap-x-4 gap-y-1", children: [
|
|
469
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Toggle, { label: "Grid lines", name: `${key}.gridlines`, checked: value.gridlines, onChange: (gridlines) => set({ gridlines }) }),
|
|
470
|
+
axis === "x" && !numeric && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Toggle, { label: "Dates", name: "xAxis.kind", checked: value.kind === "date", onChange: (dates) => set({ kind: dates ? "date" : void 0 }) })
|
|
471
|
+
] }),
|
|
472
|
+
valueAxis && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Field, { label: "Step", htmlFor: `${idBase}-${axis}-step`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
473
|
+
ChartCommitInput,
|
|
474
|
+
{
|
|
475
|
+
id: `${idBase}-${axis}-step`,
|
|
476
|
+
value: value.step === null ? "" : String(value.step),
|
|
477
|
+
placeholder: "Auto",
|
|
478
|
+
inputMode: "decimal",
|
|
479
|
+
"data-option": `${key}.step`,
|
|
480
|
+
className: chartInputClass,
|
|
481
|
+
onCommit: (text) => set({ step: text.trim() === "" ? null : Number(text) }).ok
|
|
482
|
+
}
|
|
483
|
+
) }),
|
|
484
|
+
valueAxis && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
400
485
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Field, { label: "Min / max", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex gap-1", children: [
|
|
401
486
|
bound("min"),
|
|
402
487
|
bound("max")
|
|
403
488
|
] }) }),
|
|
404
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Field, { label: "Format", htmlFor: `${idBase}-
|
|
405
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Select, { id: `${idBase}-
|
|
489
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Field, { label: "Format", htmlFor: `${idBase}-${axis}-format`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex gap-1", children: [
|
|
490
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Select, { id: `${idBase}-${axis}-format`, name: `${key}.format.style`, value: value.format.style ?? "number", options: import_chart4.CHART_NUMBER_STYLES, onChange: (style) => setFormat({ style }) }),
|
|
406
491
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
407
492
|
ChartCommitInput,
|
|
408
493
|
{
|
|
@@ -411,7 +496,7 @@ function AxisSection({ controller, axis, value, idBase, horizontal }) {
|
|
|
411
496
|
"aria-label": "Decimals",
|
|
412
497
|
title: "Decimals",
|
|
413
498
|
inputMode: "numeric",
|
|
414
|
-
"data-option":
|
|
499
|
+
"data-option": `${key}.format.decimals`,
|
|
415
500
|
className: `${chartInputClass} w-14 shrink-0`,
|
|
416
501
|
onCommit: (text) => {
|
|
417
502
|
const format = { ...value.format };
|
|
@@ -422,7 +507,23 @@ function AxisSection({ controller, axis, value, idBase, horizontal }) {
|
|
|
422
507
|
}
|
|
423
508
|
)
|
|
424
509
|
] }) }),
|
|
425
|
-
value.format.style === "currency" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Field, { label: "Currency", htmlFor: `${idBase}-
|
|
510
|
+
value.format.style === "currency" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Field, { label: "Currency", htmlFor: `${idBase}-${axis}-currency`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChartCommitInput, { id: `${idBase}-${axis}-currency`, value: value.format.currency ?? "USD", "data-option": `${key}.format.currency`, className: chartInputClass, onCommit: (currency) => void setFormat({ currency: currency.toUpperCase() }) }) }),
|
|
511
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Field, { label: "Code", htmlFor: `${idBase}-${axis}-code`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
512
|
+
ChartCommitInput,
|
|
513
|
+
{
|
|
514
|
+
id: `${idBase}-${axis}-code`,
|
|
515
|
+
value: value.format.code ?? "",
|
|
516
|
+
placeholder: 'e.g. #,##0.0,,"M"',
|
|
517
|
+
"data-option": `${key}.format.code`,
|
|
518
|
+
className: chartInputClass,
|
|
519
|
+
onCommit: (code) => {
|
|
520
|
+
const format = { ...value.format };
|
|
521
|
+
if (code.trim()) format.code = code.trim();
|
|
522
|
+
else delete format.code;
|
|
523
|
+
return set({ format }).ok;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
) })
|
|
426
527
|
] })
|
|
427
528
|
] });
|
|
428
529
|
}
|
|
@@ -431,12 +532,28 @@ function ChartInspector({ controller, sections, className }) {
|
|
|
431
532
|
const idBase = (0, import_react4.useId)();
|
|
432
533
|
const show = (section) => !sections || sections.includes(section);
|
|
433
534
|
const cartesian = (0, import_chart4.isCartesianChart)(spec.type);
|
|
535
|
+
const family = (0, import_chart4.chartFamily)(spec);
|
|
536
|
+
const hasAxes = family === "cartesian" || family === "xy" || family === "waterfall" || family === "histogram" || family === "box";
|
|
434
537
|
const types = spec.series.map((s) => (0, import_chart4.chartSeriesType)(spec, s));
|
|
538
|
+
const usesSecondary = family === "cartesian" && spec.series.some((s) => s.axis === "secondary" && !s.hidden);
|
|
539
|
+
const colorCheck = (0, import_chart4.checkChartColors)(spec.series.filter((s) => !s.hidden).map((s) => (0, import_chart4.chartSeriesColor)(spec, spec.series.indexOf(s))));
|
|
435
540
|
const hasBars = cartesian && types.includes("bar");
|
|
436
541
|
const hasLines = cartesian && types.some((t) => t === "line" || t === "area");
|
|
437
542
|
const seriesIndex = selectedSeries ? spec.series.findIndex((s) => s.id === selectedSeries.id) : -1;
|
|
438
543
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `space-y-3 text-xs ${className ?? ""}`, "data-chart-inspector": "", children: [
|
|
439
544
|
show("type") && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: chartSectionClass, "data-section": "type", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "grid grid-cols-3 gap-0.5 rounded-md bg-muted p-0.5", role: "radiogroup", "aria-label": "Chart type", children: import_chart4.CHART_TYPES.map((type) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", role: "radio", "aria-checked": spec.type === type, "data-chart-type": type, className: chartSegmentClass(spec.type === type), onClick: () => controller.setType(type), children: import_chart4.CHART_TYPE_LABELS[type] }, type)) }) }),
|
|
545
|
+
show("style") && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: chartSectionClass, "data-section": "style", children: [
|
|
546
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Field, { label: "Layout", htmlFor: `${idBase}-layout`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("select", { id: `${idBase}-layout`, className: chartInputClass, value: "", "data-option": "preset.layout", onChange: (e) => e.currentTarget.value && controller.applyPreset({ layout: e.currentTarget.value }), children: [
|
|
547
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "", children: "Quick layout\u2026" }),
|
|
548
|
+
import_chart4.CHART_LAYOUT_PRESETS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: p.id, title: p.description, children: p.label }, p.id))
|
|
549
|
+
] }) }),
|
|
550
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Field, { label: "Colours", htmlFor: `${idBase}-colors`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("select", { id: `${idBase}-colors`, className: chartInputClass, value: import_chart4.CHART_COLOR_SCHEMES.find((c) => c.palette.join() === spec.palette.join())?.id ?? "", "data-option": "preset.colors", onChange: (e) => e.currentTarget.value && controller.applyPreset({ colors: e.currentTarget.value }), children: [
|
|
551
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "", children: "Custom" }),
|
|
552
|
+
import_chart4.CHART_COLOR_SCHEMES.map((c) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: c.id, title: c.description, children: c.label }, c.id))
|
|
553
|
+
] }) }),
|
|
554
|
+
!colorCheck.ok && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-[11px] leading-snug text-amber-600 dark:text-amber-400", "data-chart-color-warning": "", children: colorCheck.issues[0] }),
|
|
555
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: chartButtonClass, "data-action": "transpose", onClick: () => controller.transpose(), children: "Switch rows and columns" })
|
|
556
|
+
] }),
|
|
440
557
|
show("titles") && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: chartSectionClass, "data-section": "titles", children: [
|
|
441
558
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Field, { label: "Title", htmlFor: `${idBase}-title`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChartCommitInput, { id: `${idBase}-title`, value: spec.title, "data-option": "title", className: chartInputClass, onCommit: (title) => void controller.setTitle(title) }) }),
|
|
442
559
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Field, { label: "Subtitle", htmlFor: `${idBase}-subtitle`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChartCommitInput, { id: `${idBase}-subtitle`, value: spec.subtitle, "data-option": "subtitle", className: chartInputClass, onCommit: (subtitle) => void controller.setSubtitle(subtitle) }) })
|
|
@@ -461,7 +578,26 @@ function ChartInspector({ controller, sections, className }) {
|
|
|
461
578
|
onChange: (e) => controller.update({ innerRadius: Number(e.currentTarget.value) })
|
|
462
579
|
}
|
|
463
580
|
) }),
|
|
581
|
+
hasBars && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Field, { label: "Spacing", htmlFor: `${idBase}-gap`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
582
|
+
"input",
|
|
583
|
+
{
|
|
584
|
+
id: `${idBase}-gap`,
|
|
585
|
+
type: "range",
|
|
586
|
+
min: 0,
|
|
587
|
+
max: 0.9,
|
|
588
|
+
step: 0.02,
|
|
589
|
+
value: spec.gapWidth,
|
|
590
|
+
"data-option": "gapWidth",
|
|
591
|
+
className: "w-full accent-primary",
|
|
592
|
+
onPointerDown: () => controller.beginGesture("gapWidth"),
|
|
593
|
+
onPointerUp: () => controller.endGesture(),
|
|
594
|
+
onChange: (e) => controller.update({ gapWidth: Number(e.currentTarget.value) })
|
|
595
|
+
}
|
|
596
|
+
) }),
|
|
597
|
+
spec.dataLabels && hasBars && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Field, { label: "Labels", htmlFor: `${idBase}-label-position`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Select, { id: `${idBase}-label-position`, name: "labelPosition", value: spec.labelPosition, options: import_chart4.CHART_LABEL_POSITIONS, onChange: (labelPosition) => controller.update({ labelPosition }) }) }),
|
|
598
|
+
spec.type === "histogram" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Field, { label: "Bins", htmlFor: `${idBase}-bins`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChartCommitInput, { id: `${idBase}-bins`, value: spec.bins === null ? "" : String(spec.bins), placeholder: "Auto", inputMode: "numeric", "data-option": "bins", className: chartInputClass, onCommit: (text) => controller.update({ bins: text.trim() === "" ? null : Number(text) }).ok }) }),
|
|
464
599
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex flex-wrap gap-x-4 gap-y-1", children: [
|
|
600
|
+
spec.type === "waterfall" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Toggle, { label: "Total bar", name: "showTotal", checked: spec.showTotal, onChange: (showTotal) => controller.update({ showTotal }) }),
|
|
465
601
|
hasLines && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Toggle, { label: "Points", name: "points", checked: spec.points, onChange: (points) => controller.update({ points }) }),
|
|
466
602
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Toggle, { label: "Data labels", name: "dataLabels", checked: spec.dataLabels, onChange: (dataLabels) => controller.update({ dataLabels }) })
|
|
467
603
|
] })
|
|
@@ -473,9 +609,10 @@ function ChartInspector({ controller, sections, className }) {
|
|
|
473
609
|
] }),
|
|
474
610
|
spec.legend.show && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex gap-0.5 rounded-md bg-muted p-0.5", children: import_chart4.CHART_LEGEND_POSITIONS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", "aria-pressed": spec.legend.position === p, "data-option": `legend.${p}`, className: chartSegmentClass(spec.legend.position === p), onClick: () => controller.setLegend({ position: p }), children: cap(p) }, p)) })
|
|
475
611
|
] }),
|
|
476
|
-
show("axes") &&
|
|
477
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AxisSection, { controller, axis: "x", value: spec.xAxis, idBase, horizontal: spec.orientation === "horizontal" }),
|
|
478
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AxisSection, { controller, axis: "y", value: spec.yAxis, idBase, horizontal: spec.orientation === "horizontal" })
|
|
612
|
+
show("axes") && hasAxes && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
613
|
+
family !== "box" && family !== "histogram" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AxisSection, { controller, axis: "x", value: spec.xAxis, idBase, horizontal: spec.orientation === "horizontal", numeric: family === "xy" }),
|
|
614
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AxisSection, { controller, axis: "y", value: spec.yAxis, idBase, horizontal: spec.orientation === "horizontal" }),
|
|
615
|
+
usesSecondary && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AxisSection, { controller, axis: "y2", value: spec.yAxis2, idBase, horizontal: spec.orientation === "horizontal" })
|
|
479
616
|
] }),
|
|
480
617
|
show("series") && selectedSeries && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: chartSectionClass, "data-section": "series", "data-series-id": selectedSeries.id, children: [
|
|
481
618
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h4", { className: "text-xs font-semibold text-foreground", children: "Series" }),
|
|
@@ -510,8 +647,44 @@ function ChartInspector({ controller, sections, className }) {
|
|
|
510
647
|
]
|
|
511
648
|
}
|
|
512
649
|
) }),
|
|
650
|
+
(cartesian || family === "xy") && ["line", "area", "scatter"].includes((0, import_chart4.chartSeriesType)(spec, selectedSeries)) && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Field, { label: "Marker", htmlFor: `${idBase}-series-marker`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
651
|
+
"select",
|
|
652
|
+
{
|
|
653
|
+
id: `${idBase}-series-marker`,
|
|
654
|
+
className: chartInputClass,
|
|
655
|
+
value: selectedSeries.marker ?? "",
|
|
656
|
+
"data-option": "series.marker",
|
|
657
|
+
onChange: (e) => controller.updateSeries(selectedSeries.id, { marker: e.currentTarget.value || null }),
|
|
658
|
+
children: [
|
|
659
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "", children: "Default" }),
|
|
660
|
+
import_chart4.CHART_MARKERS.map((m) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: m, children: cap(m) }, m))
|
|
661
|
+
]
|
|
662
|
+
}
|
|
663
|
+
) }),
|
|
664
|
+
(0, import_chart4.chartSupportsTrendlines)(spec.type, spec.stacking) && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "space-y-1", "data-section": "trendlines", children: [
|
|
665
|
+
(selectedSeries.trendlines ?? []).map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-1", "data-trendline": i, children: [
|
|
666
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "flex-1 truncate text-muted-foreground", children: (0, import_chart4.chartTrendlineName)(t, selectedSeries.name) }),
|
|
667
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Toggle, { label: "R\xB2", name: `trendline.${i}.showR2`, checked: !!t.showR2, onChange: (showR2) => controller.updateTrendline(selectedSeries.id, i, { showR2 }) }),
|
|
668
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: chartButtonClass, "aria-label": "Delete trendline", "data-action": "trendline-delete", onClick: () => controller.removeTrendline(selectedSeries.id, i), children: "\xD7" })
|
|
669
|
+
] }, i)),
|
|
670
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
671
|
+
"select",
|
|
672
|
+
{
|
|
673
|
+
className: chartInputClass,
|
|
674
|
+
value: "",
|
|
675
|
+
"aria-label": "Add trendline",
|
|
676
|
+
"data-option": "trendline.add",
|
|
677
|
+
onChange: (e) => e.currentTarget.value && controller.addTrendline(selectedSeries.id, { type: e.currentTarget.value }),
|
|
678
|
+
children: [
|
|
679
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "", children: "Add trendline\u2026" }),
|
|
680
|
+
import_chart4.CHART_TRENDLINE_TYPES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: t, children: cap(t.replace("-", " ")) }, t))
|
|
681
|
+
]
|
|
682
|
+
}
|
|
683
|
+
)
|
|
684
|
+
] }),
|
|
513
685
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
514
686
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Toggle, { label: "Hidden", name: "series.hidden", checked: !!selectedSeries.hidden, onChange: (hidden) => controller.updateSeries(selectedSeries.id, { hidden }) }),
|
|
687
|
+
family === "cartesian" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Toggle, { label: "Secondary axis", name: "series.axis", checked: selectedSeries.axis === "secondary", onChange: (secondary) => controller.updateSeries(selectedSeries.id, { axis: secondary ? "secondary" : null }) }),
|
|
515
688
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "flex-1" }),
|
|
516
689
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: chartButtonClass, disabled: seriesIndex <= 0, "aria-label": "Move series earlier", "data-action": "series-up", onClick: () => controller.moveSeries(selectedSeries.id, seriesIndex - 1), children: "\u2191" }),
|
|
517
690
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: chartButtonClass, disabled: seriesIndex >= spec.series.length - 1, "aria-label": "Move series later", "data-action": "series-down", onClick: () => controller.moveSeries(selectedSeries.id, seriesIndex + 1), children: "\u2193" }),
|
|
@@ -540,6 +713,61 @@ function ChartEditor({ controller, showInspector = true, showDataGrid = true, sh
|
|
|
540
713
|
showDataGrid && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ChartDataGrid, { controller })
|
|
541
714
|
] });
|
|
542
715
|
}
|
|
716
|
+
|
|
717
|
+
// src/chart/ChartStreamingView.tsx
|
|
718
|
+
var import_react5 = require("react");
|
|
719
|
+
var import_chart6 = require("@pixerate/editor/chart");
|
|
720
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
721
|
+
function ChartStreamingView({ source, complete = false, format = "auto", width = 640, height = 360, theme, adoptInterval = 300, tween = 450, placeholder, onIssues, className }) {
|
|
722
|
+
const stream = (0, import_react5.useMemo)(() => new import_chart6.ChartStream(format), [format]);
|
|
723
|
+
const fullTheme = (0, import_react5.useMemo)(() => ({ ...import_chart6.CSS_VARIABLE_CHART_THEME, ...theme }), [theme]);
|
|
724
|
+
const [scene, setScene] = (0, import_react5.useState)(null);
|
|
725
|
+
const shown = (0, import_react5.useRef)(null);
|
|
726
|
+
const lastAdopt = (0, import_react5.useRef)(0);
|
|
727
|
+
const frame = (0, import_react5.useRef)(null);
|
|
728
|
+
const pending = (0, import_react5.useRef)(null);
|
|
729
|
+
(0, import_react5.useEffect)(() => {
|
|
730
|
+
const adopt = () => {
|
|
731
|
+
const read = stream.update(source, complete);
|
|
732
|
+
if (complete && onIssues) onIssues(read.issues);
|
|
733
|
+
if (!read.spec) return;
|
|
734
|
+
const next = (0, import_chart6.layoutChart)(read.spec, { width, height, theme: fullTheme, valueDomain: read.valueDomain });
|
|
735
|
+
const from = shown.current;
|
|
736
|
+
lastAdopt.current = Date.now();
|
|
737
|
+
if (frame.current !== null) cancelAnimationFrame(frame.current);
|
|
738
|
+
if (!from || tween <= 0 || typeof requestAnimationFrame === "undefined") {
|
|
739
|
+
shown.current = next;
|
|
740
|
+
setScene(next);
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
const start = performance.now();
|
|
744
|
+
const step = () => {
|
|
745
|
+
const t = (0, import_chart6.easeChartTween)((performance.now() - start) / tween);
|
|
746
|
+
const s = (0, import_chart6.blendChartScenes)(from, next, t);
|
|
747
|
+
shown.current = s;
|
|
748
|
+
setScene(s);
|
|
749
|
+
frame.current = t < 1 ? requestAnimationFrame(step) : null;
|
|
750
|
+
};
|
|
751
|
+
frame.current = requestAnimationFrame(step);
|
|
752
|
+
};
|
|
753
|
+
if (pending.current) clearTimeout(pending.current);
|
|
754
|
+
const wait = complete ? 0 : Math.max(0, adoptInterval - (Date.now() - lastAdopt.current));
|
|
755
|
+
if (wait === 0) adopt();
|
|
756
|
+
else pending.current = setTimeout(adopt, wait);
|
|
757
|
+
return () => {
|
|
758
|
+
if (pending.current) clearTimeout(pending.current);
|
|
759
|
+
};
|
|
760
|
+
}, [source, complete, stream, width, height, fullTheme, adoptInterval, tween, onIssues]);
|
|
761
|
+
(0, import_react5.useEffect)(() => () => {
|
|
762
|
+
if (frame.current !== null) cancelAnimationFrame(frame.current);
|
|
763
|
+
}, []);
|
|
764
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className, "data-chart-streaming": complete ? "complete" : "streaming", "aria-busy": !complete, children: scene ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChartSceneSvg, { scene, label: complete ? void 0 : "Chart loading" }) : placeholder ?? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { height }, className: "flex items-center justify-center text-xs text-muted-foreground", children: "Drawing chart\u2026" }) });
|
|
765
|
+
}
|
|
766
|
+
function ChartSparkline({ values, width = 80, height = 20, label, className, ...options }) {
|
|
767
|
+
const scene = (0, import_react5.useMemo)(() => (0, import_chart6.layoutSparkline)(values, { ...options, width, height }), [values, width, height, options.kind, options.color, options.negativeColor, options.markers, options.highLow, options.min, options.max]);
|
|
768
|
+
const present = values.filter((v) => v !== null);
|
|
769
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChartSceneSvg, { scene, label: label ?? (present.length ? `Trend from ${present[0]} to ${present[present.length - 1]}` : "No data"), className: `inline-block align-middle ${className ?? ""}` });
|
|
770
|
+
}
|
|
543
771
|
// Annotate the CommonJS export names for ESM import in node:
|
|
544
772
|
0 && (module.exports = {
|
|
545
773
|
ChartCanvas,
|
|
@@ -547,6 +775,10 @@ function ChartEditor({ controller, showInspector = true, showDataGrid = true, sh
|
|
|
547
775
|
ChartDataGrid,
|
|
548
776
|
ChartEditor,
|
|
549
777
|
ChartInspector,
|
|
778
|
+
ChartPresenceLayer,
|
|
779
|
+
ChartSceneSvg,
|
|
780
|
+
ChartSparkline,
|
|
781
|
+
ChartStreamingView,
|
|
550
782
|
chartButtonClass,
|
|
551
783
|
chartCellClass,
|
|
552
784
|
chartIconButtonClass,
|
|
@@ -554,9 +786,12 @@ function ChartEditor({ controller, showInspector = true, showDataGrid = true, sh
|
|
|
554
786
|
chartLabelClass,
|
|
555
787
|
chartSectionClass,
|
|
556
788
|
chartSegmentClass,
|
|
789
|
+
renderChartMark,
|
|
557
790
|
useChartCommands,
|
|
558
791
|
useChartController,
|
|
792
|
+
useChartDirector,
|
|
559
793
|
useChartEditor,
|
|
560
|
-
useChartState
|
|
794
|
+
useChartState,
|
|
795
|
+
useChartToolProvider
|
|
561
796
|
});
|
|
562
797
|
//# sourceMappingURL=index.cjs.map
|