svelteplot 0.2.7-pr-65.2 → 0.2.8-pr-68.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/Plot.svelte +81 -69
- package/dist/helpers/autoTicks.js +9 -8
- package/dist/helpers/scales.js +15 -1
- package/dist/marks/ColorLegend.svelte +1 -1
- package/dist/marks/Line.svelte +1 -5
- package/dist/marks/Line.svelte.d.ts +1 -1
- package/dist/marks/helpers/CanvasLayer.svelte +30 -8
- package/package.json +2 -2
package/dist/Plot.svelte
CHANGED
|
@@ -91,89 +91,101 @@
|
|
|
91
91
|
<!-- There's a bug triggering RangeError: Maximum call stack size exceeded
|
|
92
92
|
when using SveltePlot in ssr, so for now, we're disabling it -->
|
|
93
93
|
|
|
94
|
-
<
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
{#if
|
|
123
|
-
|
|
94
|
+
<svelte:boundary>
|
|
95
|
+
<Plot
|
|
96
|
+
{overlay}
|
|
97
|
+
{underlay}
|
|
98
|
+
{...restOptions}
|
|
99
|
+
header={userHeader ||
|
|
100
|
+
restOptions.title ||
|
|
101
|
+
restOptions.subtitle ||
|
|
102
|
+
restOptions.color?.legend ||
|
|
103
|
+
restOptions.symbol?.legend
|
|
104
|
+
? header
|
|
105
|
+
: null}
|
|
106
|
+
footer={userFooter || restOptions?.caption ? footer : null}
|
|
107
|
+
projection={projectionOpts}
|
|
108
|
+
implicitScales
|
|
109
|
+
{...scales}>
|
|
110
|
+
{#snippet children({
|
|
111
|
+
hasProjection,
|
|
112
|
+
hasExplicitAxisX,
|
|
113
|
+
hasExplicitAxisY,
|
|
114
|
+
hasExplicitGridX,
|
|
115
|
+
hasExplicitGridY,
|
|
116
|
+
options,
|
|
117
|
+
scales,
|
|
118
|
+
...restProps
|
|
119
|
+
})}
|
|
120
|
+
<svelte:boundary onerror={(err) => console.warn(err)}>
|
|
121
|
+
<!-- implicit axes -->
|
|
122
|
+
{#if !hasProjection && !hasExplicitAxisX}
|
|
123
|
+
{#if options.axes && (options.x.axis === 'top' || options.x.axis === 'both')}
|
|
124
|
+
<AxisX anchor="top" automatic />
|
|
125
|
+
{/if}
|
|
126
|
+
{#if options.axes && (options.x.axis === 'bottom' || options.x.axis === 'both')}
|
|
127
|
+
<AxisX anchor="bottom" automatic />
|
|
128
|
+
{/if}
|
|
124
129
|
{/if}
|
|
125
|
-
{#if
|
|
126
|
-
|
|
130
|
+
{#if !hasProjection && !hasExplicitAxisY}
|
|
131
|
+
{#if options.axes && (options.y.axis === 'left' || options.y.axis === 'both')}
|
|
132
|
+
<AxisY anchor="left" automatic />
|
|
133
|
+
{/if}
|
|
134
|
+
{#if options.axes && (options.y.axis === 'right' || options.y.axis === 'both')}
|
|
135
|
+
<AxisY anchor="right" automatic />
|
|
136
|
+
{/if}
|
|
127
137
|
{/if}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
<AxisY anchor="left" automatic />
|
|
138
|
+
<!-- implicit grids -->
|
|
139
|
+
{#if !hasExplicitGridX && (options.grid || options.x.grid)}
|
|
140
|
+
<GridX automatic />
|
|
132
141
|
{/if}
|
|
133
|
-
{#if
|
|
134
|
-
<
|
|
142
|
+
{#if !hasExplicitGridY && (options.grid || options.y.grid)}
|
|
143
|
+
<GridY automatic />
|
|
135
144
|
{/if}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
</svelte:boundary>
|
|
160
|
-
{/snippet}
|
|
161
|
-
{#snippet facetAxes()}
|
|
162
|
-
<FacetAxes />
|
|
145
|
+
<!-- implicit frame -->
|
|
146
|
+
{#if options.frame}
|
|
147
|
+
<Frame automatic />
|
|
148
|
+
{/if}
|
|
149
|
+
{@render parentChildren?.({
|
|
150
|
+
options,
|
|
151
|
+
scales,
|
|
152
|
+
...restProps
|
|
153
|
+
})}
|
|
154
|
+
{#snippet failed(error, reset)}
|
|
155
|
+
<text class="error" transform="translate(10,10)">
|
|
156
|
+
{#each error.message.split('\n') as line, i (i)}
|
|
157
|
+
<tspan x="0" dy={i ? 14 : 0}>{line}</tspan>
|
|
158
|
+
{/each}
|
|
159
|
+
</text>{/snippet}
|
|
160
|
+
</svelte:boundary>
|
|
161
|
+
{/snippet}
|
|
162
|
+
{#snippet facetAxes()}
|
|
163
|
+
<FacetAxes />
|
|
164
|
+
{/snippet}
|
|
165
|
+
</Plot>
|
|
166
|
+
{#snippet failed(error)}
|
|
167
|
+
<div class="error">Error: {error.message}</div>
|
|
163
168
|
{/snippet}
|
|
164
|
-
</
|
|
169
|
+
</svelte:boundary>
|
|
165
170
|
|
|
166
171
|
<style>
|
|
167
172
|
:root {
|
|
168
173
|
--plot-bg: white;
|
|
169
174
|
--plot-fg: currentColor;
|
|
170
175
|
}
|
|
171
|
-
|
|
172
|
-
stroke: var(--plot-bg);
|
|
173
|
-
fill: crimson;
|
|
176
|
+
.error {
|
|
174
177
|
font-size: 11px;
|
|
175
178
|
stroke-width: 3px;
|
|
176
179
|
font-weight: bold;
|
|
180
|
+
}
|
|
181
|
+
text.error {
|
|
182
|
+
stroke: var(--plot-bg);
|
|
183
|
+
fill: crimson;
|
|
177
184
|
paint-order: stroke fill;
|
|
178
185
|
}
|
|
186
|
+
div.error {
|
|
187
|
+
color: crimson;
|
|
188
|
+
white-space: pre-wrap;
|
|
189
|
+
line-height: 1.1;
|
|
190
|
+
}
|
|
179
191
|
</style>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { maybeTimeInterval } from './time.js';
|
|
2
|
-
import { range as rangei } from 'd3-array';
|
|
2
|
+
import { extent, range as rangei } from 'd3-array';
|
|
3
3
|
export function maybeInterval(interval) {
|
|
4
4
|
if (interval == null)
|
|
5
5
|
return;
|
|
@@ -30,11 +30,12 @@ export function maybeInterval(interval) {
|
|
|
30
30
|
return interval;
|
|
31
31
|
}
|
|
32
32
|
export function autoTicks(type, ticks, interval, domain, scaleFn, count) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
if (ticks)
|
|
34
|
+
return ticks;
|
|
35
|
+
if (interval) {
|
|
36
|
+
const [lo, hi] = extent(domain);
|
|
37
|
+
const I = maybeInterval(interval, type);
|
|
38
|
+
return I.range(lo, I.offset(hi));
|
|
39
|
+
}
|
|
40
|
+
return typeof scaleFn.ticks === 'function' ? scaleFn.ticks(count) : [];
|
|
40
41
|
}
|
package/dist/helpers/scales.js
CHANGED
|
@@ -5,6 +5,7 @@ import { isSymbolOrNull } from './typeChecks.js';
|
|
|
5
5
|
import { resolveProp, toChannelOption } from './resolve.js';
|
|
6
6
|
import isDataRecord from './isDataRecord.js';
|
|
7
7
|
import { createProjection } from './projection.js';
|
|
8
|
+
import { maybeInterval } from './autoTicks.js';
|
|
8
9
|
/**
|
|
9
10
|
* compute the plot scales
|
|
10
11
|
*/
|
|
@@ -148,7 +149,7 @@ export function createScale(name, scaleOptions, marks, plotOptions, plotWidth, p
|
|
|
148
149
|
valueArr.sort(ascending);
|
|
149
150
|
}
|
|
150
151
|
const valueArray = type === 'quantile' || type === 'quantile-cont' ? allDataValues.toSorted() : valueArr;
|
|
151
|
-
|
|
152
|
+
let domain = scaleOptions.domain
|
|
152
153
|
? isOrdinal
|
|
153
154
|
? scaleOptions.domain
|
|
154
155
|
: extent(scaleOptions.zero ? [0, ...scaleOptions.domain] : scaleOptions.domain)
|
|
@@ -162,6 +163,14 @@ export function createScale(name, scaleOptions, marks, plotOptions, plotWidth, p
|
|
|
162
163
|
? valueArray.toReversed()
|
|
163
164
|
: valueArray
|
|
164
165
|
: extent(scaleOptions.zero ? [0, ...valueArray] : valueArray);
|
|
166
|
+
if (scaleOptions.interval) {
|
|
167
|
+
if (isOrdinal) {
|
|
168
|
+
domain = domainFromInterval(domain, scaleOptions.interval);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
throw new Error('Setting interval via axis options is only supported for ordinal scales');
|
|
172
|
+
}
|
|
173
|
+
}
|
|
165
174
|
if (!scaleOptions.scale) {
|
|
166
175
|
throw new Error(`No scale function defined for ${name}`);
|
|
167
176
|
}
|
|
@@ -192,6 +201,11 @@ export function createScale(name, scaleOptions, marks, plotOptions, plotWidth, p
|
|
|
192
201
|
: null
|
|
193
202
|
};
|
|
194
203
|
}
|
|
204
|
+
function domainFromInterval(domain, interval) {
|
|
205
|
+
const interval_ = maybeInterval(interval);
|
|
206
|
+
const [lo, hi] = extent(domain);
|
|
207
|
+
return interval_.range(lo, interval_.offset(hi));
|
|
208
|
+
}
|
|
195
209
|
/**
|
|
196
210
|
* Infer a scale type based on the scale name, the data values mapped to it and
|
|
197
211
|
* the mark types that are bound to the scale
|
package/dist/marks/Line.svelte
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
outlineStroke?: string;
|
|
22
22
|
outlineStrokeWidth?: number;
|
|
23
23
|
outlineStrokeOpacity?: number;
|
|
24
|
-
curve?: CurveName | CurveFactory;
|
|
24
|
+
curve?: CurveName | CurveFactory | 'auto';
|
|
25
25
|
tension?: number;
|
|
26
26
|
sort?: ConstantAccessor<RawValue> | { channel: 'stroke' | 'fill' };
|
|
27
27
|
text?: ConstantAccessor<string>;
|
|
@@ -76,10 +76,6 @@
|
|
|
76
76
|
if (groupValue === lastGroupValue) {
|
|
77
77
|
group.push(d);
|
|
78
78
|
} else {
|
|
79
|
-
if (group.length === 1) {
|
|
80
|
-
// just one point makes a bad line, add this one, too
|
|
81
|
-
group.push(d);
|
|
82
|
-
}
|
|
83
79
|
// new group
|
|
84
80
|
group = [d];
|
|
85
81
|
groups.push(group);
|
|
@@ -7,7 +7,7 @@ export type LineMarkProps = {
|
|
|
7
7
|
outlineStroke?: string;
|
|
8
8
|
outlineStrokeWidth?: number;
|
|
9
9
|
outlineStrokeOpacity?: number;
|
|
10
|
-
curve?: CurveName | CurveFactory;
|
|
10
|
+
curve?: CurveName | CurveFactory | 'auto';
|
|
11
11
|
tension?: number;
|
|
12
12
|
sort?: ConstantAccessor<RawValue> | {
|
|
13
13
|
channel: 'stroke' | 'fill';
|
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
import { getContext } from 'svelte';
|
|
3
3
|
import type { PlotContext } from '../../types';
|
|
4
4
|
import { devicePixelRatio } from 'svelte/reactivity/window';
|
|
5
|
+
import { MediaQuery } from 'svelte/reactivity';
|
|
6
|
+
import type { Attachment } from 'svelte/attachments';
|
|
7
|
+
|
|
8
|
+
const darkMode = new MediaQuery('prefers-color-scheme: dark');
|
|
9
|
+
let colorScheme = $state();
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* we need to repaint on dark mode changes in case the user
|
|
13
|
+
* is using any css variables or currentColor that changes
|
|
14
|
+
* with the color scheme
|
|
15
|
+
*/
|
|
16
|
+
const watchColorScheme: Attachment = (element: Element) => {
|
|
17
|
+
const htmlElement = element.ownerDocument.documentElement;
|
|
18
|
+
const observer = new MutationObserver(() => {
|
|
19
|
+
colorScheme = getComputedStyle(htmlElement).colorScheme;
|
|
20
|
+
});
|
|
21
|
+
observer.observe(htmlElement, { attributes: true });
|
|
22
|
+
return () => {
|
|
23
|
+
observer.disconnect();
|
|
24
|
+
};
|
|
25
|
+
};
|
|
5
26
|
|
|
6
27
|
let restProps: {} = $props();
|
|
7
28
|
|
|
@@ -14,14 +35,15 @@
|
|
|
14
35
|
canvas element inside a foreignObject for use in a plot and takes care of
|
|
15
36
|
scaling it to the device pixel ratio.
|
|
16
37
|
-->
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
38
|
+
<foreignObject x="0" y="0" {@attach watchColorScheme} width={plot.width} height={plot.height}>
|
|
39
|
+
{#key [colorScheme, darkMode.current]}
|
|
40
|
+
<canvas
|
|
41
|
+
xmlns="http://www.w3.org/1999/xhtml"
|
|
42
|
+
{...restProps}
|
|
43
|
+
width={plot.width * (devicePixelRatio.current ?? 1)}
|
|
44
|
+
height={plot.height * (devicePixelRatio.current ?? 1)}
|
|
45
|
+
style="width: {plot.width}px; height: {plot.height}px;"></canvas>
|
|
46
|
+
{/key}
|
|
25
47
|
</foreignObject>
|
|
26
48
|
|
|
27
49
|
<style>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelteplot",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8-pr-68.0",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Gregor Aisch",
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"svg-path-parser": "^1.1.0",
|
|
94
94
|
"topojson-client": "^3.1.0",
|
|
95
95
|
"tslib": "^2.8.1",
|
|
96
|
-
"typedoc": "^0.28.
|
|
96
|
+
"typedoc": "^0.28.5",
|
|
97
97
|
"typedoc-plugin-markdown": "^4.6.3",
|
|
98
98
|
"typescript": "^5.8.3",
|
|
99
99
|
"vite": "^6.3.5",
|