@pixerate/editor-svelte 0.42.1 → 0.43.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/ChartCanvas.svelte +32 -98
- package/dist/chart/ChartCanvas.svelte.d.ts +7 -1
- package/dist/chart/ChartCanvas.svelte.d.ts.map +1 -1
- package/dist/chart/ChartInspector.svelte +185 -28
- package/dist/chart/ChartInspector.svelte.d.ts +1 -1
- package/dist/chart/ChartInspector.svelte.d.ts.map +1 -1
- package/dist/chart/ChartMarks.svelte +102 -0
- package/dist/chart/ChartMarks.svelte.d.ts +11 -0
- package/dist/chart/ChartMarks.svelte.d.ts.map +1 -0
- package/dist/chart/ChartPresenceLayer.svelte +30 -0
- package/dist/chart/ChartPresenceLayer.svelte.d.ts +9 -0
- package/dist/chart/ChartPresenceLayer.svelte.d.ts.map +1 -0
- package/dist/chart/ChartSceneSvg.svelte +17 -0
- package/dist/chart/ChartSceneSvg.svelte.d.ts +11 -0
- package/dist/chart/ChartSceneSvg.svelte.d.ts.map +1 -0
- package/dist/chart/ChartSparkline.svelte +27 -0
- package/dist/chart/ChartSparkline.svelte.d.ts +20 -0
- package/dist/chart/ChartSparkline.svelte.d.ts.map +1 -0
- package/dist/chart/ChartStreamingView.svelte +92 -0
- package/dist/chart/ChartStreamingView.svelte.d.ts +25 -0
- package/dist/chart/ChartStreamingView.svelte.d.ts.map +1 -0
- package/dist/chart/createChartEditor.svelte.d.ts +25 -1
- package/dist/chart/createChartEditor.svelte.d.ts.map +1 -1
- package/dist/chart/createChartEditor.svelte.js +25 -1
- package/dist/chart/index.d.ts +9 -0
- package/dist/chart/index.d.ts.map +1 -1
- package/dist/chart/index.js +5 -0
- package/package.json +2 -2
|
@@ -3,19 +3,20 @@
|
|
|
3
3
|
import {
|
|
4
4
|
CSS_VARIABLE_CHART_THEME,
|
|
5
5
|
chartClientToScene,
|
|
6
|
-
chartTextBaseline,
|
|
7
|
-
chartTextTransform,
|
|
8
6
|
describeChartSelection,
|
|
9
7
|
handleChartKey,
|
|
10
8
|
layoutChart,
|
|
11
9
|
startChartPointDrag,
|
|
12
10
|
type ChartController,
|
|
13
|
-
type ChartMark,
|
|
14
11
|
type ChartPointDrag,
|
|
12
|
+
type ChartPresence,
|
|
13
|
+
type ChartScene,
|
|
15
14
|
type ChartTextMeasure,
|
|
16
15
|
type ChartTheme
|
|
17
16
|
} from '@pixerate/editor/chart';
|
|
18
17
|
import { chartStateOf } from './createChartEditor.svelte.js';
|
|
18
|
+
import ChartMarks from './ChartMarks.svelte';
|
|
19
|
+
import ChartPresenceLayer from './ChartPresenceLayer.svelte';
|
|
19
20
|
|
|
20
21
|
interface Props {
|
|
21
22
|
controller: ChartController;
|
|
@@ -33,6 +34,10 @@
|
|
|
33
34
|
measureText?: ChartTextMeasure;
|
|
34
35
|
emptyMessage?: string;
|
|
35
36
|
class?: string;
|
|
37
|
+
/** Agent cursors to draw over the chart (see `ChartDirector`). */
|
|
38
|
+
presence?: ChartPresence;
|
|
39
|
+
/** Receives the scene as drawn (e.g. for `ChartDirector`'s `scene`). */
|
|
40
|
+
sceneRef?: { current: ChartScene | null };
|
|
36
41
|
}
|
|
37
42
|
|
|
38
43
|
let {
|
|
@@ -44,7 +49,9 @@
|
|
|
44
49
|
editable = true,
|
|
45
50
|
measureText,
|
|
46
51
|
emptyMessage,
|
|
47
|
-
class: className = ''
|
|
52
|
+
class: className = '',
|
|
53
|
+
presence,
|
|
54
|
+
sceneRef
|
|
48
55
|
}: Props = $props();
|
|
49
56
|
|
|
50
57
|
const view = chartStateOf(() => controller);
|
|
@@ -64,8 +71,8 @@
|
|
|
64
71
|
return () => observer.disconnect();
|
|
65
72
|
});
|
|
66
73
|
let drag: ChartPointDrag | null = null;
|
|
67
|
-
// Frozen while a point is dragged so the
|
|
68
|
-
let
|
|
74
|
+
// Frozen while a point is dragged so the axes do not rescale under the pointer.
|
|
75
|
+
let frozen = $state.raw<{ valueDomain: ChartPointDrag['valueDomain']; valueDomain2: ChartPointDrag['valueDomain2'] | null; xDomain: ChartPointDrag['xDomain'] | null; readout: ChartPointDrag['target'] } | null>(null);
|
|
69
76
|
|
|
70
77
|
const sceneWidth = $derived((responsive && measured) || width);
|
|
71
78
|
const fullTheme = $derived({ ...CSS_VARIABLE_CHART_THEME, ...theme });
|
|
@@ -77,16 +84,20 @@
|
|
|
77
84
|
selection: editable ? view.current.selection : null,
|
|
78
85
|
measureText,
|
|
79
86
|
emptyMessage,
|
|
80
|
-
valueDomain
|
|
87
|
+
valueDomain: frozen?.valueDomain,
|
|
88
|
+
valueDomain2: frozen?.valueDomain2,
|
|
89
|
+
xDomain: frozen?.xDomain,
|
|
90
|
+
readout: frozen?.readout
|
|
81
91
|
})
|
|
82
92
|
);
|
|
93
|
+
$effect(() => {
|
|
94
|
+
if (sceneRef) sceneRef.current = scene;
|
|
95
|
+
});
|
|
83
96
|
const byKey = $derived(new Map(scene.marks.map((m) => [m.key, m])));
|
|
84
97
|
const label = $derived(view.current.spec.title || 'Chart');
|
|
85
98
|
|
|
86
99
|
onDestroy(() => drag?.end());
|
|
87
100
|
|
|
88
|
-
const pointerEvents = (mark: ChartMark) => (mark.role === 'selection' || (!mark.target && mark.role !== 'background') ? 'none' : undefined);
|
|
89
|
-
const cursor = (mark: ChartMark) => (editable && mark.target && mark.role !== 'background' ? 'pointer' : undefined);
|
|
90
101
|
const scenePoint = (e: PointerEvent) => chartClientToScene(scene, svg!.getBoundingClientRect(), e.clientX, e.clientY);
|
|
91
102
|
|
|
92
103
|
function pointerdown(e: PointerEvent) {
|
|
@@ -96,19 +107,26 @@
|
|
|
96
107
|
controller.select(target ?? null);
|
|
97
108
|
drag = startChartPointDrag(controller, scene, target, scenePoint(e));
|
|
98
109
|
if (drag) {
|
|
99
|
-
|
|
110
|
+
frozen = { valueDomain: drag.valueDomain, valueDomain2: drag.valueDomain2 ?? null, xDomain: drag.xDomain ?? null, readout: drag.target };
|
|
100
111
|
svg?.setPointerCapture?.(e.pointerId);
|
|
101
112
|
}
|
|
102
113
|
}
|
|
103
114
|
function pointermove(e: PointerEvent) {
|
|
104
|
-
drag?.move(scenePoint(e));
|
|
115
|
+
drag?.move(scenePoint(e), { fine: e.altKey, lockAxis: e.shiftKey, noSnap: e.ctrlKey || e.metaKey });
|
|
105
116
|
}
|
|
106
117
|
function endDrag() {
|
|
107
118
|
drag?.end();
|
|
108
119
|
drag = null;
|
|
109
|
-
|
|
120
|
+
frozen = null;
|
|
110
121
|
}
|
|
111
122
|
function keydown(e: KeyboardEvent) {
|
|
123
|
+
if (drag && e.key === 'Escape') {
|
|
124
|
+
drag.cancel();
|
|
125
|
+
drag = null;
|
|
126
|
+
frozen = null;
|
|
127
|
+
e.preventDefault();
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
112
130
|
if (editable && handleChartKey(controller, scene, e)) e.preventDefault();
|
|
113
131
|
}
|
|
114
132
|
</script>
|
|
@@ -130,91 +148,7 @@
|
|
|
130
148
|
onpointercancel={endDrag}
|
|
131
149
|
onkeydown={keydown}
|
|
132
150
|
>
|
|
133
|
-
{
|
|
134
|
-
|
|
135
|
-
<rect
|
|
136
|
-
data-role={mark.role}
|
|
137
|
-
data-chart-key={mark.target ? mark.key : undefined}
|
|
138
|
-
data-selected={mark.selected ? '' : undefined}
|
|
139
|
-
x={mark.x}
|
|
140
|
-
y={mark.y}
|
|
141
|
-
width={mark.width}
|
|
142
|
-
height={mark.height}
|
|
143
|
-
rx={mark.radius}
|
|
144
|
-
opacity={mark.opacity}
|
|
145
|
-
pointer-events={pointerEvents(mark)}
|
|
146
|
-
cursor={cursor(mark)}
|
|
147
|
-
style:fill={mark.fill}
|
|
148
|
-
style:stroke={mark.stroke}
|
|
149
|
-
style:stroke-width={mark.strokeWidth}
|
|
150
|
-
/>
|
|
151
|
-
{:else if mark.kind === 'path'}
|
|
152
|
-
<path
|
|
153
|
-
data-role={mark.role}
|
|
154
|
-
data-chart-key={mark.target ? mark.key : undefined}
|
|
155
|
-
data-selected={mark.selected ? '' : undefined}
|
|
156
|
-
d={mark.d}
|
|
157
|
-
stroke-linejoin="round"
|
|
158
|
-
stroke-linecap="round"
|
|
159
|
-
stroke-dasharray={mark.dash}
|
|
160
|
-
opacity={mark.opacity}
|
|
161
|
-
pointer-events={pointerEvents(mark)}
|
|
162
|
-
cursor={cursor(mark)}
|
|
163
|
-
style:fill={mark.fill ?? 'none'}
|
|
164
|
-
style:stroke={mark.stroke}
|
|
165
|
-
style:stroke-width={mark.strokeWidth}
|
|
166
|
-
/>
|
|
167
|
-
{:else if mark.kind === 'circle'}
|
|
168
|
-
<circle
|
|
169
|
-
data-role={mark.role}
|
|
170
|
-
data-chart-key={mark.target ? mark.key : undefined}
|
|
171
|
-
data-selected={mark.selected ? '' : undefined}
|
|
172
|
-
cx={mark.cx}
|
|
173
|
-
cy={mark.cy}
|
|
174
|
-
r={mark.r}
|
|
175
|
-
opacity={mark.opacity}
|
|
176
|
-
pointer-events={pointerEvents(mark)}
|
|
177
|
-
cursor={cursor(mark)}
|
|
178
|
-
style:fill={mark.fill}
|
|
179
|
-
style:stroke={mark.stroke}
|
|
180
|
-
style:stroke-width={mark.strokeWidth}
|
|
181
|
-
/>
|
|
182
|
-
{:else if mark.kind === 'line'}
|
|
183
|
-
<line
|
|
184
|
-
data-role={mark.role}
|
|
185
|
-
data-chart-key={mark.target ? mark.key : undefined}
|
|
186
|
-
data-selected={mark.selected ? '' : undefined}
|
|
187
|
-
x1={mark.x1}
|
|
188
|
-
y1={mark.y1}
|
|
189
|
-
x2={mark.x2}
|
|
190
|
-
y2={mark.y2}
|
|
191
|
-
stroke-dasharray={mark.dash}
|
|
192
|
-
opacity={mark.opacity}
|
|
193
|
-
pointer-events={pointerEvents(mark)}
|
|
194
|
-
cursor={cursor(mark)}
|
|
195
|
-
style:stroke={mark.stroke}
|
|
196
|
-
style:stroke-width={mark.strokeWidth}
|
|
197
|
-
/>
|
|
198
|
-
{:else}
|
|
199
|
-
<text
|
|
200
|
-
data-role={mark.role}
|
|
201
|
-
data-chart-key={mark.target ? mark.key : undefined}
|
|
202
|
-
data-selected={mark.selected ? '' : undefined}
|
|
203
|
-
x={mark.x}
|
|
204
|
-
y={mark.y}
|
|
205
|
-
text-anchor={mark.anchor}
|
|
206
|
-
dominant-baseline={chartTextBaseline(mark.baseline)}
|
|
207
|
-
transform={chartTextTransform(mark)}
|
|
208
|
-
opacity={mark.opacity}
|
|
209
|
-
pointer-events={pointerEvents(mark)}
|
|
210
|
-
cursor={cursor(mark)}
|
|
211
|
-
style:fill={mark.fill}
|
|
212
|
-
style:font-size="{mark.size}px"
|
|
213
|
-
style:font-weight={mark.weight}
|
|
214
|
-
style:font-family={scene.theme.fontFamily}
|
|
215
|
-
style:user-select="none">{mark.text}</text
|
|
216
|
-
>
|
|
217
|
-
{/if}
|
|
218
|
-
{/each}
|
|
151
|
+
<ChartMarks marks={scene.marks} fontFamily={scene.theme.fontFamily} interactive={editable} />
|
|
152
|
+
{#if presence}<ChartPresenceLayer {presence} />{/if}
|
|
219
153
|
</svg>
|
|
220
154
|
</div>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ChartController, type ChartTextMeasure, type ChartTheme } from '@pixerate/editor/chart';
|
|
1
|
+
import { type ChartController, type ChartPresence, type ChartScene, type ChartTextMeasure, type ChartTheme } from '@pixerate/editor/chart';
|
|
2
2
|
interface Props {
|
|
3
3
|
controller: ChartController;
|
|
4
4
|
/** Scene width; with `responsive` this is only the fallback before the container is measured (default 640). */
|
|
@@ -15,6 +15,12 @@ interface Props {
|
|
|
15
15
|
measureText?: ChartTextMeasure;
|
|
16
16
|
emptyMessage?: string;
|
|
17
17
|
class?: string;
|
|
18
|
+
/** Agent cursors to draw over the chart (see `ChartDirector`). */
|
|
19
|
+
presence?: ChartPresence;
|
|
20
|
+
/** Receives the scene as drawn (e.g. for `ChartDirector`'s `scene`). */
|
|
21
|
+
sceneRef?: {
|
|
22
|
+
current: ChartScene | null;
|
|
23
|
+
};
|
|
18
24
|
}
|
|
19
25
|
declare const ChartCanvas: import("svelte").Component<Props, {}, "">;
|
|
20
26
|
type ChartCanvas = ReturnType<typeof ChartCanvas>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChartCanvas.svelte.d.ts","sourceRoot":"","sources":["../../src/chart/ChartCanvas.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,
|
|
1
|
+
{"version":3,"file":"ChartCanvas.svelte.d.ts","sourceRoot":"","sources":["../../src/chart/ChartCanvas.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,EAOH,KAAK,eAAe,EAEpB,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,KAAK,UAAU,EAChB,MAAM,wBAAwB,CAAC;AAMhC,UAAU,KAAK;IACb,UAAU,EAAE,eAAe,CAAC;IAC5B,+GAA+G;IAC/G,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAC5B,gFAAgF;IAChF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oDAAoD;IACpD,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,wEAAwE;IACxE,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,UAAU,GAAG,IAAI,CAAA;KAAE,CAAC;CAC3C;AA8GH,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -3,26 +3,38 @@
|
|
|
3
3
|
// between server and client renders.
|
|
4
4
|
let nextId = 0;
|
|
5
5
|
|
|
6
|
-
export type ChartInspectorSection = 'type' | 'titles' | 'options' | 'legend' | 'axes' | 'series';
|
|
6
|
+
export type ChartInspectorSection = 'type' | 'style' | 'titles' | 'options' | 'legend' | 'axes' | 'series';
|
|
7
7
|
</script>
|
|
8
8
|
|
|
9
9
|
<script lang="ts">
|
|
10
10
|
import {
|
|
11
|
+
CHART_COLOR_SCHEMES,
|
|
11
12
|
CHART_CURVES,
|
|
13
|
+
CHART_LABEL_POSITIONS,
|
|
14
|
+
CHART_LAYOUT_PRESETS,
|
|
12
15
|
CHART_LEGEND_POSITIONS,
|
|
16
|
+
CHART_MARKERS,
|
|
13
17
|
CHART_NUMBER_STYLES,
|
|
14
18
|
CHART_SERIES_TYPES,
|
|
15
19
|
CHART_STACKINGS,
|
|
20
|
+
CHART_TRENDLINE_TYPES,
|
|
16
21
|
CHART_TYPE_LABELS,
|
|
17
22
|
CHART_TYPES,
|
|
23
|
+
checkChartColors,
|
|
18
24
|
chartColorInputValue,
|
|
25
|
+
chartFamily,
|
|
19
26
|
chartSeriesColor,
|
|
20
27
|
chartSeriesType,
|
|
28
|
+
chartSupportsTrendlines,
|
|
29
|
+
chartTrendlineName,
|
|
21
30
|
isCartesianChart,
|
|
22
31
|
type ChartAxis,
|
|
23
32
|
type ChartController,
|
|
33
|
+
type ChartLabelPosition,
|
|
34
|
+
type ChartMarker,
|
|
24
35
|
type ChartNumberFormat,
|
|
25
|
-
type ChartSeriesType
|
|
36
|
+
type ChartSeriesType,
|
|
37
|
+
type ChartTrendlineType
|
|
26
38
|
} from '@pixerate/editor/chart';
|
|
27
39
|
import ChartCommitInput from './ChartCommitInput.svelte';
|
|
28
40
|
import { chartStateOf } from './createChartEditor.svelte.js';
|
|
@@ -44,23 +56,36 @@
|
|
|
44
56
|
const selectedSeries = $derived(view.current.selectedSeries);
|
|
45
57
|
const show = (section: ChartInspectorSection) => !sections || sections.includes(section);
|
|
46
58
|
const cartesian = $derived(isCartesianChart(spec.type));
|
|
59
|
+
const family = $derived(chartFamily(spec));
|
|
60
|
+
const hasAxes = $derived(family === 'cartesian' || family === 'xy' || family === 'waterfall' || family === 'histogram' || family === 'box');
|
|
47
61
|
const types = $derived(spec.series.map((s) => chartSeriesType(spec, s)));
|
|
48
62
|
const hasBars = $derived(cartesian && types.includes('bar'));
|
|
49
63
|
const hasLines = $derived(cartesian && types.some((t) => t === 'line' || t === 'area'));
|
|
64
|
+
const usesSecondary = $derived(family === 'cartesian' && spec.series.some((s) => s.axis === 'secondary' && !s.hidden));
|
|
65
|
+
const colorCheck = $derived(checkChartColors(spec.series.filter((s) => !s.hidden).map((s) => chartSeriesColor(spec, spec.series.indexOf(s)))));
|
|
66
|
+
const currentScheme = $derived(CHART_COLOR_SCHEMES.find((c) => c.palette.join() === spec.palette.join())?.id ?? '');
|
|
50
67
|
const seriesIndex = $derived(selectedSeries ? spec.series.findIndex((s) => s.id === selectedSeries.id) : -1);
|
|
51
68
|
const horizontal = $derived(spec.orientation === 'horizontal');
|
|
52
69
|
const cap = (s: string) => s.charAt(0).toUpperCase() + s.slice(1);
|
|
53
70
|
const stackingLabels: Record<string, string> = { none: 'None', stacked: 'Stacked', percent: '100%' };
|
|
54
71
|
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
72
|
+
const keyOf = (axis: 'x' | 'y' | 'y2') => (axis === 'y2' ? 'yAxis2' : `${axis}Axis`);
|
|
73
|
+
const setAxis = (axis: 'x' | 'y' | 'y2', patch: Partial<ChartAxis>) => controller.setAxis(axis, patch);
|
|
74
|
+
const setFormat = (axis: 'x' | 'y' | 'y2', value: ChartAxis, patch: Partial<ChartNumberFormat>) => setAxis(axis, { format: { ...value.format, ...patch } });
|
|
75
|
+
const commitNumber = (axis: 'x' | 'y' | 'y2', key: 'min' | 'max' | 'step') => (text: string) => setAxis(axis, { [key]: text.trim() === '' ? null : Number(text) }).ok;
|
|
76
|
+
function commitDecimals(axis: 'x' | 'y' | 'y2', value: ChartAxis, text: string) {
|
|
77
|
+
const format = { ...value.format };
|
|
60
78
|
if (text.trim() === '') delete format.decimals;
|
|
61
79
|
else format.decimals = Number(text);
|
|
62
|
-
return setAxis(
|
|
80
|
+
return setAxis(axis, { format }).ok;
|
|
63
81
|
}
|
|
82
|
+
function commitCode(axis: 'x' | 'y' | 'y2', value: ChartAxis, code: string) {
|
|
83
|
+
const format = { ...value.format };
|
|
84
|
+
if (code.trim()) format.code = code.trim();
|
|
85
|
+
else delete format.code;
|
|
86
|
+
return setAxis(axis, { format }).ok;
|
|
87
|
+
}
|
|
88
|
+
const side = (axis: 'x' | 'y' | 'y2') => (axis === 'x' ? (horizontal ? 'left' : 'bottom') : axis === 'y2' ? (horizontal ? 'top' : 'right') : horizontal ? 'bottom' : 'left');
|
|
64
89
|
</script>
|
|
65
90
|
|
|
66
91
|
{#snippet toggle(label: string, name: string, checked: boolean, onchange: (checked: boolean) => void)}
|
|
@@ -70,39 +95,52 @@
|
|
|
70
95
|
</label>
|
|
71
96
|
{/snippet}
|
|
72
97
|
|
|
73
|
-
{#snippet axisSection(axis: 'x' | 'y', value: ChartAxis)}
|
|
98
|
+
{#snippet axisSection(axis: 'x' | 'y' | 'y2', value: ChartAxis, numeric: boolean)}
|
|
99
|
+
{@const key = keyOf(axis)}
|
|
100
|
+
{@const valueAxis = axis !== 'x' || numeric}
|
|
74
101
|
<div class={chartSectionClass} data-section="axis-{axis}">
|
|
75
102
|
<div class="flex items-center justify-between">
|
|
76
103
|
<h4 class="text-xs font-semibold text-foreground">
|
|
77
|
-
{axis === 'x' ? 'Category axis' : 'Value axis'}
|
|
78
|
-
<span class="ml-1 font-normal text-muted-foreground">({axis
|
|
104
|
+
{axis === 'x' ? (numeric ? 'X axis' : 'Category axis') : axis === 'y' ? 'Value axis' : 'Secondary axis'}
|
|
105
|
+
<span class="ml-1 font-normal text-muted-foreground">({side(axis)})</span>
|
|
79
106
|
</h4>
|
|
80
|
-
{@render toggle('Show', `${
|
|
107
|
+
{@render toggle('Show', `${key}.show`, value.show, (v) => setAxis(axis, { show: v }))}
|
|
81
108
|
</div>
|
|
82
109
|
<div class="flex items-center gap-2">
|
|
83
110
|
<label for="{idPrefix}-{axis}-title" class="{chartLabelClass} w-20 shrink-0">Title</label>
|
|
84
111
|
<div class="min-w-0 flex-1">
|
|
85
|
-
<ChartCommitInput id="{idPrefix}-{axis}-title" value={value.title} data-option="{
|
|
112
|
+
<ChartCommitInput id="{idPrefix}-{axis}-title" value={value.title} data-option="{key}.title" oncommit={(title) => void setAxis(axis, { title })} />
|
|
86
113
|
</div>
|
|
87
114
|
</div>
|
|
88
|
-
|
|
89
|
-
|
|
115
|
+
<div class="flex flex-wrap gap-x-4 gap-y-1">
|
|
116
|
+
{@render toggle('Grid lines', `${key}.gridlines`, value.gridlines, (v) => setAxis(axis, { gridlines: v }))}
|
|
117
|
+
{#if axis === 'x' && !numeric}
|
|
118
|
+
{@render toggle('Dates', 'xAxis.kind', value.kind === 'date', (dates) => setAxis('x', { kind: dates ? 'date' : undefined } as Partial<ChartAxis>))}
|
|
119
|
+
{/if}
|
|
120
|
+
</div>
|
|
121
|
+
{#if valueAxis}
|
|
122
|
+
<div class="flex items-center gap-2">
|
|
123
|
+
<label for="{idPrefix}-{axis}-step" class="{chartLabelClass} w-20 shrink-0">Step</label>
|
|
124
|
+
<div class="min-w-0 flex-1">
|
|
125
|
+
<ChartCommitInput id="{idPrefix}-{axis}-step" value={value.step === null ? '' : String(value.step)} placeholder="Auto" inputmode="decimal" data-option="{key}.step" oncommit={commitNumber(axis, 'step')} />
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
90
128
|
<div class="flex items-center gap-2">
|
|
91
129
|
<span class="{chartLabelClass} w-20 shrink-0">Min / max</span>
|
|
92
130
|
<div class="flex min-w-0 flex-1 gap-1">
|
|
93
|
-
<ChartCommitInput id="{idPrefix}-
|
|
94
|
-
<ChartCommitInput id="{idPrefix}-
|
|
131
|
+
<ChartCommitInput id="{idPrefix}-{axis}-min" value={value.min === null ? '' : String(value.min)} placeholder="Auto" inputmode="decimal" data-option="{key}.min" oncommit={commitNumber(axis, 'min')} />
|
|
132
|
+
<ChartCommitInput id="{idPrefix}-{axis}-max" value={value.max === null ? '' : String(value.max)} placeholder="Auto" inputmode="decimal" data-option="{key}.max" oncommit={commitNumber(axis, 'max')} />
|
|
95
133
|
</div>
|
|
96
134
|
</div>
|
|
97
135
|
<div class="flex items-center gap-2">
|
|
98
|
-
<label for="{idPrefix}-
|
|
136
|
+
<label for="{idPrefix}-{axis}-format" class="{chartLabelClass} w-20 shrink-0">Format</label>
|
|
99
137
|
<div class="flex min-w-0 flex-1 gap-1">
|
|
100
138
|
<select
|
|
101
|
-
id="{idPrefix}-
|
|
139
|
+
id="{idPrefix}-{axis}-format"
|
|
102
140
|
class={chartInputClass}
|
|
103
141
|
value={value.format.style ?? 'number'}
|
|
104
|
-
data-option="
|
|
105
|
-
onchange={(e) => setFormat({ style: e.currentTarget.value as ChartNumberFormat['style'] })}
|
|
142
|
+
data-option="{key}.format.style"
|
|
143
|
+
onchange={(e) => setFormat(axis, value, { style: e.currentTarget.value as ChartNumberFormat['style'] })}
|
|
106
144
|
>
|
|
107
145
|
{#each CHART_NUMBER_STYLES as style (style)}
|
|
108
146
|
<option value={style}>{cap(style)}</option>
|
|
@@ -114,20 +152,26 @@
|
|
|
114
152
|
aria-label="Decimals"
|
|
115
153
|
title="Decimals"
|
|
116
154
|
inputmode="numeric"
|
|
117
|
-
data-option="
|
|
155
|
+
data-option="{key}.format.decimals"
|
|
118
156
|
class="{chartInputClass} w-14 shrink-0"
|
|
119
|
-
oncommit={commitDecimals}
|
|
157
|
+
oncommit={(text) => commitDecimals(axis, value, text)}
|
|
120
158
|
/>
|
|
121
159
|
</div>
|
|
122
160
|
</div>
|
|
123
161
|
{#if value.format.style === 'currency'}
|
|
124
162
|
<div class="flex items-center gap-2">
|
|
125
|
-
<label for="{idPrefix}-
|
|
163
|
+
<label for="{idPrefix}-{axis}-currency" class="{chartLabelClass} w-20 shrink-0">Currency</label>
|
|
126
164
|
<div class="min-w-0 flex-1">
|
|
127
|
-
<ChartCommitInput id="{idPrefix}-
|
|
165
|
+
<ChartCommitInput id="{idPrefix}-{axis}-currency" value={value.format.currency ?? 'USD'} data-option="{key}.format.currency" oncommit={(c) => void setFormat(axis, value, { currency: c.toUpperCase() })} />
|
|
128
166
|
</div>
|
|
129
167
|
</div>
|
|
130
168
|
{/if}
|
|
169
|
+
<div class="flex items-center gap-2">
|
|
170
|
+
<label for="{idPrefix}-{axis}-code" class="{chartLabelClass} w-20 shrink-0">Code</label>
|
|
171
|
+
<div class="min-w-0 flex-1">
|
|
172
|
+
<ChartCommitInput id="{idPrefix}-{axis}-code" value={value.format.code ?? ''} placeholder={'e.g. #,##0.0,,"M"'} data-option="{key}.format.code" oncommit={(code) => commitCode(axis, value, code)} />
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
131
175
|
{/if}
|
|
132
176
|
</div>
|
|
133
177
|
{/snippet}
|
|
@@ -145,6 +189,33 @@
|
|
|
145
189
|
</div>
|
|
146
190
|
{/if}
|
|
147
191
|
|
|
192
|
+
{#if show('style')}
|
|
193
|
+
<div class={chartSectionClass} data-section="style">
|
|
194
|
+
<div class="flex items-center gap-2">
|
|
195
|
+
<label for="{idPrefix}-layout" class="{chartLabelClass} w-20 shrink-0">Layout</label>
|
|
196
|
+
<select id="{idPrefix}-layout" class={chartInputClass} value="" data-option="preset.layout" onchange={(e) => e.currentTarget.value && controller.applyPreset({ layout: e.currentTarget.value })}>
|
|
197
|
+
<option value="">Quick layout…</option>
|
|
198
|
+
{#each CHART_LAYOUT_PRESETS as p (p.id)}
|
|
199
|
+
<option value={p.id} title={p.description}>{p.label}</option>
|
|
200
|
+
{/each}
|
|
201
|
+
</select>
|
|
202
|
+
</div>
|
|
203
|
+
<div class="flex items-center gap-2">
|
|
204
|
+
<label for="{idPrefix}-colors" class="{chartLabelClass} w-20 shrink-0">Colours</label>
|
|
205
|
+
<select id="{idPrefix}-colors" class={chartInputClass} value={currentScheme} data-option="preset.colors" onchange={(e) => e.currentTarget.value && controller.applyPreset({ colors: e.currentTarget.value })}>
|
|
206
|
+
<option value="">Custom</option>
|
|
207
|
+
{#each CHART_COLOR_SCHEMES as c (c.id)}
|
|
208
|
+
<option value={c.id} title={c.description}>{c.label}</option>
|
|
209
|
+
{/each}
|
|
210
|
+
</select>
|
|
211
|
+
</div>
|
|
212
|
+
{#if !colorCheck.ok}
|
|
213
|
+
<p class="text-[11px] leading-snug text-amber-600 dark:text-amber-400" data-chart-color-warning="">{colorCheck.issues[0]}</p>
|
|
214
|
+
{/if}
|
|
215
|
+
<button type="button" class={chartButtonClass} data-action="transpose" onclick={() => controller.transpose()}>Switch rows and columns</button>
|
|
216
|
+
</div>
|
|
217
|
+
{/if}
|
|
218
|
+
|
|
148
219
|
{#if show('titles')}
|
|
149
220
|
<div class={chartSectionClass} data-section="titles">
|
|
150
221
|
<div class="flex items-center gap-2">
|
|
@@ -191,6 +262,32 @@
|
|
|
191
262
|
{/each}
|
|
192
263
|
</div>
|
|
193
264
|
</div>
|
|
265
|
+
<div class="flex items-center gap-2">
|
|
266
|
+
<label for="{idPrefix}-gap" class="{chartLabelClass} w-20 shrink-0">Spacing</label>
|
|
267
|
+
<input
|
|
268
|
+
id="{idPrefix}-gap"
|
|
269
|
+
type="range"
|
|
270
|
+
min="0"
|
|
271
|
+
max="0.9"
|
|
272
|
+
step="0.02"
|
|
273
|
+
value={spec.gapWidth}
|
|
274
|
+
data-option="gapWidth"
|
|
275
|
+
class="w-full accent-primary"
|
|
276
|
+
onpointerdown={() => controller.beginGesture('gapWidth')}
|
|
277
|
+
onpointerup={() => controller.endGesture()}
|
|
278
|
+
oninput={(e) => controller.update({ gapWidth: Number(e.currentTarget.value) })}
|
|
279
|
+
/>
|
|
280
|
+
</div>
|
|
281
|
+
{/if}
|
|
282
|
+
{#if spec.dataLabels && hasBars}
|
|
283
|
+
<div class="flex items-center gap-2">
|
|
284
|
+
<label for="{idPrefix}-label-position" class="{chartLabelClass} w-20 shrink-0">Labels</label>
|
|
285
|
+
<select id="{idPrefix}-label-position" class={chartInputClass} value={spec.labelPosition} data-option="labelPosition" onchange={(e) => controller.update({ labelPosition: e.currentTarget.value as ChartLabelPosition })}>
|
|
286
|
+
{#each CHART_LABEL_POSITIONS as p (p)}
|
|
287
|
+
<option value={p}>{cap(p)}</option>
|
|
288
|
+
{/each}
|
|
289
|
+
</select>
|
|
290
|
+
</div>
|
|
194
291
|
{/if}
|
|
195
292
|
{#if hasLines}
|
|
196
293
|
<div class="flex items-center gap-2">
|
|
@@ -202,6 +299,14 @@
|
|
|
202
299
|
</select>
|
|
203
300
|
</div>
|
|
204
301
|
{/if}
|
|
302
|
+
{#if spec.type === 'histogram'}
|
|
303
|
+
<div class="flex items-center gap-2">
|
|
304
|
+
<label for="{idPrefix}-bins" class="{chartLabelClass} w-20 shrink-0">Bins</label>
|
|
305
|
+
<div class="min-w-0 flex-1">
|
|
306
|
+
<ChartCommitInput id="{idPrefix}-bins" value={spec.bins === null ? '' : String(spec.bins)} placeholder="Auto" inputmode="numeric" data-option="bins" oncommit={(text) => controller.update({ bins: text.trim() === '' ? null : Number(text) }).ok} />
|
|
307
|
+
</div>
|
|
308
|
+
</div>
|
|
309
|
+
{/if}
|
|
205
310
|
{#if spec.type === 'donut'}
|
|
206
311
|
<div class="flex items-center gap-2">
|
|
207
312
|
<label for="{idPrefix}-hole" class="{chartLabelClass} w-20 shrink-0">Hole</label>
|
|
@@ -221,6 +326,9 @@
|
|
|
221
326
|
</div>
|
|
222
327
|
{/if}
|
|
223
328
|
<div class="flex flex-wrap gap-x-4 gap-y-1">
|
|
329
|
+
{#if spec.type === 'waterfall'}
|
|
330
|
+
{@render toggle('Total bar', 'showTotal', spec.showTotal, (showTotal) => controller.update({ showTotal }))}
|
|
331
|
+
{/if}
|
|
224
332
|
{#if hasLines}
|
|
225
333
|
{@render toggle('Points', 'points', spec.points, (points) => controller.update({ points }))}
|
|
226
334
|
{/if}
|
|
@@ -247,13 +355,19 @@
|
|
|
247
355
|
</div>
|
|
248
356
|
{/if}
|
|
249
357
|
|
|
250
|
-
{#if show('axes') &&
|
|
251
|
-
{
|
|
252
|
-
|
|
358
|
+
{#if show('axes') && hasAxes}
|
|
359
|
+
{#if family !== 'box' && family !== 'histogram'}
|
|
360
|
+
{@render axisSection('x', spec.xAxis, family === 'xy')}
|
|
361
|
+
{/if}
|
|
362
|
+
{@render axisSection('y', spec.yAxis, false)}
|
|
363
|
+
{#if usesSecondary}
|
|
364
|
+
{@render axisSection('y2', spec.yAxis2, false)}
|
|
365
|
+
{/if}
|
|
253
366
|
{/if}
|
|
254
367
|
|
|
255
368
|
{#if show('series') && selectedSeries}
|
|
256
369
|
{@const series = selectedSeries}
|
|
370
|
+
{@const drawnAs = chartSeriesType(spec, series)}
|
|
257
371
|
<div class={chartSectionClass} data-section="series" data-series-id={series.id}>
|
|
258
372
|
<h4 class="text-xs font-semibold text-foreground">Series</h4>
|
|
259
373
|
<div class="flex items-center gap-2">
|
|
@@ -297,8 +411,51 @@
|
|
|
297
411
|
</select>
|
|
298
412
|
</div>
|
|
299
413
|
{/if}
|
|
414
|
+
{#if (cartesian || family === 'xy') && (drawnAs === 'line' || drawnAs === 'area' || drawnAs === 'scatter')}
|
|
415
|
+
<div class="flex items-center gap-2">
|
|
416
|
+
<label for="{idPrefix}-series-marker" class="{chartLabelClass} w-20 shrink-0">Marker</label>
|
|
417
|
+
<select
|
|
418
|
+
id="{idPrefix}-series-marker"
|
|
419
|
+
class={chartInputClass}
|
|
420
|
+
value={series.marker ?? ''}
|
|
421
|
+
data-option="series.marker"
|
|
422
|
+
onchange={(e) => controller.updateSeries(series.id, { marker: (e.currentTarget.value || null) as ChartMarker | null })}
|
|
423
|
+
>
|
|
424
|
+
<option value="">Default</option>
|
|
425
|
+
{#each CHART_MARKERS as m (m)}
|
|
426
|
+
<option value={m}>{cap(m)}</option>
|
|
427
|
+
{/each}
|
|
428
|
+
</select>
|
|
429
|
+
</div>
|
|
430
|
+
{/if}
|
|
431
|
+
{#if chartSupportsTrendlines(spec.type, spec.stacking)}
|
|
432
|
+
<div class="space-y-1" data-section="trendlines">
|
|
433
|
+
{#each series.trendlines ?? [] as t, i (i)}
|
|
434
|
+
<div class="flex items-center gap-1" data-trendline={i}>
|
|
435
|
+
<span class="flex-1 truncate text-muted-foreground">{chartTrendlineName(t, series.name)}</span>
|
|
436
|
+
{@render toggle('R²', `trendline.${i}.showR2`, !!t.showR2, (showR2) => controller.updateTrendline(series.id, i, { showR2 }))}
|
|
437
|
+
<button type="button" class={chartButtonClass} aria-label="Delete trendline" data-action="trendline-delete" onclick={() => controller.removeTrendline(series.id, i)}>×</button>
|
|
438
|
+
</div>
|
|
439
|
+
{/each}
|
|
440
|
+
<select
|
|
441
|
+
class={chartInputClass}
|
|
442
|
+
value=""
|
|
443
|
+
aria-label="Add trendline"
|
|
444
|
+
data-option="trendline.add"
|
|
445
|
+
onchange={(e) => e.currentTarget.value && controller.addTrendline(series.id, { type: e.currentTarget.value as ChartTrendlineType })}
|
|
446
|
+
>
|
|
447
|
+
<option value="">Add trendline…</option>
|
|
448
|
+
{#each CHART_TRENDLINE_TYPES as t (t)}
|
|
449
|
+
<option value={t}>{cap(t.replace('-', ' '))}</option>
|
|
450
|
+
{/each}
|
|
451
|
+
</select>
|
|
452
|
+
</div>
|
|
453
|
+
{/if}
|
|
300
454
|
<div class="flex flex-wrap items-center gap-1">
|
|
301
455
|
{@render toggle('Hidden', 'series.hidden', !!series.hidden, (hidden) => controller.updateSeries(series.id, { hidden }))}
|
|
456
|
+
{#if family === 'cartesian'}
|
|
457
|
+
{@render toggle('Secondary axis', 'series.axis', series.axis === 'secondary', (secondary) => controller.updateSeries(series.id, { axis: secondary ? 'secondary' : null }))}
|
|
458
|
+
{/if}
|
|
302
459
|
<span class="flex-1"></span>
|
|
303
460
|
<button type="button" class={chartButtonClass} disabled={seriesIndex <= 0} aria-label="Move series earlier" data-action="series-up" onclick={() => controller.moveSeries(series.id, seriesIndex - 1)}>↑</button>
|
|
304
461
|
<button
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type ChartInspectorSection = 'type' | 'titles' | 'options' | 'legend' | 'axes' | 'series';
|
|
1
|
+
export type ChartInspectorSection = 'type' | 'style' | 'titles' | 'options' | 'legend' | 'axes' | 'series';
|
|
2
2
|
import { type ChartController } from '@pixerate/editor/chart';
|
|
3
3
|
interface Props {
|
|
4
4
|
controller: ChartController;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChartInspector.svelte.d.ts","sourceRoot":"","sources":["../../src/chart/ChartInspector.svelte.ts"],"names":[],"mappings":"AAOE,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"ChartInspector.svelte.d.ts","sourceRoot":"","sources":["../../src/chart/ChartInspector.svelte.ts"],"names":[],"mappings":"AAOE,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;AAG7G,OAAO,EAsBH,KAAK,eAAe,EAMrB,MAAM,wBAAwB,CAAC;AAMhC,UAAU,KAAK;IACb,UAAU,EAAE,eAAe,CAAC;IAC5B,qEAAqE;IACrE,QAAQ,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAC;IAC5C,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAoWH,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { chartTextBaseline, chartTextTransform, type ChartMark } from '@pixerate/editor/chart';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
marks: readonly ChartMark[];
|
|
6
|
+
fontFamily: string;
|
|
7
|
+
/** Marks with targets get a pointer cursor and receive events. */
|
|
8
|
+
interactive?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let { marks, fontFamily, interactive = false }: Props = $props();
|
|
12
|
+
|
|
13
|
+
const pointerEvents = (mark: ChartMark) => (mark.role === 'selection' || mark.role === 'drag-readout' || (!mark.target && mark.role !== 'background') ? 'none' : undefined);
|
|
14
|
+
const cursor = (mark: ChartMark) => (interactive && mark.target && mark.role !== 'background' ? 'pointer' : undefined);
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
{#each marks as mark (mark.key)}
|
|
18
|
+
{#if mark.kind === 'rect'}
|
|
19
|
+
<rect
|
|
20
|
+
data-role={mark.role}
|
|
21
|
+
data-chart-key={mark.target ? mark.key : undefined}
|
|
22
|
+
data-selected={mark.selected ? '' : undefined}
|
|
23
|
+
x={mark.x}
|
|
24
|
+
y={mark.y}
|
|
25
|
+
width={mark.width}
|
|
26
|
+
height={mark.height}
|
|
27
|
+
rx={mark.radius}
|
|
28
|
+
opacity={mark.opacity}
|
|
29
|
+
pointer-events={pointerEvents(mark)}
|
|
30
|
+
cursor={cursor(mark)}
|
|
31
|
+
style:fill={mark.fill}
|
|
32
|
+
style:stroke={mark.stroke}
|
|
33
|
+
style:stroke-width={mark.strokeWidth}
|
|
34
|
+
/>
|
|
35
|
+
{:else if mark.kind === 'path'}
|
|
36
|
+
<path
|
|
37
|
+
data-role={mark.role}
|
|
38
|
+
data-chart-key={mark.target ? mark.key : undefined}
|
|
39
|
+
data-selected={mark.selected ? '' : undefined}
|
|
40
|
+
d={mark.d}
|
|
41
|
+
stroke-linejoin="round"
|
|
42
|
+
stroke-linecap="round"
|
|
43
|
+
stroke-dasharray={mark.dash}
|
|
44
|
+
opacity={mark.opacity}
|
|
45
|
+
pointer-events={pointerEvents(mark)}
|
|
46
|
+
cursor={cursor(mark)}
|
|
47
|
+
style:fill={mark.fill ?? 'none'}
|
|
48
|
+
style:stroke={mark.stroke}
|
|
49
|
+
style:stroke-width={mark.strokeWidth}
|
|
50
|
+
/>
|
|
51
|
+
{:else if mark.kind === 'circle'}
|
|
52
|
+
<circle
|
|
53
|
+
data-role={mark.role}
|
|
54
|
+
data-chart-key={mark.target ? mark.key : undefined}
|
|
55
|
+
data-selected={mark.selected ? '' : undefined}
|
|
56
|
+
cx={mark.cx}
|
|
57
|
+
cy={mark.cy}
|
|
58
|
+
r={mark.r}
|
|
59
|
+
opacity={mark.opacity}
|
|
60
|
+
pointer-events={pointerEvents(mark)}
|
|
61
|
+
cursor={cursor(mark)}
|
|
62
|
+
style:fill={mark.fill}
|
|
63
|
+
style:stroke={mark.stroke}
|
|
64
|
+
style:stroke-width={mark.strokeWidth}
|
|
65
|
+
/>
|
|
66
|
+
{:else if mark.kind === 'line'}
|
|
67
|
+
<line
|
|
68
|
+
data-role={mark.role}
|
|
69
|
+
data-chart-key={mark.target ? mark.key : undefined}
|
|
70
|
+
data-selected={mark.selected ? '' : undefined}
|
|
71
|
+
x1={mark.x1}
|
|
72
|
+
y1={mark.y1}
|
|
73
|
+
x2={mark.x2}
|
|
74
|
+
y2={mark.y2}
|
|
75
|
+
stroke-dasharray={mark.dash}
|
|
76
|
+
opacity={mark.opacity}
|
|
77
|
+
pointer-events={pointerEvents(mark)}
|
|
78
|
+
cursor={cursor(mark)}
|
|
79
|
+
style:stroke={mark.stroke}
|
|
80
|
+
style:stroke-width={mark.strokeWidth}
|
|
81
|
+
/>
|
|
82
|
+
{:else}
|
|
83
|
+
<text
|
|
84
|
+
data-role={mark.role}
|
|
85
|
+
data-chart-key={mark.target ? mark.key : undefined}
|
|
86
|
+
data-selected={mark.selected ? '' : undefined}
|
|
87
|
+
x={mark.x}
|
|
88
|
+
y={mark.y}
|
|
89
|
+
text-anchor={mark.anchor}
|
|
90
|
+
dominant-baseline={chartTextBaseline(mark.baseline)}
|
|
91
|
+
transform={chartTextTransform(mark)}
|
|
92
|
+
opacity={mark.opacity}
|
|
93
|
+
pointer-events={pointerEvents(mark)}
|
|
94
|
+
cursor={cursor(mark)}
|
|
95
|
+
style:fill={mark.fill}
|
|
96
|
+
style:font-size="{mark.size}px"
|
|
97
|
+
style:font-weight={mark.weight}
|
|
98
|
+
style:font-family={fontFamily}
|
|
99
|
+
style:user-select="none">{mark.text}</text
|
|
100
|
+
>
|
|
101
|
+
{/if}
|
|
102
|
+
{/each}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ChartMark } from '@pixerate/editor/chart';
|
|
2
|
+
interface Props {
|
|
3
|
+
marks: readonly ChartMark[];
|
|
4
|
+
fontFamily: string;
|
|
5
|
+
/** Marks with targets get a pointer cursor and receive events. */
|
|
6
|
+
interactive?: boolean;
|
|
7
|
+
}
|
|
8
|
+
declare const ChartMarks: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type ChartMarks = ReturnType<typeof ChartMarks>;
|
|
10
|
+
export default ChartMarks;
|
|
11
|
+
//# sourceMappingURL=ChartMarks.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChartMarks.svelte.d.ts","sourceRoot":"","sources":["../../src/chart/ChartMarks.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAAyC,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAG7F,UAAU,KAAK;IACb,KAAK,EAAE,SAAS,SAAS,EAAE,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AA2BH,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ChartPresence, ChartPresenceState } from '@pixerate/editor/chart';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
/** Agent cursors to draw (see `ChartDirector`). */
|
|
6
|
+
presence: ChartPresence;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let { presence }: Props = $props();
|
|
10
|
+
|
|
11
|
+
let current: ChartPresenceState = $state.raw({ actors: [] });
|
|
12
|
+
$effect(() => {
|
|
13
|
+
current = presence.getState();
|
|
14
|
+
return presence.subscribe((next) => {
|
|
15
|
+
current = next;
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<g data-chart-presence="" pointer-events="none">
|
|
21
|
+
{#each current.actors as a (a.id)}
|
|
22
|
+
<g transform="translate({a.x} {a.y})" data-actor={a.id}>
|
|
23
|
+
<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} style:stroke="#ffffff" style:stroke-width="1" transform={a.pressed ? 'scale(0.9)' : undefined} />
|
|
24
|
+
<g transform="translate(13 16)">
|
|
25
|
+
<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} />
|
|
26
|
+
<text x="6" y="9" dominant-baseline="central" style:fill="#ffffff" style:font-size="11px" style:font-weight="600" style:font-family="inherit">{a.message ?? a.name}</text>
|
|
27
|
+
</g>
|
|
28
|
+
</g>
|
|
29
|
+
{/each}
|
|
30
|
+
</g>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ChartPresence } from '@pixerate/editor/chart';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Agent cursors to draw (see `ChartDirector`). */
|
|
4
|
+
presence: ChartPresence;
|
|
5
|
+
}
|
|
6
|
+
declare const ChartPresenceLayer: import("svelte").Component<Props, {}, "">;
|
|
7
|
+
type ChartPresenceLayer = ReturnType<typeof ChartPresenceLayer>;
|
|
8
|
+
export default ChartPresenceLayer;
|
|
9
|
+
//# sourceMappingURL=ChartPresenceLayer.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChartPresenceLayer.svelte.d.ts","sourceRoot":"","sources":["../../src/chart/ChartPresenceLayer.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAsB,MAAM,wBAAwB,CAAC;AAG9E,UAAU,KAAK;IACb,mDAAmD;IACnD,QAAQ,EAAE,aAAa,CAAC;CACzB;AA8BH,QAAA,MAAM,kBAAkB,2CAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ChartScene } from '@pixerate/editor/chart';
|
|
3
|
+
import ChartMarks from './ChartMarks.svelte';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
/** A laid-out scene (`layoutChart`, `layoutSparkline`, `blendChartScenes`). */
|
|
7
|
+
scene: ChartScene;
|
|
8
|
+
label?: string;
|
|
9
|
+
class?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let { scene, label, class: className = '' }: Props = $props();
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<svg width={scene.width} height={scene.height} viewBox="0 0 {scene.width} {scene.height}" role="img" aria-label={label} class="block max-w-full {className}">
|
|
16
|
+
<ChartMarks marks={scene.marks} fontFamily={scene.theme.fontFamily} />
|
|
17
|
+
</svg>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ChartScene } from '@pixerate/editor/chart';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** A laid-out scene (`layoutChart`, `layoutSparkline`, `blendChartScenes`). */
|
|
4
|
+
scene: ChartScene;
|
|
5
|
+
label?: string;
|
|
6
|
+
class?: string;
|
|
7
|
+
}
|
|
8
|
+
declare const ChartSceneSvg: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type ChartSceneSvg = ReturnType<typeof ChartSceneSvg>;
|
|
10
|
+
export default ChartSceneSvg;
|
|
11
|
+
//# sourceMappingURL=ChartSceneSvg.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChartSceneSvg.svelte.d.ts","sourceRoot":"","sources":["../../src/chart/ChartSceneSvg.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAIvD,UAAU,KAAK;IACb,+EAA+E;IAC/E,KAAK,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAeH,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { layoutSparkline, type ChartSparklineKind, type ChartValue } from '@pixerate/editor/chart';
|
|
3
|
+
import ChartSceneSvg from './ChartSceneSvg.svelte';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
values: readonly ChartValue[];
|
|
7
|
+
kind?: ChartSparklineKind;
|
|
8
|
+
width?: number;
|
|
9
|
+
height?: number;
|
|
10
|
+
color?: string;
|
|
11
|
+
negativeColor?: string;
|
|
12
|
+
markers?: boolean;
|
|
13
|
+
highLow?: boolean;
|
|
14
|
+
min?: number;
|
|
15
|
+
max?: number;
|
|
16
|
+
/** Accessible label (default: first and last value). */
|
|
17
|
+
label?: string;
|
|
18
|
+
class?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let { values, kind = 'line', width = 80, height = 20, color, negativeColor, markers, highLow, min, max, label, class: className = '' }: Props = $props();
|
|
22
|
+
|
|
23
|
+
const scene = $derived(layoutSparkline(values, { kind, width, height, color, negativeColor, markers, highLow, min, max }));
|
|
24
|
+
const present = $derived(values.filter((v): v is number => v !== null));
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<ChartSceneSvg {scene} label={label ?? (present.length ? `Trend from ${present[0]} to ${present[present.length - 1]}` : 'No data')} class="inline-block align-middle {className}" />
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ChartSparklineKind, type ChartValue } from '@pixerate/editor/chart';
|
|
2
|
+
interface Props {
|
|
3
|
+
values: readonly ChartValue[];
|
|
4
|
+
kind?: ChartSparklineKind;
|
|
5
|
+
width?: number;
|
|
6
|
+
height?: number;
|
|
7
|
+
color?: string;
|
|
8
|
+
negativeColor?: string;
|
|
9
|
+
markers?: boolean;
|
|
10
|
+
highLow?: boolean;
|
|
11
|
+
min?: number;
|
|
12
|
+
max?: number;
|
|
13
|
+
/** Accessible label (default: first and last value). */
|
|
14
|
+
label?: string;
|
|
15
|
+
class?: string;
|
|
16
|
+
}
|
|
17
|
+
declare const ChartSparkline: import("svelte").Component<Props, {}, "">;
|
|
18
|
+
type ChartSparkline = ReturnType<typeof ChartSparkline>;
|
|
19
|
+
export default ChartSparkline;
|
|
20
|
+
//# sourceMappingURL=ChartSparkline.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChartSparkline.svelte.d.ts","sourceRoot":"","sources":["../../src/chart/ChartSparkline.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EAAmB,KAAK,kBAAkB,EAAE,KAAK,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAIjG,UAAU,KAAK;IACb,MAAM,EAAE,SAAS,UAAU,EAAE,CAAC;IAC9B,IAAI,CAAC,EAAE,kBAAkB,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAgBH,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onDestroy, untrack } from 'svelte';
|
|
3
|
+
import {
|
|
4
|
+
CSS_VARIABLE_CHART_THEME,
|
|
5
|
+
ChartStream,
|
|
6
|
+
blendChartScenes,
|
|
7
|
+
easeChartTween,
|
|
8
|
+
layoutChart,
|
|
9
|
+
type ChartIssue,
|
|
10
|
+
type ChartScene,
|
|
11
|
+
type ChartStreamFormat,
|
|
12
|
+
type ChartTheme
|
|
13
|
+
} from '@pixerate/editor/chart';
|
|
14
|
+
import type { Snippet } from 'svelte';
|
|
15
|
+
import ChartSceneSvg from './ChartSceneSvg.svelte';
|
|
16
|
+
|
|
17
|
+
interface Props {
|
|
18
|
+
/** Mermaid (xychart-beta, pie, radar-beta) or a JSON chart spec, as far as it has arrived. */
|
|
19
|
+
source: string;
|
|
20
|
+
/** Nothing more will arrive: the final chart is drawn at once and problems are reported. */
|
|
21
|
+
complete?: boolean;
|
|
22
|
+
format?: ChartStreamFormat;
|
|
23
|
+
width?: number;
|
|
24
|
+
height?: number;
|
|
25
|
+
theme?: Partial<ChartTheme>;
|
|
26
|
+
/** How often a new layout is adopted while streaming (ms, default 300). */
|
|
27
|
+
adoptInterval?: number;
|
|
28
|
+
/** Tween length between layouts (ms, default 450; 0 to switch at once). */
|
|
29
|
+
tween?: number;
|
|
30
|
+
/** Shown before anything can be drawn. */
|
|
31
|
+
placeholder?: Snippet;
|
|
32
|
+
/** Called with the parse problems once `complete`. */
|
|
33
|
+
onissues?: (issues: ChartIssue[]) => void;
|
|
34
|
+
class?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let { source, complete = false, format = 'auto', width = 640, height = 360, theme, adoptInterval = 300, tween = 450, placeholder, onissues, class: className = '' }: Props = $props();
|
|
38
|
+
|
|
39
|
+
const stream = $derived(new ChartStream(format));
|
|
40
|
+
const fullTheme = $derived({ ...CSS_VARIABLE_CHART_THEME, ...theme });
|
|
41
|
+
let scene: ChartScene | null = $state.raw(null);
|
|
42
|
+
let lastAdopt = 0;
|
|
43
|
+
let frame: number | null = null;
|
|
44
|
+
let pending: ReturnType<typeof setTimeout> | null = null;
|
|
45
|
+
|
|
46
|
+
function adopt(src: string, done: boolean) {
|
|
47
|
+
const read = stream.update(src, done);
|
|
48
|
+
if (done) onissues?.(read.issues);
|
|
49
|
+
if (!read.spec) return;
|
|
50
|
+
const next = layoutChart(read.spec, { width, height, theme: fullTheme, valueDomain: read.valueDomain });
|
|
51
|
+
const from = untrack(() => scene);
|
|
52
|
+
lastAdopt = Date.now();
|
|
53
|
+
if (frame !== null) cancelAnimationFrame(frame);
|
|
54
|
+
if (!from || tween <= 0 || typeof requestAnimationFrame === 'undefined') {
|
|
55
|
+
scene = next;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const start = performance.now();
|
|
59
|
+
const step = () => {
|
|
60
|
+
const t = easeChartTween((performance.now() - start) / tween);
|
|
61
|
+
scene = blendChartScenes(from, next, t);
|
|
62
|
+
frame = t < 1 ? requestAnimationFrame(step) : null;
|
|
63
|
+
};
|
|
64
|
+
frame = requestAnimationFrame(step);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
$effect(() => {
|
|
68
|
+
const src = source;
|
|
69
|
+
const done = complete;
|
|
70
|
+
if (pending) clearTimeout(pending);
|
|
71
|
+
const wait = done ? 0 : Math.max(0, adoptInterval - (Date.now() - lastAdopt));
|
|
72
|
+
if (wait === 0) adopt(src, done);
|
|
73
|
+
else pending = setTimeout(() => adopt(src, done), wait);
|
|
74
|
+
return () => {
|
|
75
|
+
if (pending) clearTimeout(pending);
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
onDestroy(() => {
|
|
80
|
+
if (frame !== null) cancelAnimationFrame(frame);
|
|
81
|
+
});
|
|
82
|
+
</script>
|
|
83
|
+
|
|
84
|
+
<div class={className} data-chart-streaming={complete ? 'complete' : 'streaming'} aria-busy={!complete}>
|
|
85
|
+
{#if scene}
|
|
86
|
+
<ChartSceneSvg {scene} label={complete ? undefined : 'Chart loading'} />
|
|
87
|
+
{:else if placeholder}
|
|
88
|
+
{@render placeholder()}
|
|
89
|
+
{:else}
|
|
90
|
+
<div style:height="{height}px" class="flex items-center justify-center text-xs text-muted-foreground">Drawing chart…</div>
|
|
91
|
+
{/if}
|
|
92
|
+
</div>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type ChartIssue, type ChartStreamFormat, type ChartTheme } from '@pixerate/editor/chart';
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
interface Props {
|
|
4
|
+
/** Mermaid (xychart-beta, pie, radar-beta) or a JSON chart spec, as far as it has arrived. */
|
|
5
|
+
source: string;
|
|
6
|
+
/** Nothing more will arrive: the final chart is drawn at once and problems are reported. */
|
|
7
|
+
complete?: boolean;
|
|
8
|
+
format?: ChartStreamFormat;
|
|
9
|
+
width?: number;
|
|
10
|
+
height?: number;
|
|
11
|
+
theme?: Partial<ChartTheme>;
|
|
12
|
+
/** How often a new layout is adopted while streaming (ms, default 300). */
|
|
13
|
+
adoptInterval?: number;
|
|
14
|
+
/** Tween length between layouts (ms, default 450; 0 to switch at once). */
|
|
15
|
+
tween?: number;
|
|
16
|
+
/** Shown before anything can be drawn. */
|
|
17
|
+
placeholder?: Snippet;
|
|
18
|
+
/** Called with the parse problems once `complete`. */
|
|
19
|
+
onissues?: (issues: ChartIssue[]) => void;
|
|
20
|
+
class?: string;
|
|
21
|
+
}
|
|
22
|
+
declare const ChartStreamingView: import("svelte").Component<Props, {}, "">;
|
|
23
|
+
type ChartStreamingView = ReturnType<typeof ChartStreamingView>;
|
|
24
|
+
export default ChartStreamingView;
|
|
25
|
+
//# sourceMappingURL=ChartStreamingView.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChartStreamingView.svelte.d.ts","sourceRoot":"","sources":["../../src/chart/ChartStreamingView.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,EAMH,KAAK,UAAU,EAEf,KAAK,iBAAiB,EACtB,KAAK,UAAU,EAChB,MAAM,wBAAwB,CAAC;AAClC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAIpC,UAAU,KAAK;IACb,8FAA8F;IAC9F,MAAM,EAAE,MAAM,CAAC;IACf,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAC5B,2EAA2E;IAC3E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,sDAAsD;IACtD,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,IAAI,CAAC;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAmEH,QAAA,MAAM,kBAAkB,2CAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { ToolRegistry } from "@pixerate/editor/tools";
|
|
2
|
+
import { ChartController, ChartDirector, ChartPresence, type ChartScene, type ChartToolProviderOptions, type ChartActionResult, type ChartCommandOptions, type ChartControllerOptions, type ChartSelection, type ChartSpecPatch, type ChartState, type ChartType } from "@pixerate/editor/chart";
|
|
2
3
|
export interface SvelteChartEditor {
|
|
3
4
|
readonly controller: ChartController;
|
|
4
5
|
/** The controller's state (reactive). */
|
|
@@ -35,4 +36,27 @@ export interface ChartCommandTarget {
|
|
|
35
36
|
* initialisation). Pass a getter to follow a changing registry.
|
|
36
37
|
*/
|
|
37
38
|
export declare function useChartCommands(registry: ChartCommandTarget | null | undefined | (() => ChartCommandTarget | null | undefined), controller: ChartController, options?: ChartCommandOptions): void;
|
|
39
|
+
/**
|
|
40
|
+
* Registers the chart's agent tools (`createChartToolProvider`) with a `ToolRegistry` for the
|
|
41
|
+
* lifetime of the calling component. Pass a getter to follow a changing registry.
|
|
42
|
+
*/
|
|
43
|
+
export declare function useChartToolProvider(registry: ToolRegistry | null | undefined | (() => ToolRegistry | null | undefined), controller: ChartController, options?: ChartToolProviderOptions): void;
|
|
44
|
+
/**
|
|
45
|
+
* A `ChartPresence` and `ChartDirector` for a chart: pass `presence` and `sceneRef` to
|
|
46
|
+
* `ChartCanvas`, and `director` to `useChartToolProvider` so agent edits are acted out with a
|
|
47
|
+
* cursor.
|
|
48
|
+
*/
|
|
49
|
+
export declare function createChartDirector(controller: ChartController, options?: {
|
|
50
|
+
speed?: number;
|
|
51
|
+
size?: {
|
|
52
|
+
width: number;
|
|
53
|
+
height: number;
|
|
54
|
+
};
|
|
55
|
+
}): {
|
|
56
|
+
presence: ChartPresence;
|
|
57
|
+
director: ChartDirector;
|
|
58
|
+
sceneRef: {
|
|
59
|
+
current: ChartScene | null;
|
|
60
|
+
};
|
|
61
|
+
};
|
|
38
62
|
//# sourceMappingURL=createChartEditor.svelte.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createChartEditor.svelte.d.ts","sourceRoot":"","sources":["../../src/chart/createChartEditor.svelte.ts"],"names":[],"mappings":"AACA,OAAO,EACL,eAAe,
|
|
1
|
+
{"version":3,"file":"createChartEditor.svelte.d.ts","sourceRoot":"","sources":["../../src/chart/createChartEditor.svelte.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EACL,eAAe,EACf,aAAa,EACb,aAAa,EAIb,KAAK,UAAU,EACf,KAAK,wBAAwB,EAC7B,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,SAAS,EACf,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC;IACrC,yCAAyC;IACzC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,6CAA6C;IAC7C,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IAClC,kDAAkD;IAClD,QAAQ,CAAC,SAAS,EAAE,cAAc,GAAG,IAAI,CAAC;IAC1C,MAAM,CAAC,KAAK,EAAE,cAAc,GAAG,iBAAiB,CAAC;IACjD,OAAO,CAAC,IAAI,EAAE,SAAS,GAAG,iBAAiB,CAAC;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,iBAAiB,CAAC;IAC7E,MAAM,CAAC,SAAS,EAAE,cAAc,GAAG,IAAI,GAAG,IAAI,CAAC;IAC/C,IAAI,IAAI,IAAI,CAAC;IACb,IAAI,IAAI,IAAI,CAAC;IACb,yCAAyC;IACzC,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,mBAAmB,GAAE,eAAe,GAAG,sBAA2B,GAAG,iBAAiB,CAyBvH;AAED,0FAA0F;AAC1F,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,eAAe,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAA;CAAE,CAgBnG;AAED,0EAA0E;AAC1E,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,IAAI,CAAC;CAClC;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,kBAAkB,GAAG,IAAI,GAAG,SAAS,GAAG,CAAC,MAAM,kBAAkB,GAAG,IAAI,GAAG,SAAS,CAAC,EAC/F,UAAU,EAAE,eAAe,EAC3B,OAAO,GAAE,mBAAwB,GAChC,IAAI,CAMN;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,YAAY,GAAG,IAAI,GAAG,SAAS,GAAG,CAAC,MAAM,YAAY,GAAG,IAAI,GAAG,SAAS,CAAC,EACnF,UAAU,EAAE,eAAe,EAC3B,OAAO,GAAE,wBAA6B,GACrC,IAAI,CAMN;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,eAAe,EAAE,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAAO;;;;iBAC9G,UAAU,GAAG,IAAI;;EAK7C"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { untrack } from "svelte";
|
|
2
|
-
import { ChartController, createChartCommands, } from "@pixerate/editor/chart";
|
|
2
|
+
import { ChartController, ChartDirector, ChartPresence, createChartCommands, createChartToolProvider, layoutChart, } from "@pixerate/editor/chart";
|
|
3
3
|
/**
|
|
4
4
|
* Svelte 5 rune: subscribes to a `ChartController` (or creates one from options) and
|
|
5
5
|
* exposes its state reactively with bound actions.
|
|
@@ -59,3 +59,27 @@ export function useChartCommands(registry, controller, options = {}) {
|
|
|
59
59
|
return target.register(createChartCommands(controller, options));
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Registers the chart's agent tools (`createChartToolProvider`) with a `ToolRegistry` for the
|
|
64
|
+
* lifetime of the calling component. Pass a getter to follow a changing registry.
|
|
65
|
+
*/
|
|
66
|
+
export function useChartToolProvider(registry, controller, options = {}) {
|
|
67
|
+
$effect(() => {
|
|
68
|
+
const target = typeof registry === "function" ? registry() : registry;
|
|
69
|
+
if (!target)
|
|
70
|
+
return;
|
|
71
|
+
return target.register(createChartToolProvider(controller, options));
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* A `ChartPresence` and `ChartDirector` for a chart: pass `presence` and `sceneRef` to
|
|
76
|
+
* `ChartCanvas`, and `director` to `useChartToolProvider` so agent edits are acted out with a
|
|
77
|
+
* cursor.
|
|
78
|
+
*/
|
|
79
|
+
export function createChartDirector(controller, options = {}) {
|
|
80
|
+
const sceneRef = { current: null };
|
|
81
|
+
const presence = new ChartPresence();
|
|
82
|
+
const size = options.size ?? { width: 640, height: 400 };
|
|
83
|
+
const director = new ChartDirector(controller, presence, { scene: () => sceneRef.current ?? layoutChart(controller.getSpec(), size), speed: options.speed });
|
|
84
|
+
return { presence, director, sceneRef };
|
|
85
|
+
}
|
package/dist/chart/index.d.ts
CHANGED
|
@@ -3,11 +3,18 @@ import type ChartCanvasComponent from "./ChartCanvas.svelte";
|
|
|
3
3
|
import type ChartDataGridComponent from "./ChartDataGrid.svelte";
|
|
4
4
|
import type ChartEditorComponent from "./ChartEditor.svelte";
|
|
5
5
|
import type ChartInspectorComponent from "./ChartInspector.svelte";
|
|
6
|
+
import type ChartStreamingViewComponent from "./ChartStreamingView.svelte";
|
|
7
|
+
import type ChartSparklineComponent from "./ChartSparkline.svelte";
|
|
6
8
|
export { default as ChartEditor } from "./ChartEditor.svelte";
|
|
7
9
|
export { default as ChartCanvas } from "./ChartCanvas.svelte";
|
|
8
10
|
export { default as ChartDataGrid } from "./ChartDataGrid.svelte";
|
|
9
11
|
export { default as ChartInspector } from "./ChartInspector.svelte";
|
|
10
12
|
export { default as ChartCommitInput } from "./ChartCommitInput.svelte";
|
|
13
|
+
export { default as ChartMarks } from "./ChartMarks.svelte";
|
|
14
|
+
export { default as ChartSceneSvg } from "./ChartSceneSvg.svelte";
|
|
15
|
+
export { default as ChartPresenceLayer } from "./ChartPresenceLayer.svelte";
|
|
16
|
+
export { default as ChartStreamingView } from "./ChartStreamingView.svelte";
|
|
17
|
+
export { default as ChartSparkline } from "./ChartSparkline.svelte";
|
|
11
18
|
export type { ChartInspectorSection } from "./ChartInspector.svelte";
|
|
12
19
|
export * from "./createChartEditor.svelte.js";
|
|
13
20
|
export * from "./styles.js";
|
|
@@ -15,4 +22,6 @@ export type ChartCanvasProps = ComponentProps<typeof ChartCanvasComponent>;
|
|
|
15
22
|
export type ChartDataGridProps = ComponentProps<typeof ChartDataGridComponent>;
|
|
16
23
|
export type ChartEditorProps = ComponentProps<typeof ChartEditorComponent>;
|
|
17
24
|
export type ChartInspectorProps = ComponentProps<typeof ChartInspectorComponent>;
|
|
25
|
+
export type ChartStreamingViewProps = ComponentProps<typeof ChartStreamingViewComponent>;
|
|
26
|
+
export type ChartSparklineProps = ComponentProps<typeof ChartSparklineComponent>;
|
|
18
27
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/chart/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,oBAAoB,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,sBAAsB,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,oBAAoB,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,uBAAuB,MAAM,yBAAyB,CAAC;AAEnE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,YAAY,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AACrE,cAAc,+BAA+B,CAAC;AAC9C,cAAc,aAAa,CAAC;AAE5B,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAC3E,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAC/E,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAC3E,MAAM,MAAM,mBAAmB,GAAG,cAAc,CAAC,OAAO,uBAAuB,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/chart/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,oBAAoB,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,sBAAsB,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,oBAAoB,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,uBAAuB,MAAM,yBAAyB,CAAC;AACnE,OAAO,KAAK,2BAA2B,MAAM,6BAA6B,CAAC;AAC3E,OAAO,KAAK,uBAAuB,MAAM,yBAAyB,CAAC;AAEnE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACpE,YAAY,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AACrE,cAAc,+BAA+B,CAAC;AAC9C,cAAc,aAAa,CAAC;AAE5B,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAC3E,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAC/E,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAC3E,MAAM,MAAM,mBAAmB,GAAG,cAAc,CAAC,OAAO,uBAAuB,CAAC,CAAC;AACjF,MAAM,MAAM,uBAAuB,GAAG,cAAc,CAAC,OAAO,2BAA2B,CAAC,CAAC;AACzF,MAAM,MAAM,mBAAmB,GAAG,cAAc,CAAC,OAAO,uBAAuB,CAAC,CAAC"}
|
package/dist/chart/index.js
CHANGED
|
@@ -3,5 +3,10 @@ export { default as ChartCanvas } from "./ChartCanvas.svelte";
|
|
|
3
3
|
export { default as ChartDataGrid } from "./ChartDataGrid.svelte";
|
|
4
4
|
export { default as ChartInspector } from "./ChartInspector.svelte";
|
|
5
5
|
export { default as ChartCommitInput } from "./ChartCommitInput.svelte";
|
|
6
|
+
export { default as ChartMarks } from "./ChartMarks.svelte";
|
|
7
|
+
export { default as ChartSceneSvg } from "./ChartSceneSvg.svelte";
|
|
8
|
+
export { default as ChartPresenceLayer } from "./ChartPresenceLayer.svelte";
|
|
9
|
+
export { default as ChartStreamingView } from "./ChartStreamingView.svelte";
|
|
10
|
+
export { default as ChartSparkline } from "./ChartSparkline.svelte";
|
|
6
11
|
export * from "./createChartEditor.svelte.js";
|
|
7
12
|
export * from "./styles.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixerate/editor-svelte",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.43.0",
|
|
4
4
|
"description": "Svelte bindings and prompt/rich-text components for @pixerate/editor",
|
|
5
5
|
"author": "Pixerate",
|
|
6
6
|
"license": "MIT",
|
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
"@tiptap/extension-bubble-menu": "^2.11.5",
|
|
116
116
|
"@tiptap/extension-placeholder": "^2.11.5",
|
|
117
117
|
"@tiptap/starter-kit": "^2.11.5",
|
|
118
|
-
"@pixerate/editor": "0.
|
|
118
|
+
"@pixerate/editor": "0.47.0"
|
|
119
119
|
},
|
|
120
120
|
"peerDependencies": {
|
|
121
121
|
"@dagrejs/dagre": "^1.1.4",
|