svelteplot 0.8.1 → 0.9.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/README.md +1 -1
- package/dist/Mark.svelte +1 -1
- package/dist/Mark.svelte.d.ts +1 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +2 -0
- package/dist/core/Plot.svelte +4 -4
- package/dist/helpers/projection.js +7 -2
- package/dist/hooks/usePlot.svelte.d.ts +51 -0
- package/dist/hooks/usePlot.svelte.js +90 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/marks/Area.svelte +3 -5
- package/dist/marks/Area.svelte.d.ts +1 -0
- package/dist/marks/AreaX.svelte.d.ts +1 -0
- package/dist/marks/Arrow.svelte +3 -5
- package/dist/marks/Arrow.svelte.d.ts +1 -0
- package/dist/marks/AxisX.svelte +2 -3
- package/dist/marks/AxisX.svelte.d.ts +1 -0
- package/dist/marks/AxisY.svelte +3 -4
- package/dist/marks/AxisY.svelte.d.ts +1 -0
- package/dist/marks/BarX.svelte +2 -4
- package/dist/marks/BarX.svelte.d.ts +1 -0
- package/dist/marks/BarY.svelte +2 -4
- package/dist/marks/BarY.svelte.d.ts +1 -0
- package/dist/marks/BollingerX.svelte.d.ts +1 -0
- package/dist/marks/BollingerY.svelte.d.ts +1 -0
- package/dist/marks/BoxX.svelte +4 -138
- package/dist/marks/BoxY.svelte +20 -137
- package/dist/marks/BoxY.svelte.d.ts +8 -3
- package/dist/marks/Brush.svelte +3 -3
- package/dist/marks/Brush.svelte.d.ts +1 -0
- package/dist/marks/Cell.svelte +2 -4
- package/dist/marks/Cell.svelte.d.ts +1 -0
- package/dist/marks/ColorLegend.svelte +2 -4
- package/dist/marks/CustomMark.svelte.d.ts +1 -0
- package/dist/marks/CustomMarkHTML.svelte +5 -10
- package/dist/marks/DifferenceY.svelte +3 -5
- package/dist/marks/DifferenceY.svelte.d.ts +1 -0
- package/dist/marks/Dot.svelte +4 -5
- package/dist/marks/Dot.svelte.d.ts +1 -0
- package/dist/marks/DotX.svelte.d.ts +1 -0
- package/dist/marks/DotY.svelte.d.ts +1 -0
- package/dist/marks/Frame.svelte +3 -9
- package/dist/marks/Frame.svelte.d.ts +1 -0
- package/dist/marks/Geo.svelte +5 -5
- package/dist/marks/Geo.svelte.d.ts +2 -0
- package/dist/marks/GridX.svelte +3 -10
- package/dist/marks/GridX.svelte.d.ts +1 -0
- package/dist/marks/GridY.svelte +3 -4
- package/dist/marks/GridY.svelte.d.ts +1 -0
- package/dist/marks/HTMLTooltip.svelte +5 -5
- package/dist/marks/Image.svelte.d.ts +1 -0
- package/dist/marks/Line.svelte +7 -6
- package/dist/marks/Line.svelte.d.ts +1 -0
- package/dist/marks/LineX.svelte.d.ts +1 -0
- package/dist/marks/LineY.svelte.d.ts +1 -0
- package/dist/marks/Link.svelte +2 -4
- package/dist/marks/Link.svelte.d.ts +1 -0
- package/dist/marks/Pointer.svelte +4 -4
- package/dist/marks/Rect.svelte +2 -4
- package/dist/marks/Rect.svelte.d.ts +1 -0
- package/dist/marks/RectX.svelte +4 -4
- package/dist/marks/RectY.svelte +4 -4
- package/dist/marks/RuleX.svelte +2 -4
- package/dist/marks/RuleX.svelte.d.ts +1 -0
- package/dist/marks/RuleY.svelte +2 -4
- package/dist/marks/RuleY.svelte.d.ts +1 -0
- package/dist/marks/Spike.svelte.d.ts +1 -0
- package/dist/marks/SymbolLegend.svelte +2 -4
- package/dist/marks/SymbolLegend.svelte.d.ts +17 -2
- package/dist/marks/Text.svelte.d.ts +1 -0
- package/dist/marks/TickX.svelte +2 -3
- package/dist/marks/TickX.svelte.d.ts +1 -0
- package/dist/marks/TickY.svelte +2 -3
- package/dist/marks/TickY.svelte.d.ts +1 -0
- package/dist/marks/Trail.svelte +161 -0
- package/dist/marks/Trail.svelte.d.ts +107 -0
- package/dist/marks/Vector.svelte +3 -4
- package/dist/marks/Vector.svelte.d.ts +1 -0
- package/dist/marks/WaffleX.svelte +2 -3
- package/dist/marks/WaffleX.svelte.d.ts +1 -0
- package/dist/marks/WaffleY.svelte +2 -4
- package/dist/marks/WaffleY.svelte.d.ts +1 -0
- package/dist/marks/helpers/AreaCanvas.svelte +2 -4
- package/dist/marks/helpers/Box.svelte +271 -0
- package/dist/marks/helpers/Box.svelte.d.ts +118 -0
- package/dist/marks/helpers/CanvasLayer.svelte +2 -4
- package/dist/marks/helpers/DotCanvas.svelte +3 -5
- package/dist/marks/helpers/GeoCanvas.svelte +2 -4
- package/dist/marks/helpers/LineCanvas.svelte +2 -4
- package/dist/marks/helpers/LinearGradientX.svelte +3 -4
- package/dist/marks/helpers/LinearGradientY.svelte +3 -4
- package/dist/marks/helpers/MarkerPath.svelte +4 -5
- package/dist/marks/helpers/MarkerPath.svelte.d.ts +1 -0
- package/dist/marks/helpers/MultilineText.svelte +4 -4
- package/dist/marks/helpers/RectPath.svelte +5 -6
- package/dist/marks/helpers/Regression.svelte +4 -8
- package/dist/marks/helpers/TrailCanvas.svelte +138 -0
- package/dist/marks/helpers/TrailCanvas.svelte.d.ts +40 -0
- package/dist/marks/helpers/events.d.ts +2 -2
- package/dist/marks/helpers/events.js +4 -4
- package/dist/marks/helpers/trail.d.ts +23 -0
- package/dist/marks/helpers/trail.js +372 -0
- package/dist/marks/index.d.ts +1 -0
- package/dist/marks/index.js +1 -0
- package/dist/transforms/bollinger.d.ts +1 -0
- package/dist/transforms/interval.d.ts +2 -0
- package/dist/transforms/select.d.ts +7 -0
- package/dist/transforms/sort.d.ts +4 -0
- package/dist/transforms/window.d.ts +2 -0
- package/dist/types/mark.d.ts +2 -1
- package/dist/types/plot.d.ts +6 -1
- package/dist/ui/Spiral.svelte +4 -0
- package/package.json +24 -23
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
<script lang="ts" generics="Datum extends DataRecord">
|
|
2
|
+
interface TrailMarkProps extends Omit<
|
|
3
|
+
BaseMarkProps<Datum>,
|
|
4
|
+
'stroke' | 'strokeWidth' | 'strokeDasharray'
|
|
5
|
+
> {
|
|
6
|
+
data?: Datum[];
|
|
7
|
+
x?: ChannelAccessor<Datum>;
|
|
8
|
+
y?: ChannelAccessor<Datum>;
|
|
9
|
+
z?: ChannelAccessor<Datum>;
|
|
10
|
+
r?: ChannelAccessor<Datum>;
|
|
11
|
+
curve?: CurveName | CurveFactory;
|
|
12
|
+
tension?: number;
|
|
13
|
+
sort?: ConstantAccessor<RawValue, Datum> | { channel: 'stroke' | 'fill' };
|
|
14
|
+
defined?: ConstantAccessor<boolean, Datum>;
|
|
15
|
+
canvas?: boolean;
|
|
16
|
+
cap?: 'butt' | 'round';
|
|
17
|
+
/**
|
|
18
|
+
* Samples per segment for curve interpolation
|
|
19
|
+
*/
|
|
20
|
+
resolution?: number | 'auto';
|
|
21
|
+
}
|
|
22
|
+
import type {
|
|
23
|
+
DataRecord,
|
|
24
|
+
ChannelAccessor,
|
|
25
|
+
BaseMarkProps,
|
|
26
|
+
ConstantAccessor,
|
|
27
|
+
RawValue,
|
|
28
|
+
ScaledDataRecord,
|
|
29
|
+
CurveName
|
|
30
|
+
} from '../types';
|
|
31
|
+
import Mark from '../Mark.svelte';
|
|
32
|
+
import { path as d3Path } from 'd3-path';
|
|
33
|
+
import { resolveProp, resolveStyles } from '../helpers/resolve.js';
|
|
34
|
+
import { getPlotDefaults } from '../hooks/plotDefaults';
|
|
35
|
+
import { sort } from '../transforms';
|
|
36
|
+
import trailPath, { type TrailSample } from './helpers/trail.js';
|
|
37
|
+
import TrailCanvas from './helpers/TrailCanvas.svelte';
|
|
38
|
+
import { addEventHandlers } from './helpers/events';
|
|
39
|
+
import type { CurveFactory } from 'd3-shape';
|
|
40
|
+
import { usePlot } from '../hooks/usePlot.svelte.js';
|
|
41
|
+
|
|
42
|
+
let markProps: TrailMarkProps = $props();
|
|
43
|
+
|
|
44
|
+
const DEFAULTS: TrailMarkProps = {
|
|
45
|
+
curve: 'linear',
|
|
46
|
+
r: 3,
|
|
47
|
+
canvas: false,
|
|
48
|
+
resolution: 'auto',
|
|
49
|
+
cap: 'round',
|
|
50
|
+
tension: 0,
|
|
51
|
+
...getPlotDefaults().trail
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const {
|
|
55
|
+
data = [{} as Datum],
|
|
56
|
+
curve,
|
|
57
|
+
resolution,
|
|
58
|
+
tension,
|
|
59
|
+
canvas,
|
|
60
|
+
cap,
|
|
61
|
+
class: className,
|
|
62
|
+
...options
|
|
63
|
+
}: TrailMarkProps = $derived({
|
|
64
|
+
...DEFAULTS,
|
|
65
|
+
...markProps
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const args = $derived(sort({ data, ...options })) as TrailMarkProps;
|
|
69
|
+
|
|
70
|
+
const plot = usePlot();
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Groups the data by the specified key
|
|
74
|
+
*/
|
|
75
|
+
function groupIndex(data: ScaledDataRecord[], groupByKey: ChannelAccessor<Datum> | null) {
|
|
76
|
+
if (!groupByKey) return [data];
|
|
77
|
+
let group: ScaledDataRecord[] = [];
|
|
78
|
+
const groups = [group];
|
|
79
|
+
let lastGroupValue;
|
|
80
|
+
for (const d of data) {
|
|
81
|
+
const groupValue = resolveProp(groupByKey, d.datum);
|
|
82
|
+
if (groupValue === lastGroupValue || group.length === 0) {
|
|
83
|
+
group.push(d);
|
|
84
|
+
lastGroupValue = groupValue;
|
|
85
|
+
} else {
|
|
86
|
+
// new group
|
|
87
|
+
group = [d];
|
|
88
|
+
groups.push(group);
|
|
89
|
+
lastGroupValue = groupValue;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return groups.filter((d) => d.length > 0);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const groupByKey = $derived(args.z || args.fill) as ChannelAccessor<Datum> | null;
|
|
96
|
+
</script>
|
|
97
|
+
|
|
98
|
+
<Mark
|
|
99
|
+
type="trail"
|
|
100
|
+
channels={['x', 'y', 'opacity', 'fill', 'fillOpacity', 'r']}
|
|
101
|
+
required={['x', 'y']}
|
|
102
|
+
{...args}>
|
|
103
|
+
{#snippet children({ mark, usedScales, scaledData })}
|
|
104
|
+
{#if scaledData.length > 0}
|
|
105
|
+
{@const groupedTrailData = groupIndex(scaledData, groupByKey)}
|
|
106
|
+
{#if canvas}
|
|
107
|
+
<!-- todo -->
|
|
108
|
+
<TrailCanvas
|
|
109
|
+
{curve}
|
|
110
|
+
{cap}
|
|
111
|
+
{tension}
|
|
112
|
+
{resolution}
|
|
113
|
+
{usedScales}
|
|
114
|
+
data={groupedTrailData}
|
|
115
|
+
options={args} />
|
|
116
|
+
{:else}
|
|
117
|
+
<g class={['trail', className]}>
|
|
118
|
+
{#each groupedTrailData as trailData, i (i)}
|
|
119
|
+
{@const samples = trailData.map((d) => ({
|
|
120
|
+
x: Number(d.x),
|
|
121
|
+
y: Number(d.y),
|
|
122
|
+
r: Number(d.r ?? 0)
|
|
123
|
+
})) satisfies TrailSample[]}
|
|
124
|
+
{@const defined = trailData.map(
|
|
125
|
+
(d) =>
|
|
126
|
+
d.valid &&
|
|
127
|
+
d.r >= 0 &&
|
|
128
|
+
(resolveProp(options.defined, d.datum, true) ?? true)
|
|
129
|
+
)}
|
|
130
|
+
{@const pathString = trailPath(samples, defined, d3Path(), {
|
|
131
|
+
curve,
|
|
132
|
+
cap,
|
|
133
|
+
tension,
|
|
134
|
+
...(typeof resolution === 'number'
|
|
135
|
+
? { samplesPerSegment: resolution }
|
|
136
|
+
: {})
|
|
137
|
+
})}
|
|
138
|
+
{@const [style, styleClass] = resolveStyles(
|
|
139
|
+
plot,
|
|
140
|
+
trailData[0],
|
|
141
|
+
{
|
|
142
|
+
...args
|
|
143
|
+
},
|
|
144
|
+
'fill',
|
|
145
|
+
usedScales
|
|
146
|
+
)}
|
|
147
|
+
<path
|
|
148
|
+
d={pathString}
|
|
149
|
+
{style}
|
|
150
|
+
class={styleClass}
|
|
151
|
+
{@attach addEventHandlers({
|
|
152
|
+
plot,
|
|
153
|
+
options: mark.options,
|
|
154
|
+
datum: trailData[0].datum
|
|
155
|
+
})} />
|
|
156
|
+
{/each}
|
|
157
|
+
</g>
|
|
158
|
+
{/if}
|
|
159
|
+
{/if}
|
|
160
|
+
{/snippet}
|
|
161
|
+
</Mark>
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { DataRecord, ChannelAccessor, ConstantAccessor, RawValue, CurveName } from '../types';
|
|
2
|
+
import type { CurveFactory } from 'd3-shape';
|
|
3
|
+
declare function $$render<Datum extends DataRecord>(): {
|
|
4
|
+
props: Omit<Partial<{
|
|
5
|
+
filter: ConstantAccessor<boolean, Datum>;
|
|
6
|
+
facet: "auto" | "include" | "exclude";
|
|
7
|
+
fx: ChannelAccessor<Datum>;
|
|
8
|
+
fy: ChannelAccessor<Datum>;
|
|
9
|
+
dx: ConstantAccessor<number, Datum>;
|
|
10
|
+
dy: ConstantAccessor<number, Datum>;
|
|
11
|
+
dodgeX: import("../transforms/dodge").DodgeXOptions;
|
|
12
|
+
dodgeY: import("../transforms/dodge").DodgeYOptions;
|
|
13
|
+
fill: ChannelAccessor<Datum>;
|
|
14
|
+
fillOpacity: ConstantAccessor<number, Datum>;
|
|
15
|
+
sort: ((a: RawValue, b: RawValue) => number) | {
|
|
16
|
+
channel: string;
|
|
17
|
+
order?: "ascending" | "descending";
|
|
18
|
+
} | ConstantAccessor<RawValue, Datum>;
|
|
19
|
+
stroke: ChannelAccessor<Datum>;
|
|
20
|
+
strokeWidth: ConstantAccessor<number, Datum>;
|
|
21
|
+
strokeOpacity: ConstantAccessor<number, Datum>;
|
|
22
|
+
strokeLinejoin: ConstantAccessor<import("csstype").Property.StrokeLinejoin, Datum>;
|
|
23
|
+
strokeLinecap: ConstantAccessor<import("csstype").Property.StrokeLinecap, Datum>;
|
|
24
|
+
strokeMiterlimit: ConstantAccessor<number, Datum>;
|
|
25
|
+
opacity: ChannelAccessor<Datum>;
|
|
26
|
+
strokeDasharray: ConstantAccessor<string, Datum>;
|
|
27
|
+
strokeDashoffset: ConstantAccessor<number, Datum>;
|
|
28
|
+
mixBlendMode: ConstantAccessor<import("csstype").Property.MixBlendMode, Datum>;
|
|
29
|
+
clipPath: string;
|
|
30
|
+
mask: string;
|
|
31
|
+
imageFilter: ConstantAccessor<string, Datum>;
|
|
32
|
+
shapeRendering: ConstantAccessor<import("csstype").Property.ShapeRendering, Datum>;
|
|
33
|
+
paintOrder: ConstantAccessor<string, Datum>;
|
|
34
|
+
onclick: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
35
|
+
ondblclick: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
36
|
+
onmouseup: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
37
|
+
onmousedown: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
38
|
+
onmouseenter: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
39
|
+
onmousemove: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
40
|
+
onmouseleave: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
41
|
+
onmouseout: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
42
|
+
onmouseover: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
43
|
+
onpointercancel: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
44
|
+
onpointerdown: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
45
|
+
onpointerup: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
46
|
+
onpointerenter: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
47
|
+
onpointerleave: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
48
|
+
onpointermove: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
49
|
+
onpointerover: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
50
|
+
onpointerout: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
51
|
+
ondrag: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
52
|
+
ondrop: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
53
|
+
ondragstart: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
54
|
+
ondragenter: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
55
|
+
ondragleave: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
56
|
+
ondragover: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
57
|
+
ondragend: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
58
|
+
ontouchstart: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
59
|
+
ontouchmove: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
60
|
+
ontouchend: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
61
|
+
ontouchcancel: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
62
|
+
oncontextmenu: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
63
|
+
onwheel: import("svelte/elements").MouseEventHandler<SVGPathElement>;
|
|
64
|
+
class: string;
|
|
65
|
+
style: string;
|
|
66
|
+
cursor: ConstantAccessor<import("csstype").Property.Cursor, Datum>;
|
|
67
|
+
}>, "stroke" | "strokeWidth" | "strokeDasharray"> & {
|
|
68
|
+
data?: Datum[];
|
|
69
|
+
x?: ChannelAccessor<Datum>;
|
|
70
|
+
y?: ChannelAccessor<Datum>;
|
|
71
|
+
z?: ChannelAccessor<Datum>;
|
|
72
|
+
r?: ChannelAccessor<Datum>;
|
|
73
|
+
curve?: CurveName | CurveFactory;
|
|
74
|
+
tension?: number;
|
|
75
|
+
sort?: ConstantAccessor<RawValue, Datum> | {
|
|
76
|
+
channel: "stroke" | "fill";
|
|
77
|
+
};
|
|
78
|
+
defined?: ConstantAccessor<boolean, Datum>;
|
|
79
|
+
canvas?: boolean;
|
|
80
|
+
cap?: "butt" | "round";
|
|
81
|
+
/**
|
|
82
|
+
* Samples per segment for curve interpolation
|
|
83
|
+
*/
|
|
84
|
+
resolution?: number | "auto";
|
|
85
|
+
};
|
|
86
|
+
exports: {};
|
|
87
|
+
bindings: "";
|
|
88
|
+
slots: {};
|
|
89
|
+
events: {};
|
|
90
|
+
};
|
|
91
|
+
declare class __sveltets_Render<Datum extends DataRecord> {
|
|
92
|
+
props(): ReturnType<typeof $$render<Datum>>['props'];
|
|
93
|
+
events(): ReturnType<typeof $$render<Datum>>['events'];
|
|
94
|
+
slots(): ReturnType<typeof $$render<Datum>>['slots'];
|
|
95
|
+
bindings(): "";
|
|
96
|
+
exports(): {};
|
|
97
|
+
}
|
|
98
|
+
interface $$IsomorphicComponent {
|
|
99
|
+
new <Datum extends DataRecord>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<Datum>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<Datum>['props']>, ReturnType<__sveltets_Render<Datum>['events']>, ReturnType<__sveltets_Render<Datum>['slots']>> & {
|
|
100
|
+
$$bindings?: ReturnType<__sveltets_Render<Datum>['bindings']>;
|
|
101
|
+
} & ReturnType<__sveltets_Render<Datum>['exports']>;
|
|
102
|
+
<Datum extends DataRecord>(internal: unknown, props: ReturnType<__sveltets_Render<Datum>['props']> & {}): ReturnType<__sveltets_Render<Datum>['exports']>;
|
|
103
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
104
|
+
}
|
|
105
|
+
declare const Trail: $$IsomorphicComponent;
|
|
106
|
+
type Trail<Datum extends DataRecord> = InstanceType<typeof Trail<Datum>>;
|
|
107
|
+
export default Trail;
|
package/dist/marks/Vector.svelte
CHANGED
|
@@ -29,7 +29,6 @@
|
|
|
29
29
|
canvas?: boolean;
|
|
30
30
|
}
|
|
31
31
|
import type {
|
|
32
|
-
PlotContext,
|
|
33
32
|
DataRecord,
|
|
34
33
|
BaseMarkProps,
|
|
35
34
|
ChannelAccessor,
|
|
@@ -47,6 +46,7 @@
|
|
|
47
46
|
import { addEventHandlers } from './helpers/events.js';
|
|
48
47
|
import { indexData } from '../transforms/recordize.js';
|
|
49
48
|
import { getPlotDefaults } from '../hooks/plotDefaults.js';
|
|
49
|
+
import { usePlot } from '../hooks/usePlot.svelte.js';
|
|
50
50
|
|
|
51
51
|
const defaultRadius = 3.5;
|
|
52
52
|
|
|
@@ -72,8 +72,7 @@
|
|
|
72
72
|
...markProps
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
-
const
|
|
76
|
-
const plot = $derived(getPlotState());
|
|
75
|
+
const plot = usePlot();
|
|
77
76
|
|
|
78
77
|
const { getTestFacet } = getContext<FacetContext>('svelteplot/facet');
|
|
79
78
|
const testFacet = $derived(getTestFacet());
|
|
@@ -203,7 +202,7 @@
|
|
|
203
202
|
: `translate(0, ${d.length / 2})`}"
|
|
204
203
|
{style}
|
|
205
204
|
{@attach addEventHandlers({
|
|
206
|
-
|
|
205
|
+
plot,
|
|
207
206
|
options: args,
|
|
208
207
|
datum: d?.datum
|
|
209
208
|
})}
|
|
@@ -31,6 +31,7 @@ declare function $$render<Datum extends DataRecord>(): {
|
|
|
31
31
|
strokeDashoffset: import("../types/index.js").ConstantAccessor<number, Datum>;
|
|
32
32
|
mixBlendMode: import("../types/index.js").ConstantAccessor<import("csstype").Property.MixBlendMode, Datum>;
|
|
33
33
|
clipPath: string;
|
|
34
|
+
mask: string;
|
|
34
35
|
imageFilter: import("../types/index.js").ConstantAccessor<string, Datum>;
|
|
35
36
|
shapeRendering: import("../types/index.js").ConstantAccessor<import("csstype").Property.ShapeRendering, Datum>;
|
|
36
37
|
paintOrder: import("../types/index.js").ConstantAccessor<string, Datum>;
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
import { intervalX, recordizeX, sort, stackX } from '../transforms';
|
|
16
16
|
import type { StackOptions } from '../transforms/stack';
|
|
17
17
|
import Mark from '../Mark.svelte';
|
|
18
|
-
import { getContext } from 'svelte';
|
|
19
18
|
import { resolveProp, resolveStyles } from '../helpers/resolve';
|
|
20
19
|
import { roundedRect } from '../helpers/roundedRect';
|
|
20
|
+
import { usePlot } from '../hooks/usePlot.svelte.js';
|
|
21
21
|
|
|
22
22
|
interface WaffleXMarkProps
|
|
23
23
|
extends BaseMarkProps<Datum>, LinkableMarkProps<Datum>, WaffleOptions<Datum> {
|
|
@@ -58,8 +58,7 @@
|
|
|
58
58
|
...options
|
|
59
59
|
}: WaffleXMarkProps = $derived({ ...DEFAULTS, ...markProps });
|
|
60
60
|
|
|
61
|
-
const
|
|
62
|
-
const plot = $derived(getPlotState());
|
|
61
|
+
const plot = usePlot();
|
|
63
62
|
|
|
64
63
|
const args = $derived(
|
|
65
64
|
stackX(
|
|
@@ -28,6 +28,7 @@ declare function $$render<Datum extends DataRecord>(): {
|
|
|
28
28
|
strokeDashoffset: import("../types").ConstantAccessor<number, Datum>;
|
|
29
29
|
mixBlendMode: import("../types").ConstantAccessor<import("csstype").Property.MixBlendMode, Datum>;
|
|
30
30
|
clipPath: string;
|
|
31
|
+
mask: string;
|
|
31
32
|
imageFilter: import("../types").ConstantAccessor<string, Datum>;
|
|
32
33
|
shapeRendering: import("../types").ConstantAccessor<import("csstype").Property.ShapeRendering, Datum>;
|
|
33
34
|
paintOrder: import("../types").ConstantAccessor<string, Datum>;
|
|
@@ -8,17 +8,16 @@
|
|
|
8
8
|
ChannelAccessor,
|
|
9
9
|
BaseMarkProps,
|
|
10
10
|
LinkableMarkProps,
|
|
11
|
-
PlotContext,
|
|
12
11
|
BorderRadius
|
|
13
12
|
} from '../types';
|
|
14
13
|
import { wafflePolygon, type WaffleOptions } from './helpers/waffle';
|
|
15
14
|
import { getPlotDefaults } from '../hooks/plotDefaults';
|
|
16
|
-
import { getContext } from 'svelte';
|
|
17
15
|
import { intervalY, recordizeY, sort, stackY } from '../transforms';
|
|
18
16
|
import Mark from '../Mark.svelte';
|
|
19
17
|
import { resolveProp, resolveStyles } from '../helpers/resolve';
|
|
20
18
|
import { roundedRect } from '../helpers/roundedRect';
|
|
21
19
|
import GroupMultiple from './helpers/GroupMultiple.svelte';
|
|
20
|
+
import { usePlot } from '../hooks/usePlot.svelte.js';
|
|
22
21
|
|
|
23
22
|
interface WaffleYMarkProps
|
|
24
23
|
extends BaseMarkProps<Datum>, LinkableMarkProps<Datum>, WaffleOptions<Datum> {
|
|
@@ -56,8 +55,7 @@
|
|
|
56
55
|
...options
|
|
57
56
|
}: WaffleYMarkProps = $derived({ ...DEFAULTS, ...markProps });
|
|
58
57
|
|
|
59
|
-
const
|
|
60
|
-
const plot = $derived(getPlotState());
|
|
58
|
+
const plot = usePlot();
|
|
61
59
|
|
|
62
60
|
const args = $derived(
|
|
63
61
|
stackY(
|
|
@@ -27,6 +27,7 @@ declare function $$render<Datum extends DataRecord>(): {
|
|
|
27
27
|
strokeDashoffset: import("../types").ConstantAccessor<number, Datum>;
|
|
28
28
|
mixBlendMode: import("../types").ConstantAccessor<import("csstype").Property.MixBlendMode, Datum>;
|
|
29
29
|
clipPath: string;
|
|
30
|
+
mask: string;
|
|
30
31
|
imageFilter: import("../types").ConstantAccessor<string, Datum>;
|
|
31
32
|
shapeRendering: import("../types").ConstantAccessor<import("csstype").Property.ShapeRendering, Datum>;
|
|
32
33
|
paintOrder: import("../types").ConstantAccessor<string, Datum>;
|
|
@@ -2,17 +2,16 @@
|
|
|
2
2
|
import type {
|
|
3
3
|
Mark,
|
|
4
4
|
BaseMarkProps,
|
|
5
|
-
PlotContext,
|
|
6
5
|
ScaledDataRecord,
|
|
7
6
|
UsedScales
|
|
8
7
|
} from '../../types/index.js';
|
|
9
8
|
import { resolveProp, resolveScaledStyleProps } from '../../helpers/resolve.js';
|
|
10
|
-
import { getContext } from 'svelte';
|
|
11
9
|
import { type Area } from 'd3-shape';
|
|
12
10
|
import CanvasLayer from './CanvasLayer.svelte';
|
|
13
11
|
import type { Attachment } from 'svelte/attachments';
|
|
14
12
|
import { devicePixelRatio } from 'svelte/reactivity/window';
|
|
15
13
|
import { resolveColor } from './canvas.js';
|
|
14
|
+
import { usePlot } from '../../hooks/usePlot.svelte.js';
|
|
16
15
|
|
|
17
16
|
let {
|
|
18
17
|
mark,
|
|
@@ -26,8 +25,7 @@
|
|
|
26
25
|
areaPath: Area<ScaledDataRecord>;
|
|
27
26
|
} = $props();
|
|
28
27
|
|
|
29
|
-
const
|
|
30
|
-
const plot = $derived(getPlotState());
|
|
28
|
+
const plot = usePlot();
|
|
31
29
|
|
|
32
30
|
function maybeOpacity(value: unknown) {
|
|
33
31
|
return value == null ? 1 : +value;
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
<!-- @component
|
|
2
|
+
Internal shared box plot implementation for BoxX and BoxY
|
|
3
|
+
-->
|
|
4
|
+
<script lang="ts" generics="Datum extends DataRecord">
|
|
5
|
+
type Orientation = 'x' | 'y';
|
|
6
|
+
|
|
7
|
+
interface BoxMarkProps extends Pick<
|
|
8
|
+
BaseMarkProps<Datum>,
|
|
9
|
+
'class' | 'fill' | 'stroke' | 'fx' | 'fy'
|
|
10
|
+
> {
|
|
11
|
+
data: Datum[];
|
|
12
|
+
x: ChannelAccessor;
|
|
13
|
+
y: ChannelAccessor;
|
|
14
|
+
/**
|
|
15
|
+
* Custom sort order for grouped box plot data
|
|
16
|
+
*/
|
|
17
|
+
sort?: 'min' | 'max' | 'median' | 'p25' | 'p75' | ((d: Datum) => RawValue);
|
|
18
|
+
/**
|
|
19
|
+
* Options for the rule marks that represent the min/max range
|
|
20
|
+
*/
|
|
21
|
+
rule: Record<string, ChannelAccessor<Datum>>;
|
|
22
|
+
/**
|
|
23
|
+
* Options for the bar marks that represent the IQR range
|
|
24
|
+
*/
|
|
25
|
+
bar: Record<string, ChannelAccessor<Datum>>;
|
|
26
|
+
/**
|
|
27
|
+
* Options for the tick marks that represent the median
|
|
28
|
+
*/
|
|
29
|
+
tickMedian: Record<string, ChannelAccessor<Datum>> | boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Options for the tick marks that represent the min/max range
|
|
32
|
+
*/
|
|
33
|
+
tickMinMax: Record<string, ChannelAccessor<Datum>> | boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Options for the dot marks that represent the outliers
|
|
36
|
+
*/
|
|
37
|
+
dot: Record<string, ChannelAccessor<Datum>>;
|
|
38
|
+
orientation: Orientation;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
import GroupMultiple from './GroupMultiple.svelte';
|
|
42
|
+
import { groupX, groupY, BarY, TickY, RuleX, BarX, TickX, RuleY, Dot } from '../../index.js';
|
|
43
|
+
import { resolveChannel } from '../../helpers/resolve.js';
|
|
44
|
+
import type { BaseMarkProps, ChannelAccessor, DataRecord, RawValue } from '../../types';
|
|
45
|
+
import { IS_SORTED } from '../../transforms/sort';
|
|
46
|
+
|
|
47
|
+
let markProps: BoxMarkProps = $props();
|
|
48
|
+
|
|
49
|
+
const {
|
|
50
|
+
data = [{}],
|
|
51
|
+
bar,
|
|
52
|
+
rule,
|
|
53
|
+
tickMedian,
|
|
54
|
+
tickMinMax,
|
|
55
|
+
dot,
|
|
56
|
+
x,
|
|
57
|
+
y,
|
|
58
|
+
sort,
|
|
59
|
+
fx,
|
|
60
|
+
fy,
|
|
61
|
+
fill,
|
|
62
|
+
stroke,
|
|
63
|
+
orientation,
|
|
64
|
+
class: className = ''
|
|
65
|
+
}: BoxMarkProps = $derived(markProps);
|
|
66
|
+
|
|
67
|
+
const groupFn = $derived(orientation === 'y' ? groupX : groupY);
|
|
68
|
+
const BarMark = $derived(orientation === 'y' ? BarY : BarX);
|
|
69
|
+
const RuleMark = $derived(orientation === 'y' ? RuleX : RuleY);
|
|
70
|
+
const TickMark = $derived(orientation === 'y' ? TickY : TickX);
|
|
71
|
+
|
|
72
|
+
// the channels as if this would be a BoxX
|
|
73
|
+
const xChannel = $derived(orientation === 'y' ? y : x);
|
|
74
|
+
const yChannel = $derived(orientation === 'y' ? x : y);
|
|
75
|
+
const xProp = $derived(orientation === 'x' ? 'x' : 'y');
|
|
76
|
+
const x1Prop = $derived(`${xProp}1`);
|
|
77
|
+
const x2Prop = $derived(`${xProp}2`);
|
|
78
|
+
const yProp = $derived(orientation === 'x' ? 'y' : 'x');
|
|
79
|
+
|
|
80
|
+
const { data: grouped, ...groupChannels } = $derived(
|
|
81
|
+
groupFn(
|
|
82
|
+
{
|
|
83
|
+
data: data.filter((d) => resolveChannel(xProp, d, { x, y }) != null),
|
|
84
|
+
x,
|
|
85
|
+
y,
|
|
86
|
+
[x1Prop]: xChannel,
|
|
87
|
+
[x2Prop]: xChannel,
|
|
88
|
+
fx,
|
|
89
|
+
fy
|
|
90
|
+
},
|
|
91
|
+
{ [xProp]: 'median', [x1Prop]: 'p25', [x2Prop]: 'p75', fill: (rows) => rows }
|
|
92
|
+
)
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const X = Symbol('x'),
|
|
96
|
+
Y = Symbol('y'),
|
|
97
|
+
FX = Symbol('fx'),
|
|
98
|
+
FY = Symbol('fy'),
|
|
99
|
+
P25 = Symbol('p25'),
|
|
100
|
+
P75 = Symbol('p75'),
|
|
101
|
+
MEDIAN = Symbol('median'),
|
|
102
|
+
MIN = Symbol('min'),
|
|
103
|
+
MAX = Symbol('max'),
|
|
104
|
+
OUTLIERS = Symbol('outliers'),
|
|
105
|
+
SORT_REF = Symbol('sortRef');
|
|
106
|
+
|
|
107
|
+
const facets = $derived({
|
|
108
|
+
...(fx != null && { fx: FX }),
|
|
109
|
+
...(fy != null && { fy: FY })
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const sortProps = { [IS_SORTED]: true };
|
|
113
|
+
|
|
114
|
+
const compareValues = (a: RawValue, b: RawValue) =>
|
|
115
|
+
(typeof a === 'string' && typeof b === 'string'
|
|
116
|
+
? a.localeCompare(b)
|
|
117
|
+
: a > b
|
|
118
|
+
? 1
|
|
119
|
+
: a < b
|
|
120
|
+
? -1
|
|
121
|
+
: 0) || 0;
|
|
122
|
+
|
|
123
|
+
const boxData = $derived.by(() => {
|
|
124
|
+
const boxes = grouped
|
|
125
|
+
.map((row) => {
|
|
126
|
+
const medianKey = groupChannels[xProp];
|
|
127
|
+
const p25Key = groupChannels[x1Prop];
|
|
128
|
+
const p75Key = groupChannels[x2Prop];
|
|
129
|
+
const groupKey = groupChannels[yProp];
|
|
130
|
+
|
|
131
|
+
const iqr = row[p75Key] - row[p25Key];
|
|
132
|
+
const whisker = iqr * 1.5;
|
|
133
|
+
const lower = row[p25Key] - whisker;
|
|
134
|
+
const upper = row[p75Key] + whisker;
|
|
135
|
+
const data = row[groupChannels.fill].map((d) => ({
|
|
136
|
+
...d,
|
|
137
|
+
[orientation === 'y' ? Y : X]: resolveChannel(xProp, d, {
|
|
138
|
+
x,
|
|
139
|
+
y
|
|
140
|
+
})
|
|
141
|
+
}));
|
|
142
|
+
const valueSym = orientation === 'y' ? Y : X;
|
|
143
|
+
const groupSym = orientation === 'y' ? X : Y;
|
|
144
|
+
const outliers = data.filter((d) => d[valueSym] < lower || d[valueSym] > upper);
|
|
145
|
+
const inside = data
|
|
146
|
+
.filter((d) => d[valueSym] >= lower && d[valueSym] <= upper)
|
|
147
|
+
.sort((a, b) => a[valueSym] - b[valueSym]);
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
...data[0],
|
|
151
|
+
[SORT_REF]: row[groupChannels.fill]?.[0],
|
|
152
|
+
[groupSym]: row[groupKey],
|
|
153
|
+
[P25]: row[p25Key],
|
|
154
|
+
[MEDIAN]: row[medianKey],
|
|
155
|
+
[P75]: row[p75Key],
|
|
156
|
+
[MIN]: inside.length ? inside[0][valueSym] : null,
|
|
157
|
+
[MAX]: inside.length ? inside.at(-1)[valueSym] : null,
|
|
158
|
+
[FX]: resolveChannel('fx', data[0], { fx }, null),
|
|
159
|
+
[FY]: resolveChannel('fy', data[0], { fy }, null),
|
|
160
|
+
[OUTLIERS]: outliers
|
|
161
|
+
};
|
|
162
|
+
})
|
|
163
|
+
.filter(Boolean);
|
|
164
|
+
|
|
165
|
+
const stripSortRef = ({ [SORT_REF]: _, ...rest }) => rest;
|
|
166
|
+
|
|
167
|
+
if (!sort) return boxes.map(stripSortRef);
|
|
168
|
+
|
|
169
|
+
const [sort_, direction] = maybeSort(sort);
|
|
170
|
+
|
|
171
|
+
const sortAccessor =
|
|
172
|
+
typeof sort === 'function'
|
|
173
|
+
? (d) => sort(d[SORT_REF])
|
|
174
|
+
: (d) => {
|
|
175
|
+
switch (sort_) {
|
|
176
|
+
case 'min':
|
|
177
|
+
return d[MIN];
|
|
178
|
+
case 'max':
|
|
179
|
+
return d[MAX];
|
|
180
|
+
case 'p25':
|
|
181
|
+
return d[P25];
|
|
182
|
+
case 'p75':
|
|
183
|
+
return d[P75];
|
|
184
|
+
case 'median':
|
|
185
|
+
default:
|
|
186
|
+
return d[MEDIAN];
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
return boxes
|
|
191
|
+
.toSorted(
|
|
192
|
+
(a, b) =>
|
|
193
|
+
compareValues(sortAccessor(a), sortAccessor(b)) *
|
|
194
|
+
direction *
|
|
195
|
+
(orientation === 'x' ? -1 : 1)
|
|
196
|
+
)
|
|
197
|
+
.map(stripSortRef);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
function maybeSort(
|
|
201
|
+
sort: string | ((d: Datum) => RawValue) | undefined
|
|
202
|
+
): [string | ((d: Datum) => RawValue), 1 | -1] {
|
|
203
|
+
if (typeof sort !== 'string') return [sort, 1];
|
|
204
|
+
if (sort.startsWith('-')) {
|
|
205
|
+
return [sort.slice(1), -1];
|
|
206
|
+
}
|
|
207
|
+
return [sort, 1];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const valueSymbol = $derived(orientation === 'y' ? Y : X);
|
|
211
|
+
const groupSymbol = $derived(orientation === 'y' ? X : Y);
|
|
212
|
+
const length = $derived(className ? 2 : grouped.length);
|
|
213
|
+
const baseClass = $derived(`box-${orientation} ${className || ''}`);
|
|
214
|
+
</script>
|
|
215
|
+
|
|
216
|
+
<GroupMultiple class={baseClass} {length}>
|
|
217
|
+
<RuleMark
|
|
218
|
+
data={boxData}
|
|
219
|
+
{...{ [yProp]: groupSymbol, [x1Prop]: MIN, [x2Prop]: P25 }}
|
|
220
|
+
{stroke}
|
|
221
|
+
{...rule || {}}
|
|
222
|
+
{...facets}
|
|
223
|
+
{...sortProps} />
|
|
224
|
+
<RuleMark
|
|
225
|
+
data={boxData}
|
|
226
|
+
{...{ [yProp]: groupSymbol, [x1Prop]: P75, [x2Prop]: MAX }}
|
|
227
|
+
{stroke}
|
|
228
|
+
{...rule || {}}
|
|
229
|
+
{...facets} />
|
|
230
|
+
<BarMark
|
|
231
|
+
data={boxData}
|
|
232
|
+
{...{ [yProp]: groupSymbol, [x1Prop]: P25, [x2Prop]: P75 }}
|
|
233
|
+
{fill}
|
|
234
|
+
{stroke}
|
|
235
|
+
{...facets}
|
|
236
|
+
{...bar || {}} />
|
|
237
|
+
{#if tickMedian}
|
|
238
|
+
<TickMark
|
|
239
|
+
data={boxData}
|
|
240
|
+
{...{ [yProp]: groupSymbol, [xProp]: MEDIAN }}
|
|
241
|
+
{...facets}
|
|
242
|
+
{stroke}
|
|
243
|
+
strokeWidth={2}
|
|
244
|
+
{...typeof tickMedian === 'object' ? tickMedian : {}} />
|
|
245
|
+
{/if}
|
|
246
|
+
{#if tickMinMax}
|
|
247
|
+
<TickMark
|
|
248
|
+
data={boxData}
|
|
249
|
+
{...{ [yProp]: groupSymbol, [xProp]: MIN }}
|
|
250
|
+
{stroke}
|
|
251
|
+
{...facets}
|
|
252
|
+
inset="20%"
|
|
253
|
+
{...typeof tickMinMax === 'object' ? tickMinMax : {}} />
|
|
254
|
+
<TickMark
|
|
255
|
+
data={boxData}
|
|
256
|
+
{...{ [yProp]: groupSymbol, [xProp]: MAX }}
|
|
257
|
+
{stroke}
|
|
258
|
+
{...facets}
|
|
259
|
+
inset="20%"
|
|
260
|
+
{...typeof tickMinMax === 'object' ? tickMinMax : {}} />
|
|
261
|
+
{/if}
|
|
262
|
+
<Dot
|
|
263
|
+
data={boxData.map((d) => d[OUTLIERS]).flat()}
|
|
264
|
+
{x}
|
|
265
|
+
{y}
|
|
266
|
+
{fx}
|
|
267
|
+
{fy}
|
|
268
|
+
{fill}
|
|
269
|
+
{stroke}
|
|
270
|
+
{...dot || {}} />
|
|
271
|
+
</GroupMultiple>
|