svelteplot 0.0.1-alpha.8 → 0.1.3-next.3
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/LICENSE.md +5 -0
- package/README.md +3 -36
- package/dist/Mark.svelte +292 -0
- package/dist/Mark.svelte.d.ts +22 -0
- package/dist/Plot.svelte +148 -153
- package/dist/Plot.svelte.d.ts +15 -15
- package/dist/constants.d.ts +14 -0
- package/dist/constants.js +109 -0
- package/dist/core/Facet.svelte +59 -0
- package/dist/core/Facet.svelte.d.ts +18 -0
- package/dist/core/FacetAxes.svelte +66 -0
- package/dist/core/FacetAxes.svelte.d.ts +4 -0
- package/dist/core/FacetGrid.svelte +86 -0
- package/dist/core/FacetGrid.svelte.d.ts +13 -0
- package/dist/core/Plot.svelte +567 -0
- package/dist/core/Plot.svelte.d.ts +14 -0
- package/dist/helpers/arrowPath.d.ts +14 -0
- package/dist/helpers/arrowPath.js +129 -0
- package/dist/helpers/autoProjection.d.ts +19 -0
- package/dist/helpers/autoProjection.js +87 -0
- package/dist/helpers/autoScales.d.ts +23 -0
- package/dist/helpers/autoScales.js +203 -0
- package/dist/helpers/autoTicks.d.ts +3 -0
- package/dist/helpers/autoTicks.js +40 -0
- package/dist/helpers/autoTimeFormat.d.ts +2 -2
- package/dist/helpers/autoTimeFormat.js +34 -5
- package/dist/helpers/callWithProps.d.ts +8 -0
- package/dist/helpers/callWithProps.js +13 -0
- package/dist/helpers/colors.js +17 -2
- package/dist/helpers/curves.d.ts +3 -0
- package/dist/helpers/curves.js +42 -0
- package/dist/helpers/data.d.ts +9 -0
- package/dist/helpers/data.js +16 -0
- package/dist/helpers/facets.d.ts +12 -0
- package/dist/helpers/facets.js +49 -0
- package/dist/helpers/formats.d.ts +3 -0
- package/dist/helpers/formats.js +3 -0
- package/dist/helpers/getBaseStyles.d.ts +7 -2
- package/dist/helpers/getBaseStyles.js +34 -10
- package/dist/helpers/getLogTicks.js +5 -5
- package/dist/helpers/group.d.ts +6 -0
- package/dist/helpers/group.js +53 -0
- package/dist/helpers/index.d.ts +18 -0
- package/dist/helpers/index.js +53 -0
- package/dist/helpers/isRawValue.d.ts +2 -0
- package/dist/helpers/isRawValue.js +5 -0
- package/dist/helpers/isValid.d.ts +6 -0
- package/dist/helpers/isValid.js +6 -0
- package/dist/helpers/math.d.ts +19 -0
- package/dist/helpers/math.js +116 -0
- package/dist/helpers/mergeDeep.d.ts +1 -1
- package/dist/helpers/noise.d.ts +1 -0
- package/dist/helpers/noise.js +72 -0
- package/dist/helpers/projection.d.ts +33 -0
- package/dist/helpers/projection.js +100 -0
- package/dist/helpers/reduce.d.ts +10 -0
- package/dist/helpers/reduce.js +85 -0
- package/dist/helpers/regressionLoess.d.ts +12 -0
- package/dist/helpers/regressionLoess.js +47 -0
- package/dist/helpers/removeIdenticalLines.d.ts +8 -1
- package/dist/helpers/removeIdenticalLines.js +14 -7
- package/dist/helpers/resolve.d.ts +17 -0
- package/dist/helpers/resolve.js +152 -0
- package/dist/helpers/roundedRect.d.ts +9 -0
- package/dist/helpers/roundedRect.js +31 -0
- package/dist/helpers/scales.d.ts +42 -0
- package/dist/helpers/scales.js +309 -0
- package/dist/helpers/time.d.ts +6 -0
- package/dist/helpers/time.js +282 -0
- package/dist/helpers/typeChecks.d.ts +8 -5
- package/dist/helpers/typeChecks.js +27 -6
- package/dist/index.d.ts +49 -1
- package/dist/index.js +53 -2
- package/dist/marks/Area.svelte +146 -0
- package/dist/marks/Area.svelte.d.ts +30 -0
- package/dist/marks/AreaX.svelte +27 -0
- package/dist/marks/AreaX.svelte.d.ts +12 -0
- package/dist/marks/AreaY.svelte +38 -0
- package/dist/marks/AreaY.svelte.d.ts +19 -0
- package/dist/marks/Arrow.svelte +139 -0
- package/dist/marks/Arrow.svelte.d.ts +44 -0
- package/dist/marks/AxisX.svelte +198 -93
- package/dist/marks/AxisX.svelte.d.ts +17 -16
- package/dist/marks/AxisY.svelte +176 -62
- package/dist/marks/AxisY.svelte.d.ts +17 -14
- package/dist/marks/BarX.svelte +81 -0
- package/dist/marks/BarX.svelte.d.ts +4 -0
- package/dist/marks/BarY.svelte +95 -0
- package/dist/marks/BarY.svelte.d.ts +4 -0
- package/dist/marks/BollingerX.svelte +44 -0
- package/dist/marks/BollingerX.svelte.d.ts +18 -0
- package/dist/marks/BollingerY.svelte +39 -0
- package/dist/marks/BollingerY.svelte.d.ts +18 -0
- package/dist/marks/BoxX.svelte +89 -0
- package/dist/marks/BoxX.svelte.d.ts +4 -0
- package/dist/marks/BoxY.svelte +110 -0
- package/dist/marks/BoxY.svelte.d.ts +29 -0
- package/dist/marks/Cell.svelte +110 -0
- package/dist/marks/Cell.svelte.d.ts +16 -0
- package/dist/marks/CellX.svelte +24 -0
- package/dist/marks/CellX.svelte.d.ts +3 -0
- package/dist/marks/CellY.svelte +24 -0
- package/dist/marks/CellY.svelte.d.ts +3 -0
- package/dist/marks/ColorLegend.svelte +148 -27
- package/dist/marks/ColorLegend.svelte.d.ts +12 -13
- package/dist/marks/CustomMark.svelte +43 -0
- package/dist/marks/CustomMark.svelte.d.ts +16 -0
- package/dist/marks/CustomMarkHTML.svelte +103 -0
- package/dist/marks/CustomMarkHTML.svelte.d.ts +17 -0
- package/dist/marks/DifferenceY.svelte +144 -0
- package/dist/marks/DifferenceY.svelte.d.ts +30 -0
- package/dist/marks/Dot.svelte +128 -73
- package/dist/marks/Dot.svelte.d.ts +24 -14
- package/dist/marks/DotX.svelte +15 -3
- package/dist/marks/DotX.svelte.d.ts +8 -16
- package/dist/marks/DotY.svelte +8 -3
- package/dist/marks/DotY.svelte.d.ts +5 -17
- package/dist/marks/Frame.svelte +32 -31
- package/dist/marks/Frame.svelte.d.ts +7 -14
- package/dist/marks/Geo.svelte +102 -0
- package/dist/marks/Geo.svelte.d.ts +10 -0
- package/dist/marks/Graticule.svelte +28 -0
- package/dist/marks/Graticule.svelte.d.ts +9 -0
- package/dist/marks/GridX.svelte +67 -36
- package/dist/marks/GridX.svelte.d.ts +7 -18
- package/dist/marks/GridY.svelte +64 -25
- package/dist/marks/GridY.svelte.d.ts +7 -14
- package/dist/marks/HTMLTooltip.svelte +91 -0
- package/dist/marks/HTMLTooltip.svelte.d.ts +11 -0
- package/dist/marks/Line.svelte +219 -58
- package/dist/marks/Line.svelte.d.ts +30 -14
- package/dist/marks/LineX.svelte +8 -8
- package/dist/marks/LineX.svelte.d.ts +4 -17
- package/dist/marks/LineY.svelte +7 -8
- package/dist/marks/LineY.svelte.d.ts +4 -17
- package/dist/marks/Link.svelte +173 -0
- package/dist/marks/Link.svelte.d.ts +21 -0
- package/dist/marks/Pointer.svelte +126 -0
- package/dist/marks/Pointer.svelte.d.ts +23 -0
- package/dist/marks/Rect.svelte +103 -0
- package/dist/marks/Rect.svelte.d.ts +15 -0
- package/dist/marks/RectX.svelte +33 -0
- package/dist/marks/RectX.svelte.d.ts +15 -0
- package/dist/marks/RectY.svelte +33 -0
- package/dist/marks/RectY.svelte.d.ts +15 -0
- package/dist/marks/RegressionX.svelte +26 -0
- package/dist/marks/RegressionX.svelte.d.ts +4 -0
- package/dist/marks/RegressionY.svelte +26 -0
- package/dist/marks/RegressionY.svelte.d.ts +4 -0
- package/dist/marks/RuleX.svelte +52 -28
- package/dist/marks/RuleX.svelte.d.ts +14 -14
- package/dist/marks/RuleY.svelte +52 -28
- package/dist/marks/RuleY.svelte.d.ts +14 -14
- package/dist/marks/Sphere.svelte +8 -0
- package/dist/marks/Sphere.svelte.d.ts +51 -0
- package/dist/marks/Spike.svelte +15 -0
- package/dist/marks/Spike.svelte.d.ts +4 -0
- package/dist/marks/SymbolLegend.svelte +27 -12
- package/dist/marks/SymbolLegend.svelte.d.ts +8 -14
- package/dist/marks/Text.svelte +185 -0
- package/dist/marks/Text.svelte.d.ts +26 -0
- package/dist/marks/TickX.svelte +89 -0
- package/dist/marks/TickX.svelte.d.ts +22 -0
- package/dist/marks/TickY.svelte +90 -0
- package/dist/marks/TickY.svelte.d.ts +22 -0
- package/dist/marks/Vector.svelte +213 -0
- package/dist/marks/Vector.svelte.d.ts +31 -0
- package/dist/marks/helpers/BaseAxisX.svelte +210 -0
- package/dist/marks/helpers/BaseAxisX.svelte.d.ts +24 -0
- package/dist/marks/helpers/BaseAxisY.svelte +187 -0
- package/dist/marks/helpers/BaseAxisY.svelte.d.ts +23 -0
- package/dist/marks/helpers/CanvasLayer.svelte +38 -0
- package/dist/marks/helpers/CanvasLayer.svelte.d.ts +13 -0
- package/dist/marks/helpers/DotCanvas.svelte +184 -0
- package/dist/marks/helpers/DotCanvas.svelte.d.ts +11 -0
- package/dist/marks/helpers/GeoCanvas.svelte +165 -0
- package/dist/marks/helpers/GeoCanvas.svelte.d.ts +13 -0
- package/dist/marks/helpers/GroupMultiple.svelte +17 -0
- package/dist/marks/helpers/GroupMultiple.svelte.d.ts +9 -0
- package/dist/marks/helpers/Marker.svelte +93 -0
- package/dist/marks/helpers/Marker.svelte.d.ts +10 -0
- package/dist/marks/helpers/MarkerPath.svelte +164 -0
- package/dist/marks/helpers/MarkerPath.svelte.d.ts +44 -0
- package/dist/marks/helpers/Regression.svelte +174 -0
- package/dist/marks/helpers/Regression.svelte.d.ts +26 -0
- package/dist/marks/helpers/events.d.ts +8 -0
- package/dist/marks/helpers/events.js +64 -0
- package/dist/transforms/bin.d.ts +51 -0
- package/dist/transforms/bin.js +171 -0
- package/dist/transforms/bollinger.d.ts +21 -0
- package/dist/transforms/bollinger.js +53 -0
- package/dist/transforms/centroid.d.ts +9 -0
- package/dist/transforms/centroid.js +13 -0
- package/dist/transforms/facet.d.ts +1 -0
- package/dist/transforms/facet.js +1 -0
- package/dist/transforms/filter.d.ts +2 -0
- package/dist/transforms/filter.js +8 -0
- package/dist/transforms/group.d.ts +66 -0
- package/dist/transforms/group.js +109 -0
- package/dist/transforms/interval.d.ts +11 -0
- package/dist/transforms/interval.js +34 -0
- package/dist/transforms/jitter.d.ts +0 -0
- package/dist/transforms/jitter.js +1 -0
- package/dist/transforms/map.d.ts +10 -0
- package/dist/transforms/map.js +89 -0
- package/dist/transforms/normalize.d.ts +9 -0
- package/dist/transforms/normalize.js +86 -0
- package/dist/transforms/recordize.d.ts +13 -0
- package/dist/transforms/recordize.js +75 -0
- package/dist/transforms/rename.d.ts +14 -0
- package/dist/transforms/rename.js +42 -0
- package/dist/transforms/select.d.ts +35 -0
- package/dist/transforms/select.js +55 -0
- package/dist/transforms/shift.d.ts +13 -0
- package/dist/transforms/shift.js +45 -0
- package/dist/transforms/sort.d.ts +28 -0
- package/dist/transforms/sort.js +61 -0
- package/dist/transforms/stack.d.ts +10 -0
- package/dist/transforms/stack.js +110 -0
- package/dist/transforms/window.d.ts +22 -0
- package/dist/transforms/window.js +73 -0
- package/dist/types.d.ts +625 -188
- package/dist/ui/Checkbox.svelte +6 -0
- package/dist/ui/Checkbox.svelte.d.ts +13 -0
- package/dist/ui/RadioInput.svelte +27 -0
- package/dist/ui/RadioInput.svelte.d.ts +9 -0
- package/dist/ui/Select.svelte +27 -0
- package/dist/ui/Select.svelte.d.ts +9 -0
- package/dist/ui/Slider.svelte +47 -0
- package/dist/ui/Slider.svelte.d.ts +11 -0
- package/dist/ui/Spiral.svelte +46 -0
- package/dist/ui/Spiral.svelte.d.ts +15 -0
- package/dist/ui/index.d.ts +4 -0
- package/dist/ui/index.js +4 -0
- package/package.json +79 -40
- package/LICENSE +0 -11
- package/dist/classes/Channel.svelte.js +0 -74
- package/dist/classes/Mark.svelte.js +0 -17
- package/dist/classes/Plot.svelte.js +0 -98
- package/dist/contants.d.ts +0 -3
- package/dist/contants.js +0 -40
- package/dist/helpers/GroupMultiple.svelte +0 -8
- package/dist/helpers/GroupMultiple.svelte.d.ts +0 -19
- package/dist/helpers/createScale.d.ts +0 -5
- package/dist/helpers/createScale.js +0 -57
- package/dist/helpers/resolveChannel.d.ts +0 -2
- package/dist/helpers/resolveChannel.js +0 -28
- package/dist/helpers/wrapArray.d.ts +0 -2
- package/dist/helpers/wrapArray.js +0 -4
- package/dist/marks/BaseMark.svelte +0 -22
- package/dist/marks/BaseMark.svelte.d.ts +0 -19
package/LICENSE.md
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Copyright 2024, Gregor Aisch
|
|
2
|
+
|
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
4
|
+
|
|
5
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,38 +1,5 @@
|
|
|
1
|
-
#
|
|
1
|
+
# SveltePlot
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
SveltePlot is a new reactive visualization framework based on the [layered grammar of graphics](https://vita.had.co.nz/papers/layered-grammar.html) ideas. It's API is heavily inspired by [Observable Plot](https://github.com/observablehq/plot). Created by Gregor Aisch.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
If you're seeing this, you've probably already done this step. Congrats!
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
# create a new project in the current directory
|
|
11
|
-
npm create svelte@latest
|
|
12
|
-
|
|
13
|
-
# create a new project in my-app
|
|
14
|
-
npm create svelte@latest my-app
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## Developing
|
|
18
|
-
|
|
19
|
-
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
npm run dev
|
|
23
|
-
|
|
24
|
-
# or start the server and open the app in a new browser tab
|
|
25
|
-
npm run dev -- --open
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Building
|
|
29
|
-
|
|
30
|
-
To create a production version of your app:
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
npm run build
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
You can preview the production build with `npm run preview`.
|
|
37
|
-
|
|
38
|
-
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
|
|
5
|
+
<img src="static/logo.png" alt="logo" width="400" />
|
package/dist/Mark.svelte
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getContext, untrack, type Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
import { CHANNEL_SCALE } from './constants.js';
|
|
5
|
+
import type {
|
|
6
|
+
ScaledChannelName,
|
|
7
|
+
MarkType,
|
|
8
|
+
DataRecord,
|
|
9
|
+
PlotContext,
|
|
10
|
+
ChannelName,
|
|
11
|
+
GenericMarkOptions,
|
|
12
|
+
ChannelAccessor,
|
|
13
|
+
BaseMarkProps,
|
|
14
|
+
FacetContext,
|
|
15
|
+
ScaleName,
|
|
16
|
+
RawValue,
|
|
17
|
+
ResolvedDataRecord,
|
|
18
|
+
ScaledDataRecord,
|
|
19
|
+
ScaleType
|
|
20
|
+
} from './types.js';
|
|
21
|
+
import { getUsedScales, projectXY, projectX, projectY } from './helpers/scales.js';
|
|
22
|
+
import { testFilter, isValid } from './helpers/index.js';
|
|
23
|
+
import { resolveChannel, resolveProp } from './helpers/resolve.js';
|
|
24
|
+
|
|
25
|
+
type MarkProps = {
|
|
26
|
+
data?: DataRecord[];
|
|
27
|
+
automatic?: boolean;
|
|
28
|
+
type: MarkType;
|
|
29
|
+
channels?: ScaledChannelName[];
|
|
30
|
+
required?: ScaledChannelName[];
|
|
31
|
+
requiredScales?: Partial<Record<ScaleName, ScaleType[]>>;
|
|
32
|
+
children?: Snippet<
|
|
33
|
+
[
|
|
34
|
+
{
|
|
35
|
+
mark: Mark<GenericMarkOptions>;
|
|
36
|
+
usedScales: ReturnType<typeof getUsedScales>;
|
|
37
|
+
scaledData: ScaledDataRecord[];
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
>;
|
|
41
|
+
defaults?: Partial<Record<ScaledChannelName, RawValue>>;
|
|
42
|
+
} & Partial<Record<ChannelName, ChannelAccessor>> &
|
|
43
|
+
Partial<BaseMarkProps>;
|
|
44
|
+
|
|
45
|
+
let {
|
|
46
|
+
data = [],
|
|
47
|
+
children,
|
|
48
|
+
type,
|
|
49
|
+
channels = [],
|
|
50
|
+
required = [],
|
|
51
|
+
requiredScales = {},
|
|
52
|
+
defaults = {},
|
|
53
|
+
...options
|
|
54
|
+
}: MarkProps = $props();
|
|
55
|
+
|
|
56
|
+
const channelsWithFacets: ScaledChannelName[] = $derived([...channels, 'fx', 'fy']);
|
|
57
|
+
|
|
58
|
+
const { addMark, updateMark, updatePlotState, removeMark, getTopLevelFacet, getPlotState } =
|
|
59
|
+
getContext<PlotContext>('svelteplot');
|
|
60
|
+
|
|
61
|
+
const plot = $derived(getPlotState());
|
|
62
|
+
const facet = $derived(getTopLevelFacet());
|
|
63
|
+
|
|
64
|
+
const { getFacetState } = getContext<FacetContext>('svelteplot/facet');
|
|
65
|
+
const { left, top } = $derived(getFacetState());
|
|
66
|
+
|
|
67
|
+
class Mark {
|
|
68
|
+
id;
|
|
69
|
+
type;
|
|
70
|
+
channels: ScaledChannelName[] = $state.raw([]);
|
|
71
|
+
scales: Set<ScaleName> = $state.raw(new Set());
|
|
72
|
+
data: DataRecord[] = $state.raw([]);
|
|
73
|
+
options: GenericMarkOptions = $state.raw({});
|
|
74
|
+
|
|
75
|
+
constructor(type: MarkType) {
|
|
76
|
+
this.id = Symbol();
|
|
77
|
+
this.type = type;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const mark = new Mark(type);
|
|
82
|
+
|
|
83
|
+
$effect(() => {
|
|
84
|
+
return () => {
|
|
85
|
+
removeMark(mark);
|
|
86
|
+
added = false;
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// let mark2 = $state(mark);
|
|
91
|
+
const facetMode = $derived(options.facet || 'auto');
|
|
92
|
+
|
|
93
|
+
const optionsWithAutoFacet = $derived({
|
|
94
|
+
...options,
|
|
95
|
+
__firstFacet: left && top,
|
|
96
|
+
...(facet &&
|
|
97
|
+
facet.data &&
|
|
98
|
+
((facetMode === 'auto' && facet.data === data) || facetMode === 'include')
|
|
99
|
+
? {
|
|
100
|
+
fx: facet.x,
|
|
101
|
+
fy: facet.y
|
|
102
|
+
}
|
|
103
|
+
: {})
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
let added = false;
|
|
107
|
+
|
|
108
|
+
$effect(() => {
|
|
109
|
+
if (added) return;
|
|
110
|
+
// without using untrack() here we end up with inexplicable
|
|
111
|
+
// circular dependency updates resulting in a stack overflow
|
|
112
|
+
const channels = untrack(() => channelsWithFacets);
|
|
113
|
+
mark.channels = channels;
|
|
114
|
+
mark.scales = new Set(
|
|
115
|
+
channels
|
|
116
|
+
.filter((channel) => options[channel] !== 0)
|
|
117
|
+
.map((channel) => CHANNEL_SCALE[channel])
|
|
118
|
+
);
|
|
119
|
+
mark.data = untrack(() => data);
|
|
120
|
+
mark.options = untrack(() => optionsWithAutoFacet);
|
|
121
|
+
addMark(mark);
|
|
122
|
+
added = true;
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const { getTestFacet } = getContext<FacetContext>('svelteplot/facet');
|
|
126
|
+
const testFacet = $derived(getTestFacet());
|
|
127
|
+
|
|
128
|
+
const resolvedData: ResolvedDataRecord[] = $derived(
|
|
129
|
+
data.flatMap((row) => {
|
|
130
|
+
const channels = options as Record<ChannelName, ChannelAccessor>;
|
|
131
|
+
if (!testFacet(row, channels) || !testFilter(row, channels)) return [];
|
|
132
|
+
const out: ResolvedDataRecord = {
|
|
133
|
+
datum: row
|
|
134
|
+
};
|
|
135
|
+
for (const [channel] of Object.entries(CHANNEL_SCALE) as [
|
|
136
|
+
ScaledChannelName,
|
|
137
|
+
ScaleName
|
|
138
|
+
][]) {
|
|
139
|
+
// check if the mark has defined an accessor for this channel
|
|
140
|
+
if (options?.[channel] !== undefined && out[channel] === undefined) {
|
|
141
|
+
// resolve value
|
|
142
|
+
out[channel] = resolveChannel(channel, row, options);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return [out];
|
|
146
|
+
})
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
let prevResolvedData: ResolvedDataRecord[] = [];
|
|
150
|
+
|
|
151
|
+
$effect(() => {
|
|
152
|
+
if (isDifferent(resolvedData, prevResolvedData)) {
|
|
153
|
+
prevResolvedData = resolvedData;
|
|
154
|
+
// data has changed
|
|
155
|
+
mark.data = data;
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
function isDifferent(array1: ResolvedDataRecord[], array2: ResolvedDataRecord[]) {
|
|
160
|
+
if (array1.length !== array2.length) return true;
|
|
161
|
+
for (let i = 0; i < array1.length; i++) {
|
|
162
|
+
for (const [channel] of Object.entries(CHANNEL_SCALE) as [
|
|
163
|
+
ScaledChannelName,
|
|
164
|
+
ScaleName
|
|
165
|
+
][]) {
|
|
166
|
+
if (array1[i][channel] !== array2[i][channel]) return true;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const errors = $derived([
|
|
173
|
+
...required
|
|
174
|
+
.filter((name) => options[name] == null)
|
|
175
|
+
.map((name) => `missing channel value for ${mark.type} mark: ${name}`),
|
|
176
|
+
...Object.entries(requiredScales)
|
|
177
|
+
.filter(([scale, types]) => {
|
|
178
|
+
return !types.includes(plot.scales[scale].type);
|
|
179
|
+
})
|
|
180
|
+
.map(
|
|
181
|
+
([scale, types]) => `scale type mismatch for ${scale} (needs ${types.join(' or ')})`
|
|
182
|
+
)
|
|
183
|
+
]);
|
|
184
|
+
|
|
185
|
+
$effect(() => {
|
|
186
|
+
for (const name of required) {
|
|
187
|
+
if (options[name] == null) throw new Error(`missing channel value: ${name}`);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
const usedScales = $derived(getUsedScales(plot, optionsWithAutoFacet, mark));
|
|
192
|
+
/**
|
|
193
|
+
* based on the data and the global scales we can now map the data
|
|
194
|
+
* elements to the scales
|
|
195
|
+
*/
|
|
196
|
+
const scaledData = $derived(
|
|
197
|
+
resolvedData.flatMap((row) => {
|
|
198
|
+
const out: ScaledDataRecord = {
|
|
199
|
+
datum: row.datum,
|
|
200
|
+
valid: true
|
|
201
|
+
};
|
|
202
|
+
// compute dx/dy
|
|
203
|
+
const dx = Number(resolveProp<number>(options.dx, out.datum, 0));
|
|
204
|
+
const dy = Number(resolveProp<number>(options.dy, out.datum, 0));
|
|
205
|
+
|
|
206
|
+
// special handling if there's a projection
|
|
207
|
+
if (plot.scales.projection && mark.type !== 'geo') {
|
|
208
|
+
for (const suffix of ['', '1', '2']) {
|
|
209
|
+
if (
|
|
210
|
+
options?.[`x${suffix}`] !== undefined &&
|
|
211
|
+
options?.[`y${suffix}`] !== undefined
|
|
212
|
+
) {
|
|
213
|
+
// we have two-dimensional accessors
|
|
214
|
+
// for the x and y channels
|
|
215
|
+
const rx = resolveChannel(`x${suffix}`, row, options);
|
|
216
|
+
const ry = resolveChannel(`y${suffix}`, row, options);
|
|
217
|
+
const [x, y] =
|
|
218
|
+
mark.type === 'line'
|
|
219
|
+
? [rx, ry] // line paths are projected later
|
|
220
|
+
: projectXY(
|
|
221
|
+
plot.scales,
|
|
222
|
+
rx,
|
|
223
|
+
ry,
|
|
224
|
+
usedScales.x,
|
|
225
|
+
usedScales.y,
|
|
226
|
+
suffix
|
|
227
|
+
);
|
|
228
|
+
out[`x${suffix}`] = x;
|
|
229
|
+
out[`y${suffix}`] = y;
|
|
230
|
+
out.valid = out.valid && isValid(rx) && isValid(ry);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// iterate over all scaled channels
|
|
236
|
+
for (const [channel, scale] of Object.entries(CHANNEL_SCALE) as [
|
|
237
|
+
ScaledChannelName,
|
|
238
|
+
ScaleName
|
|
239
|
+
][]) {
|
|
240
|
+
// check if the mark has defined an accessor for this channel
|
|
241
|
+
if (options?.[channel] !== undefined && out[channel] === undefined) {
|
|
242
|
+
// resolve value
|
|
243
|
+
const value = row[channel];
|
|
244
|
+
|
|
245
|
+
const scaled = usedScales[channel]
|
|
246
|
+
? scale === 'x'
|
|
247
|
+
? projectX(channel as 'x' | 'x1' | 'x2', plot.scales, value)
|
|
248
|
+
: scale === 'y'
|
|
249
|
+
? projectY(channel as 'y' | 'y1' | 'y1', plot.scales, value)
|
|
250
|
+
: plot.scales[scale].fn(value)
|
|
251
|
+
: value;
|
|
252
|
+
|
|
253
|
+
out.valid = out.valid && isValid(value);
|
|
254
|
+
// apply dx/dy transform
|
|
255
|
+
out[channel] =
|
|
256
|
+
scale === 'x' && Number.isFinite(scaled) ? (scaled as number) + dx : scaled;
|
|
257
|
+
out[channel] =
|
|
258
|
+
scale === 'y' && Number.isFinite(scaled) ? (scaled as number) + dy : scaled;
|
|
259
|
+
} else if (defaults[channel]) {
|
|
260
|
+
out[channel] = defaults[channel];
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return [out];
|
|
265
|
+
})
|
|
266
|
+
);
|
|
267
|
+
</script>
|
|
268
|
+
|
|
269
|
+
{#if errors.length}
|
|
270
|
+
<text transform="translate(10,10)">
|
|
271
|
+
{#each errors as error, i}
|
|
272
|
+
<tspan x="0" dy={i ? 14 : 0}>{error}</tspan>
|
|
273
|
+
{/each}
|
|
274
|
+
</text>
|
|
275
|
+
{:else if children}
|
|
276
|
+
{@render children({
|
|
277
|
+
mark,
|
|
278
|
+
usedScales,
|
|
279
|
+
scaledData
|
|
280
|
+
})}
|
|
281
|
+
{/if}
|
|
282
|
+
|
|
283
|
+
<style>
|
|
284
|
+
text {
|
|
285
|
+
stroke: var(--plot-bg);
|
|
286
|
+
fill: crimson;
|
|
287
|
+
font-size: 11px;
|
|
288
|
+
stroke-width: 3px;
|
|
289
|
+
font-weight: bold;
|
|
290
|
+
paint-order: stroke fill;
|
|
291
|
+
}
|
|
292
|
+
</style>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type Snippet } from 'svelte';
|
|
2
|
+
import type { ScaledChannelName, MarkType, DataRecord, ChannelName, GenericMarkOptions, ChannelAccessor, BaseMarkProps, ScaleName, RawValue, ScaledDataRecord, ScaleType } from './types.js';
|
|
3
|
+
import { getUsedScales } from './helpers/scales.js';
|
|
4
|
+
type MarkProps = {
|
|
5
|
+
data?: DataRecord[];
|
|
6
|
+
automatic?: boolean;
|
|
7
|
+
type: MarkType;
|
|
8
|
+
channels?: ScaledChannelName[];
|
|
9
|
+
required?: ScaledChannelName[];
|
|
10
|
+
requiredScales?: Partial<Record<ScaleName, ScaleType[]>>;
|
|
11
|
+
children?: Snippet<[
|
|
12
|
+
{
|
|
13
|
+
mark: Mark<GenericMarkOptions>;
|
|
14
|
+
usedScales: ReturnType<typeof getUsedScales>;
|
|
15
|
+
scaledData: ScaledDataRecord[];
|
|
16
|
+
}
|
|
17
|
+
]>;
|
|
18
|
+
defaults?: Partial<Record<ScaledChannelName, RawValue>>;
|
|
19
|
+
} & Partial<Record<ChannelName, ChannelAccessor>> & Partial<BaseMarkProps>;
|
|
20
|
+
declare const Mark: import("svelte").Component<MarkProps, {}, "">;
|
|
21
|
+
type Mark = ReturnType<typeof Mark>;
|
|
22
|
+
export default Mark;
|
package/dist/Plot.svelte
CHANGED
|
@@ -1,168 +1,163 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
let
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
onmousemove({ ...evt, plot });
|
|
72
|
-
}
|
|
73
|
-
$inspect(plot.x.domain);
|
|
74
|
-
let hasLegend = $derived(color?.legend || symbol?.legend);
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
The Plot component is the container for plots. It collects the marks with
|
|
4
|
+
their data and channels and computes the shared scales.
|
|
5
|
+
|
|
6
|
+
The Plot component is split into two parts. This is the outer Plot which
|
|
7
|
+
provides convenient defaults and automatically adds axes etc to the grapihcs.
|
|
8
|
+
The downside is that it adds a bunch of imports that you may not be using.
|
|
9
|
+
To help with this you can use the core/Plot component directly for a more
|
|
10
|
+
low-level Plot wrapper.
|
|
11
|
+
-->
|
|
12
|
+
<script lang="ts">
|
|
13
|
+
import Plot from './core/Plot.svelte';
|
|
14
|
+
|
|
15
|
+
import type { PlotOptions } from './types.js';
|
|
16
|
+
|
|
17
|
+
// implicit marks
|
|
18
|
+
import AxisX from './marks/AxisX.svelte';
|
|
19
|
+
import AxisY from './marks/AxisY.svelte';
|
|
20
|
+
import ColorLegend from './marks/ColorLegend.svelte';
|
|
21
|
+
import Frame from './marks/Frame.svelte';
|
|
22
|
+
import GridX from './marks/GridX.svelte';
|
|
23
|
+
import GridY from './marks/GridY.svelte';
|
|
24
|
+
import SymbolLegend from './marks/SymbolLegend.svelte';
|
|
25
|
+
import FacetAxes from './core/FacetAxes.svelte';
|
|
26
|
+
|
|
27
|
+
// automatic scales
|
|
28
|
+
import { autoScale, autoScaleColor } from './helpers/autoScales.js';
|
|
29
|
+
import { namedProjection } from './helpers/autoProjection.js';
|
|
30
|
+
import { isObject } from './helpers/index.js';
|
|
31
|
+
|
|
32
|
+
let {
|
|
33
|
+
header: userHeader,
|
|
34
|
+
footer: userFooter,
|
|
35
|
+
overlay,
|
|
36
|
+
underlay,
|
|
37
|
+
children: parentChildren,
|
|
38
|
+
testid,
|
|
39
|
+
facet,
|
|
40
|
+
projection,
|
|
41
|
+
...restOptions
|
|
42
|
+
}: Partial<PlotOptions> = $props();
|
|
43
|
+
|
|
44
|
+
const projectionOpts = $derived.by(() => {
|
|
45
|
+
if (
|
|
46
|
+
projection &&
|
|
47
|
+
typeof projection !== 'function' &&
|
|
48
|
+
typeof projection?.type !== 'function'
|
|
49
|
+
) {
|
|
50
|
+
const { type: projFactory, aspectRatio } = namedProjection(
|
|
51
|
+
isObject(projection) ? projection.type : projection
|
|
52
|
+
);
|
|
53
|
+
return {
|
|
54
|
+
...(isObject(projection) ? projection : {}),
|
|
55
|
+
type: projFactory,
|
|
56
|
+
aspectRatio
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return projection;
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const scales = $derived(
|
|
63
|
+
Object.fromEntries(
|
|
64
|
+
['x', 'y', 'r', 'color', 'opacity', 'symbol', 'length', 'fx', 'fy'].map((scale) => {
|
|
65
|
+
const scaleOpts = restOptions[scale] || {};
|
|
66
|
+
const scaleFn = scaleOpts.scale || (scale === 'color' ? autoScaleColor : autoScale);
|
|
67
|
+
return [scale, { ...scaleOpts, scale: scaleFn }];
|
|
68
|
+
})
|
|
69
|
+
)
|
|
70
|
+
);
|
|
75
71
|
</script>
|
|
76
72
|
|
|
77
|
-
|
|
78
|
-
<
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
73
|
+
{#snippet header()}
|
|
74
|
+
{#if restOptions.title}<h2>{restOptions.title}</h2>{/if}
|
|
75
|
+
{#if restOptions.subtitle}<h3>{restOptions.subtitle}</h3>{/if}
|
|
76
|
+
<!-- also pass on user header -->
|
|
77
|
+
{#if userHeader}{@render userHeader()}{/if}
|
|
78
|
+
{#if restOptions.color?.legend}
|
|
79
|
+
<ColorLegend />
|
|
80
|
+
{/if}
|
|
81
|
+
{#if restOptions.symbol?.legend}
|
|
82
|
+
<SymbolLegend />
|
|
83
|
+
{/if}
|
|
84
|
+
{/snippet}
|
|
85
|
+
|
|
86
|
+
{#snippet footer()}
|
|
87
|
+
{#if restOptions.caption}<div>{restOptions.caption}</div>{/if}
|
|
88
|
+
{#if userFooter}{@render userFooter()}{/if}
|
|
89
|
+
{/snippet}
|
|
87
90
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
<!-- There's a bug triggering RangeError: Maximum call stack size exceeded
|
|
92
|
+
when using SveltePlot in ssr, so for now, we're disabling it -->
|
|
93
|
+
|
|
94
|
+
<Plot
|
|
95
|
+
{overlay}
|
|
96
|
+
{underlay}
|
|
97
|
+
{...restOptions}
|
|
98
|
+
header={userHeader ||
|
|
99
|
+
restOptions.title ||
|
|
100
|
+
restOptions.subtitle ||
|
|
101
|
+
restOptions.color?.legend ||
|
|
102
|
+
restOptions.symbol?.legend
|
|
103
|
+
? header
|
|
104
|
+
: null}
|
|
105
|
+
footer={userFooter || restOptions?.caption ? footer : null}
|
|
106
|
+
projection={projectionOpts}
|
|
107
|
+
implicitScales
|
|
108
|
+
{...scales}>
|
|
109
|
+
{#snippet children({
|
|
110
|
+
hasProjection,
|
|
111
|
+
hasExplicitAxisX,
|
|
112
|
+
hasExplicitAxisY,
|
|
113
|
+
hasExplicitGridX,
|
|
114
|
+
hasExplicitGridY,
|
|
115
|
+
options,
|
|
116
|
+
scales,
|
|
117
|
+
...restProps
|
|
118
|
+
})}
|
|
119
|
+
<!-- implicit axes -->
|
|
120
|
+
{#if !hasProjection && !hasExplicitAxisX}
|
|
121
|
+
{#if options.axes && (options.x.axis === 'top' || options.x.axis === 'both')}
|
|
94
122
|
<AxisX anchor="top" automatic />
|
|
95
123
|
{/if}
|
|
124
|
+
{#if options.axes && (options.x.axis === 'bottom' || options.x.axis === 'both')}
|
|
125
|
+
<AxisX anchor="bottom" automatic />
|
|
126
|
+
{/if}
|
|
96
127
|
{/if}
|
|
97
|
-
{#if !
|
|
98
|
-
|
|
99
|
-
{#if plot.options.y.axis === 'left' || plot.options.y.axis === 'both'}
|
|
128
|
+
{#if !hasProjection && !hasExplicitAxisY}
|
|
129
|
+
{#if options.axes && (options.y.axis === 'left' || options.y.axis === 'both')}
|
|
100
130
|
<AxisY anchor="left" automatic />
|
|
101
131
|
{/if}
|
|
102
|
-
{#if
|
|
132
|
+
{#if options.axes && (options.y.axis === 'right' || options.y.axis === 'both')}
|
|
103
133
|
<AxisY anchor="right" automatic />
|
|
104
134
|
{/if}
|
|
105
135
|
{/if}
|
|
106
|
-
<!--
|
|
107
|
-
{#if
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
{/if}
|
|
128
|
-
|
|
129
|
-
{#if footer || plot.options.caption}
|
|
130
|
-
<div class="plot-footer">
|
|
131
|
-
{#if plot.options.caption}
|
|
132
|
-
<figcaption>{@html plot.options.caption}</figcaption>
|
|
133
|
-
{/if}
|
|
134
|
-
{#if footer}{@render footer(plot)}{/if}
|
|
135
|
-
</div>
|
|
136
|
-
{/if}
|
|
137
|
-
|
|
138
|
-
<div class="overlay">
|
|
139
|
-
{#if overlay}{@render overlay(plot)}{/if}
|
|
140
|
-
</div>
|
|
141
|
-
</figure>
|
|
136
|
+
<!-- implicit grids -->
|
|
137
|
+
{#if !hasExplicitGridX && (options.grid || options.x.grid)}
|
|
138
|
+
<GridX automatic />
|
|
139
|
+
{/if}
|
|
140
|
+
{#if !hasExplicitGridY && (options.grid || options.y.grid)}
|
|
141
|
+
<GridY automatic />
|
|
142
|
+
{/if}
|
|
143
|
+
<!-- implicit frame -->
|
|
144
|
+
{#if options.frame}
|
|
145
|
+
<Frame automatic />
|
|
146
|
+
{/if}
|
|
147
|
+
{@render parentChildren?.({
|
|
148
|
+
options,
|
|
149
|
+
scales,
|
|
150
|
+
...restProps
|
|
151
|
+
})}
|
|
152
|
+
{/snippet}
|
|
153
|
+
{#snippet facetAxes()}
|
|
154
|
+
<FacetAxes />
|
|
155
|
+
{/snippet}
|
|
156
|
+
</Plot>
|
|
142
157
|
|
|
143
158
|
<style>
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
display: flex;
|
|
148
|
-
flex-direction: column;
|
|
149
|
-
}
|
|
150
|
-
figure .plot-header {
|
|
151
|
-
order: 1;
|
|
152
|
-
}
|
|
153
|
-
svg {
|
|
154
|
-
order: 2;
|
|
155
|
-
overflow: visible;
|
|
156
|
-
}
|
|
157
|
-
figure .plot-footer {
|
|
158
|
-
order: 3;
|
|
159
|
-
}
|
|
160
|
-
.overlay {
|
|
161
|
-
position: absolute;
|
|
162
|
-
pointer-events: none;
|
|
163
|
-
top: 0;
|
|
164
|
-
left: 0;
|
|
165
|
-
bottom: 0;
|
|
166
|
-
right: 0;
|
|
159
|
+
:root {
|
|
160
|
+
--plot-bg: white;
|
|
161
|
+
--plot-fg: currentColor;
|
|
167
162
|
}
|
|
168
163
|
</style>
|