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
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
The core Plot component handles the main layout of the plot but it does not
|
|
4
|
+
include smart automatic scales and marks. It is to be used in cases where
|
|
5
|
+
you want to reduce the footprint of the plot to the bare minimum.
|
|
6
|
+
|
|
7
|
+
Keep in mind that you will have to create your own scales if you're using
|
|
8
|
+
this component.
|
|
9
|
+
-->
|
|
10
|
+
<script lang="ts">
|
|
11
|
+
import { getContext, setContext } from 'svelte';
|
|
12
|
+
import { SvelteMap } from 'svelte/reactivity';
|
|
13
|
+
import { writable } from 'svelte/store';
|
|
14
|
+
|
|
15
|
+
import type {
|
|
16
|
+
PlotOptions,
|
|
17
|
+
GenericMarkOptions,
|
|
18
|
+
Mark,
|
|
19
|
+
PlotScales,
|
|
20
|
+
ScaleName,
|
|
21
|
+
PlotScale,
|
|
22
|
+
PlotDefaults,
|
|
23
|
+
PlotState,
|
|
24
|
+
RawValue
|
|
25
|
+
} from '../types.js';
|
|
26
|
+
import FacetGrid from './FacetGrid.svelte';
|
|
27
|
+
|
|
28
|
+
import mergeDeep from '../helpers/mergeDeep.js';
|
|
29
|
+
import { computeScales, projectXY } from '../helpers/scales.js';
|
|
30
|
+
import { CHANNEL_SCALE, SCALES } from '../constants.js';
|
|
31
|
+
import { scale } from 'svelte/transition';
|
|
32
|
+
|
|
33
|
+
let {
|
|
34
|
+
header,
|
|
35
|
+
footer,
|
|
36
|
+
overlay,
|
|
37
|
+
underlay,
|
|
38
|
+
children,
|
|
39
|
+
facetAxes,
|
|
40
|
+
testid,
|
|
41
|
+
facet,
|
|
42
|
+
class: className = '',
|
|
43
|
+
css,
|
|
44
|
+
...initialOpts
|
|
45
|
+
}: Partial<PlotOptions> = $props();
|
|
46
|
+
|
|
47
|
+
// automatic margins can be applied by the marks, registered
|
|
48
|
+
// with their respective unique identifier as keys
|
|
49
|
+
let autoMarginLeft = writable(new SvelteMap<string, number>());
|
|
50
|
+
let autoMarginRight = writable(new SvelteMap<string, number>());
|
|
51
|
+
let autoMarginBottom = writable(new SvelteMap<string, number>());
|
|
52
|
+
let autoMarginTop = writable(new SvelteMap<string, number>());
|
|
53
|
+
|
|
54
|
+
// autoMargin stores are shared via context
|
|
55
|
+
setContext('svelteplot/autoMargins', {
|
|
56
|
+
autoMarginLeft,
|
|
57
|
+
autoMarginRight,
|
|
58
|
+
autoMarginBottom,
|
|
59
|
+
autoMarginTop
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// compute maximum margins to either side of the plot from the
|
|
63
|
+
// automatic margins defined by marks
|
|
64
|
+
const maxMarginLeft = $derived(Math.max(...$autoMarginLeft.values()));
|
|
65
|
+
const maxMarginRight = $derived(Math.max(...$autoMarginRight.values()));
|
|
66
|
+
const maxMarginBottom = $derived(Math.max(...$autoMarginBottom.values()));
|
|
67
|
+
const maxMarginTop = $derived(Math.max(...$autoMarginTop.values()));
|
|
68
|
+
|
|
69
|
+
// default settings in the plot and marks can be overwritten by
|
|
70
|
+
// defining the svelteplot/defaults context outside of Plot
|
|
71
|
+
const DEFAULTS: PlotDefaults = {
|
|
72
|
+
axisXAnchor: 'bottom',
|
|
73
|
+
axisYAnchor: 'left',
|
|
74
|
+
xTickSpacing: 80,
|
|
75
|
+
yTickSpacing: 50,
|
|
76
|
+
height: 350,
|
|
77
|
+
initialWidth: 500,
|
|
78
|
+
inset: 0,
|
|
79
|
+
colorScheme: 'turbo',
|
|
80
|
+
dotRadius: 3,
|
|
81
|
+
frame: false,
|
|
82
|
+
axes: true,
|
|
83
|
+
grid: false,
|
|
84
|
+
categoricalColorScheme: 'observable10',
|
|
85
|
+
pointScaleHeight: 18,
|
|
86
|
+
bandScaleHeight: 30,
|
|
87
|
+
locale: 'en-US',
|
|
88
|
+
numberFormat: {
|
|
89
|
+
style: 'decimal',
|
|
90
|
+
notation: 'compact',
|
|
91
|
+
compactDisplay: 'short'
|
|
92
|
+
},
|
|
93
|
+
markerDotRadius: 3,
|
|
94
|
+
...getContext<Partial<PlotDefaults>>('svelteplot/defaults')
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
let width = $state(DEFAULTS.initialWidth);
|
|
98
|
+
|
|
99
|
+
setContext('svelteplot/_defaults', DEFAULTS);
|
|
100
|
+
|
|
101
|
+
// information that influences the default plot options
|
|
102
|
+
type PlotOptionsParameters = {
|
|
103
|
+
explicitScales: Set<ScaleName>;
|
|
104
|
+
explicitDomains: Set<ScaleName>;
|
|
105
|
+
hasProjection: boolean;
|
|
106
|
+
margins?: number;
|
|
107
|
+
inset?: number;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
let scaleCounter = $state(0);
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* the marks used in the plot
|
|
114
|
+
*/
|
|
115
|
+
let marks = $state.raw<Mark<GenericMarkOptions>[]>([]);
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
*
|
|
119
|
+
*/
|
|
120
|
+
const explicitMarks = $derived(marks.filter((m) => !m.options.automatic));
|
|
121
|
+
|
|
122
|
+
// knowing if the plot includes explicit grids and marks is useful for
|
|
123
|
+
// including the automatic/implicit axes/grids
|
|
124
|
+
const hasExplicitAxisX = $derived(!!explicitMarks.find((m) => m.type === 'axisX'));
|
|
125
|
+
const hasExplicitAxisY = $derived(!!explicitMarks.find((m) => m.type === 'axisY'));
|
|
126
|
+
const hasExplicitGridX = $derived(!!explicitMarks.find((m) => m.type === 'gridX'));
|
|
127
|
+
const hasExplicitGridY = $derived(!!explicitMarks.find((m) => m.type === 'gridY'));
|
|
128
|
+
|
|
129
|
+
const explicitScales = $derived(
|
|
130
|
+
new Set(
|
|
131
|
+
explicitMarks
|
|
132
|
+
.map((m) =>
|
|
133
|
+
[...m.scales.values()].filter((scale) => {
|
|
134
|
+
// remove the scales where no input channels are defined for this mark
|
|
135
|
+
const channels = Object.entries(CHANNEL_SCALE)
|
|
136
|
+
.filter(([, scaleName]) => scale === scaleName)
|
|
137
|
+
.map(([channel]) => channel);
|
|
138
|
+
return channels.find((channel) => m.options[channel] != null);
|
|
139
|
+
})
|
|
140
|
+
)
|
|
141
|
+
.flat(1)
|
|
142
|
+
)
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
const explicitDomains = $derived(
|
|
146
|
+
new Set(SCALES.filter((scale) => !!initialOpts[scale]?.domain))
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
// one-dimensional plots have different automatic margins and heights
|
|
150
|
+
const isOneDimensional = $derived(explicitScales.has('x') !== explicitScales.has('y'));
|
|
151
|
+
|
|
152
|
+
// construct the plot options from the user-defined options (top-level props) as well
|
|
153
|
+
// as extending them from smart context-aware defaults
|
|
154
|
+
const plotOptions = $derived(
|
|
155
|
+
extendPlotOptions(initialOpts, {
|
|
156
|
+
explicitScales,
|
|
157
|
+
explicitDomains,
|
|
158
|
+
hasProjection: !!initialOpts.projection,
|
|
159
|
+
margins: initialOpts.margins,
|
|
160
|
+
inset: initialOpts.inset
|
|
161
|
+
})
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
// if the plot is showing filled dot marks we're using different defaults
|
|
165
|
+
// for the symbol axis range, so we're passing on this info to the createScales
|
|
166
|
+
// function below
|
|
167
|
+
const hasFilledDotMarks = $derived(
|
|
168
|
+
!!explicitMarks.find((d) => d.type === 'dot' && d.options.fill)
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
// compute preliminary scales with a fixed height, since we don't have
|
|
172
|
+
// height defined at this point, but still need some of the scales
|
|
173
|
+
const preScales: PlotScales = $derived(
|
|
174
|
+
computeScales(plotOptions, width, 400, hasFilledDotMarks, marks, DEFAULTS)
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
const hasProjection = $derived(!!preScales.projection);
|
|
178
|
+
|
|
179
|
+
const plotWidth = $derived(width - plotOptions.marginLeft - plotOptions.marginRight);
|
|
180
|
+
|
|
181
|
+
// the facet and y domain counts are used for computing the automatic height
|
|
182
|
+
const xFacetCount = $derived(Math.max(1, preScales.fx.domain.length));
|
|
183
|
+
const yFacetCount = $derived(Math.max(1, preScales.fy.domain.length));
|
|
184
|
+
const yDomainCount = $derived(
|
|
185
|
+
isOneDimensional && explicitScales.has('x') ? 1 : preScales.y.domain.length
|
|
186
|
+
);
|
|
187
|
+
// compute the (automatic) height based on various factors:
|
|
188
|
+
// - if the plot used a projection and the projection requires an aspect ratio,
|
|
189
|
+
// we use it, but adjust for the facet counts
|
|
190
|
+
// - if the user defined a domain-aspect ratio, we use the heightFromAspect
|
|
191
|
+
// method to compute the height based on the preliminary x and y scales
|
|
192
|
+
// - for one-dimensional scales using the x scale we set a fixed height
|
|
193
|
+
// - for y band-scales we use the number of items in the y domain
|
|
194
|
+
const height = $derived(
|
|
195
|
+
plotOptions.height === 'auto'
|
|
196
|
+
? Math.round(
|
|
197
|
+
preScales.projection && preScales.projection.aspectRatio
|
|
198
|
+
? ((plotWidth * preScales.projection.aspectRatio) / xFacetCount) *
|
|
199
|
+
yFacetCount +
|
|
200
|
+
plotOptions.marginTop +
|
|
201
|
+
plotOptions.marginBottom
|
|
202
|
+
: plotOptions.aspectRatio
|
|
203
|
+
? heightFromAspect(
|
|
204
|
+
preScales.x,
|
|
205
|
+
preScales.y,
|
|
206
|
+
plotOptions.aspectRatio,
|
|
207
|
+
plotWidth,
|
|
208
|
+
plotOptions.marginTop,
|
|
209
|
+
plotOptions.marginBottom
|
|
210
|
+
)
|
|
211
|
+
: ((isOneDimensional && explicitScales.has('x')) || !explicitMarks.length
|
|
212
|
+
? yFacetCount * DEFAULTS.bandScaleHeight
|
|
213
|
+
: preScales.y.type === 'band'
|
|
214
|
+
? yFacetCount * yDomainCount * DEFAULTS.bandScaleHeight
|
|
215
|
+
: preScales.y.type === 'point'
|
|
216
|
+
? yFacetCount * yDomainCount * DEFAULTS.pointScaleHeight
|
|
217
|
+
: DEFAULTS.height) +
|
|
218
|
+
plotOptions.marginTop +
|
|
219
|
+
plotOptions.marginBottom
|
|
220
|
+
)
|
|
221
|
+
: typeof plotOptions.height === 'function'
|
|
222
|
+
? plotOptions.height(plotWidth)
|
|
223
|
+
: plotOptions.height
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
const plotHeight = $derived(height - plotOptions.marginTop - plotOptions.marginBottom);
|
|
227
|
+
|
|
228
|
+
// TODO: check if there's still a reason to store and expose the plot body element
|
|
229
|
+
let plotBody: HTMLDivElement | null = $state(null);
|
|
230
|
+
|
|
231
|
+
let facetWidth: number | null = $state(null);
|
|
232
|
+
let facetHeight: number | null = $state(null);
|
|
233
|
+
|
|
234
|
+
let plotState: PlotState = $state(computePlotState());
|
|
235
|
+
|
|
236
|
+
$effect(() => {
|
|
237
|
+
plotState = computePlotState();
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
function computePlotState() {
|
|
241
|
+
// now that we know the actual height and facet dimensions, we can compute
|
|
242
|
+
// the scales used in all the marks
|
|
243
|
+
const scales = computeScales(
|
|
244
|
+
plotOptions,
|
|
245
|
+
facetWidth || width,
|
|
246
|
+
facetHeight || height,
|
|
247
|
+
hasFilledDotMarks,
|
|
248
|
+
marks,
|
|
249
|
+
DEFAULTS
|
|
250
|
+
);
|
|
251
|
+
const colorSymbolRedundant =
|
|
252
|
+
scales.color.uniqueScaleProps?.size === 1 &&
|
|
253
|
+
scales.symbol.uniqueScaleProps?.size === 1 &&
|
|
254
|
+
[...scales.color.uniqueScaleProps?.values()][0] ===
|
|
255
|
+
[...scales.symbol.uniqueScaleProps?.values()][0];
|
|
256
|
+
return {
|
|
257
|
+
options: plotOptions,
|
|
258
|
+
width,
|
|
259
|
+
height,
|
|
260
|
+
facetWidth,
|
|
261
|
+
facetHeight,
|
|
262
|
+
plotHeight,
|
|
263
|
+
plotWidth,
|
|
264
|
+
scales,
|
|
265
|
+
colorSymbolRedundant,
|
|
266
|
+
hasFilledDotMarks,
|
|
267
|
+
body: plotBody,
|
|
268
|
+
css
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
setContext('svelteplot', {
|
|
273
|
+
/**
|
|
274
|
+
* used by the Mark component to register new marks to the plot
|
|
275
|
+
*/
|
|
276
|
+
addMark(mark: Mark<GenericMarkOptions>) {
|
|
277
|
+
if (marks.find((m) => m.id === mark.id)) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
marks = [...marks, mark];
|
|
282
|
+
},
|
|
283
|
+
/**
|
|
284
|
+
* used by the Mark component to update marks when its props change
|
|
285
|
+
*/
|
|
286
|
+
updateMark(mark: Mark<GenericMarkOptions>) {
|
|
287
|
+
// marks = marks.map((m) => (m.id === mark.id ? mark : m));
|
|
288
|
+
},
|
|
289
|
+
/**
|
|
290
|
+
* used by the Mark component to unregister marks when their
|
|
291
|
+
* respective components get removed from the plot
|
|
292
|
+
*/
|
|
293
|
+
removeMark(mark: Mark<GenericMarkOptions>) {
|
|
294
|
+
marks = marks.filter((m) => m.id !== mark.id);
|
|
295
|
+
},
|
|
296
|
+
getPlotState() {
|
|
297
|
+
return plotState;
|
|
298
|
+
},
|
|
299
|
+
getTopLevelFacet() {
|
|
300
|
+
// we need to expose the facet options to allow marks to
|
|
301
|
+
// react to state changes by updating the fx and fy channels
|
|
302
|
+
return facet;
|
|
303
|
+
},
|
|
304
|
+
updateDimensions(w: number, h: number) {
|
|
305
|
+
if (facetWidth !== w) facetWidth = w;
|
|
306
|
+
if (facetHeight !== h) facetHeight = h;
|
|
307
|
+
},
|
|
308
|
+
updatePlotState() {
|
|
309
|
+
plotState = computePlotState();
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// TODO: perhaps we don't need this anymore
|
|
314
|
+
export function getWidth() {
|
|
315
|
+
return width;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function heightFromAspect(
|
|
319
|
+
x: PlotScale,
|
|
320
|
+
y: PlotScale,
|
|
321
|
+
aspectRatio: number,
|
|
322
|
+
plotWidth: number,
|
|
323
|
+
marginTop: number,
|
|
324
|
+
marginBottom: number
|
|
325
|
+
) {
|
|
326
|
+
const xDomainExtent =
|
|
327
|
+
x.type === 'band' || x.type === 'point'
|
|
328
|
+
? x.domain.length
|
|
329
|
+
: Math.abs(x.domain[1] - x.domain[0]);
|
|
330
|
+
const yDomainExtent =
|
|
331
|
+
y.type === 'band' || y.type === 'point'
|
|
332
|
+
? y.domain.length
|
|
333
|
+
: Math.abs(y.domain[1] - y.domain[0]);
|
|
334
|
+
return (
|
|
335
|
+
((plotWidth / xDomainExtent) * yDomainExtent) / aspectRatio + marginTop + marginBottom
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function extendPlotOptions(
|
|
340
|
+
initialOpts: Partial<PlotOptions>,
|
|
341
|
+
opts: PlotOptionsParameters
|
|
342
|
+
): PlotOptions {
|
|
343
|
+
return mergeDeep<PlotOptions>({}, smartDefaultPlotOptions(opts), initialOpts);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* compute smart default options for the plot based on the scales and marks
|
|
348
|
+
*/
|
|
349
|
+
function smartDefaultPlotOptions({
|
|
350
|
+
explicitScales,
|
|
351
|
+
explicitDomains,
|
|
352
|
+
hasProjection,
|
|
353
|
+
margins
|
|
354
|
+
}: PlotOptionsParameters): PlotOptions {
|
|
355
|
+
const autoXAxis = explicitScales.has('x') || explicitDomains.has('x');
|
|
356
|
+
const autoYAxis = explicitScales.has('y') || explicitDomains.has('y');
|
|
357
|
+
const isOneDimensional = autoXAxis !== autoYAxis;
|
|
358
|
+
const oneDimX = autoXAxis && !autoYAxis;
|
|
359
|
+
const oneDimY = autoYAxis && !autoXAxis;
|
|
360
|
+
return {
|
|
361
|
+
title: '',
|
|
362
|
+
subtitle: '',
|
|
363
|
+
caption: '',
|
|
364
|
+
height: 'auto',
|
|
365
|
+
// maxWidth: oneDimY ? `${60 * e}px` : undefined,
|
|
366
|
+
marginLeft: hasProjection
|
|
367
|
+
? 0
|
|
368
|
+
: margins != null
|
|
369
|
+
? margins
|
|
370
|
+
: Math.max(maxMarginLeft + 1, 1),
|
|
371
|
+
marginRight: hasProjection
|
|
372
|
+
? 0
|
|
373
|
+
: margins != null
|
|
374
|
+
? margins
|
|
375
|
+
: oneDimY
|
|
376
|
+
? 0
|
|
377
|
+
: Math.max(maxMarginRight + 1, 4),
|
|
378
|
+
marginTop: hasProjection
|
|
379
|
+
? 0
|
|
380
|
+
: margins != null
|
|
381
|
+
? margins
|
|
382
|
+
: oneDimX
|
|
383
|
+
? 0
|
|
384
|
+
: Math.max(5, maxMarginTop),
|
|
385
|
+
marginBottom: hasProjection
|
|
386
|
+
? 0
|
|
387
|
+
: margins != null
|
|
388
|
+
? margins
|
|
389
|
+
: Math.max(5, maxMarginBottom),
|
|
390
|
+
inset: isOneDimensional ? 10 : DEFAULTS.inset,
|
|
391
|
+
grid: DEFAULTS.grid,
|
|
392
|
+
axes: DEFAULTS.axes,
|
|
393
|
+
frame: DEFAULTS.frame,
|
|
394
|
+
projection: null,
|
|
395
|
+
aspectRatio: null,
|
|
396
|
+
facet: {},
|
|
397
|
+
padding: 0.1,
|
|
398
|
+
x: {
|
|
399
|
+
type: 'auto',
|
|
400
|
+
axis: autoXAxis ? DEFAULTS.axisXAnchor : false,
|
|
401
|
+
labelAnchor: 'auto',
|
|
402
|
+
reverse: false,
|
|
403
|
+
clamp: false,
|
|
404
|
+
nice: false,
|
|
405
|
+
zero: false,
|
|
406
|
+
round: false,
|
|
407
|
+
percent: false,
|
|
408
|
+
align: 0.5,
|
|
409
|
+
tickSpacing: DEFAULTS.xTickSpacing,
|
|
410
|
+
tickFormat: 'auto',
|
|
411
|
+
grid: false
|
|
412
|
+
},
|
|
413
|
+
y: {
|
|
414
|
+
type: 'auto',
|
|
415
|
+
axis: autoYAxis ? DEFAULTS.axisYAnchor : false,
|
|
416
|
+
labelAnchor: 'auto',
|
|
417
|
+
reverse: false,
|
|
418
|
+
clamp: false,
|
|
419
|
+
nice: false,
|
|
420
|
+
zero: false,
|
|
421
|
+
round: false,
|
|
422
|
+
percent: false,
|
|
423
|
+
align: 0.5,
|
|
424
|
+
tickSpacing: DEFAULTS.yTickSpacing,
|
|
425
|
+
tickFormat: 'auto',
|
|
426
|
+
grid: false
|
|
427
|
+
},
|
|
428
|
+
opacity: {
|
|
429
|
+
type: 'linear',
|
|
430
|
+
reverse: false,
|
|
431
|
+
clamp: false,
|
|
432
|
+
nice: false,
|
|
433
|
+
zero: false,
|
|
434
|
+
round: false,
|
|
435
|
+
tickSpacing: 0,
|
|
436
|
+
percent: false,
|
|
437
|
+
padding: 0.1,
|
|
438
|
+
align: 0.5
|
|
439
|
+
},
|
|
440
|
+
r: {
|
|
441
|
+
type: 'sqrt',
|
|
442
|
+
reverse: false,
|
|
443
|
+
clamp: false,
|
|
444
|
+
nice: false,
|
|
445
|
+
zero: true,
|
|
446
|
+
percent: false,
|
|
447
|
+
round: false,
|
|
448
|
+
padding: 0,
|
|
449
|
+
align: 0
|
|
450
|
+
},
|
|
451
|
+
color: { type: 'auto' },
|
|
452
|
+
length: { type: 'linear' },
|
|
453
|
+
symbol: { type: 'ordinal' },
|
|
454
|
+
fx: { type: 'band', axis: 'top' },
|
|
455
|
+
fy: { type: 'band', axis: 'right' },
|
|
456
|
+
locale: DEFAULTS.locale
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const mapXY = $derived((x: RawValue, y: RawValue) => {
|
|
461
|
+
const [px, py] = projectXY(plotState.scales, x, y);
|
|
462
|
+
return { x: px, y: py };
|
|
463
|
+
});
|
|
464
|
+
</script>
|
|
465
|
+
|
|
466
|
+
<figure
|
|
467
|
+
class="svelteplot {className}"
|
|
468
|
+
bind:clientWidth={width}
|
|
469
|
+
style:max-width={plotOptions.maxWidth}
|
|
470
|
+
data-testid={testid}>
|
|
471
|
+
{#if header}
|
|
472
|
+
<div class="plot-header">
|
|
473
|
+
{#if header}{@render header()}{/if}
|
|
474
|
+
</div>
|
|
475
|
+
{/if}
|
|
476
|
+
<div class="plot-body" bind:this={plotBody}>
|
|
477
|
+
{#if underlay}<div class="plot-underlay">{@render underlay(plotOptions)}</div>{/if}
|
|
478
|
+
<svg
|
|
479
|
+
{width}
|
|
480
|
+
{height}
|
|
481
|
+
fill="currentColor"
|
|
482
|
+
viewBox="0 0 {width} {height}"
|
|
483
|
+
font-family="system-ui, sans-serif">
|
|
484
|
+
{@render facetAxes?.()}
|
|
485
|
+
<FacetGrid marks={explicitMarks}>
|
|
486
|
+
{#if children}
|
|
487
|
+
{@render children({
|
|
488
|
+
width,
|
|
489
|
+
height,
|
|
490
|
+
options: plotOptions,
|
|
491
|
+
scales: plotState.scales,
|
|
492
|
+
mapXY,
|
|
493
|
+
hasProjection,
|
|
494
|
+
hasExplicitAxisX,
|
|
495
|
+
hasExplicitAxisY,
|
|
496
|
+
hasExplicitGridX,
|
|
497
|
+
hasExplicitGridY
|
|
498
|
+
})}
|
|
499
|
+
{/if}
|
|
500
|
+
</FacetGrid>
|
|
501
|
+
</svg>
|
|
502
|
+
{#if overlay}<div class="plot-overlay">{@render overlay()}</div>{/if}
|
|
503
|
+
</div>
|
|
504
|
+
{#if footer}
|
|
505
|
+
<figcaption class="plot-footer">
|
|
506
|
+
{#if footer}{@render footer()}{/if}
|
|
507
|
+
</figcaption>
|
|
508
|
+
{/if}
|
|
509
|
+
</figure>
|
|
510
|
+
|
|
511
|
+
<style>
|
|
512
|
+
:root {
|
|
513
|
+
--plot-bg: white;
|
|
514
|
+
--plot-fg: currentColor;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
figure {
|
|
518
|
+
margin: 0;
|
|
519
|
+
padding: 0;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
.plot-body {
|
|
523
|
+
position: relative;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
.plot-overlay,
|
|
527
|
+
.plot-underlay {
|
|
528
|
+
position: absolute;
|
|
529
|
+
top: 0;
|
|
530
|
+
left: 0;
|
|
531
|
+
right: 0;
|
|
532
|
+
bottom: 0;
|
|
533
|
+
pointer-events: none;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
.plot-underlay {
|
|
537
|
+
z-index: -1;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
.plot-header {
|
|
541
|
+
margin-top: 1rem;
|
|
542
|
+
display: flex;
|
|
543
|
+
flex-direction: column;
|
|
544
|
+
row-gap: 0.35rem;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
.plot-header :global(h2),
|
|
548
|
+
.plot-header :global(h3) {
|
|
549
|
+
margin: 0 !important;
|
|
550
|
+
padding: 0 !important;
|
|
551
|
+
border: 0 !important;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
.plot-header :global(h3) {
|
|
555
|
+
font-weight: 500;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
.plot-footer {
|
|
559
|
+
margin-bottom: 2rem;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
.plot-footer :global(> div) {
|
|
563
|
+
font-size: 12px;
|
|
564
|
+
font-style: italic;
|
|
565
|
+
opacity: 0.7;
|
|
566
|
+
}
|
|
567
|
+
</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { PlotOptions } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The core Plot component handles the main layout of the plot but it does not
|
|
4
|
+
* include smart automatic scales and marks. It is to be used in cases where
|
|
5
|
+
* you want to reduce the footprint of the plot to the bare minimum.
|
|
6
|
+
*
|
|
7
|
+
* Keep in mind that you will have to create your own scales if you're using
|
|
8
|
+
* this component.
|
|
9
|
+
*/
|
|
10
|
+
declare const Plot: import("svelte").Component<Partial<PlotOptions>, {
|
|
11
|
+
getWidth: () => number;
|
|
12
|
+
}, "">;
|
|
13
|
+
type Plot = ReturnType<typeof Plot>;
|
|
14
|
+
export default Plot;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2020-2023 Observable, Inc.
|
|
4
|
+
*
|
|
5
|
+
* Permission to use, copy, modify, and/or distribute this software for any purpose
|
|
6
|
+
* with or without fee is hereby granted, provided that the above copyright notice
|
|
7
|
+
* and this permission notice appear in all copies.
|
|
8
|
+
*/
|
|
9
|
+
export declare function arrowPath(x1: number, y1: number, x2: number, y2: number, insetStart: number, insetEnd: number, headAngle: number, headLength: number, bend: number, strokeWidth: number, sweep: SweepFunc): string | null;
|
|
10
|
+
export declare function constant<T>(x: T): () => T;
|
|
11
|
+
export type SweepFunc = (x1: number, y1: number, x2: number, y2: number) => number;
|
|
12
|
+
export declare function keyword(input: string, name: string, allowed: string[]): string;
|
|
13
|
+
export type SweepOption = 1 | 0 | -1 | '+x' | '-x' | '+y' | '-y' | SweepFunc;
|
|
14
|
+
export declare function maybeSweep(sweep?: SweepOption): SweepFunc | undefined;
|