svelteplot 0.5.1 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants.d.ts +2 -1
- package/dist/constants.js +22 -0
- package/dist/core/Plot.svelte +7 -2
- package/dist/helpers/autoScales.js +10 -1
- package/dist/helpers/index.d.ts +1 -1
- package/dist/helpers/isRawValue.js +6 -1
- package/dist/helpers/reduce.js +3 -2
- package/dist/helpers/scales.d.ts +1 -0
- package/dist/helpers/scales.js +13 -10
- package/dist/marks/Area.svelte +9 -3
- package/dist/marks/Area.svelte.d.ts +1 -0
- package/dist/marks/AreaX.svelte.d.ts +1 -0
- package/dist/marks/BarX.svelte +1 -1
- package/dist/marks/BarY.svelte +1 -1
- package/dist/marks/BoxX.svelte +13 -1
- package/dist/marks/BoxY.svelte +12 -1
- package/dist/marks/Dot.svelte +6 -1
- package/dist/marks/Rect.svelte +2 -0
- package/dist/marks/RuleX.svelte +5 -1
- package/dist/marks/RuleY.svelte +6 -1
- package/dist/marks/TickX.svelte +5 -1
- package/dist/marks/TickY.svelte +5 -1
- package/dist/marks/helpers/RectPath.svelte +3 -1
- package/dist/transforms/bin.js +46 -26
- package/dist/transforms/sort.d.ts +1 -4
- package/dist/transforms/stack.d.ts +1 -1
- package/dist/transforms/stack.js +9 -3
- package/dist/types/channel.d.ts +1 -1
- package/dist/types/data.d.ts +1 -1
- package/dist/types/mark.d.ts +1 -1
- package/dist/types/plot.d.ts +8 -0
- package/package.json +2 -2
package/dist/constants.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { ScaleName, ScaleType, ScaledChannelName } from './types/index.js';
|
|
1
|
+
import type { ChannelName, ScaleName, ScaleType, ScaledChannelName } from './types/index.js';
|
|
2
2
|
export declare const SCALE_TYPES: Record<ScaleName, symbol>;
|
|
3
|
+
export declare const ORIGINAL_NAME_KEYS: Record<ChannelName, symbol>;
|
|
3
4
|
export declare const SCALES: ScaleName[];
|
|
4
5
|
export declare const VALID_SCALE_TYPES: Record<ScaleName, Set<ScaleType>>;
|
|
5
6
|
/**
|
package/dist/constants.js
CHANGED
|
@@ -10,6 +10,28 @@ export const SCALE_TYPES = {
|
|
|
10
10
|
fy: Symbol('fy'),
|
|
11
11
|
projection: Symbol('projection')
|
|
12
12
|
};
|
|
13
|
+
export const ORIGINAL_NAME_KEYS = {
|
|
14
|
+
x: Symbol('origName_x'),
|
|
15
|
+
x1: Symbol('origName_x1'),
|
|
16
|
+
x2: Symbol('origName_x2'),
|
|
17
|
+
y: Symbol('origName_y'),
|
|
18
|
+
y1: Symbol('origName_y1'),
|
|
19
|
+
y2: Symbol('origName_y2'),
|
|
20
|
+
fill: Symbol('origName_color'),
|
|
21
|
+
stroke: Symbol('origName_color'),
|
|
22
|
+
opacity: Symbol('origName_opacity'),
|
|
23
|
+
symbol: Symbol('origName_symbol'),
|
|
24
|
+
r: Symbol('origName_r'),
|
|
25
|
+
z: Symbol('origName_z'),
|
|
26
|
+
sort: Symbol('origName_sort'),
|
|
27
|
+
filter: Symbol('origName_filter'),
|
|
28
|
+
interval: Symbol('origName_interval'),
|
|
29
|
+
length: Symbol('origName_length'),
|
|
30
|
+
fx: Symbol('origName_fx'),
|
|
31
|
+
fy: Symbol('origName_fy'),
|
|
32
|
+
fillOpacity: Symbol('origName_opacity'),
|
|
33
|
+
strokeOpacity: Symbol('origName_opacity')
|
|
34
|
+
};
|
|
13
35
|
export const SCALES = [
|
|
14
36
|
'x',
|
|
15
37
|
'y',
|
package/dist/core/Plot.svelte
CHANGED
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
margin: 'auto',
|
|
66
66
|
colorScheme: 'turbo',
|
|
67
67
|
unknown: '#cccccc99',
|
|
68
|
-
|
|
68
|
+
sortOrdinalDomains: true,
|
|
69
69
|
categoricalColorScheme: 'observable10',
|
|
70
70
|
pointScaleHeight: 18,
|
|
71
71
|
bandScaleHeight: 30,
|
|
@@ -365,7 +365,12 @@
|
|
|
365
365
|
initialOpts: Partial<PlotOptions>,
|
|
366
366
|
opts: PlotOptionsParameters
|
|
367
367
|
): PlotOptions {
|
|
368
|
-
return mergeDeep<PlotOptions>(
|
|
368
|
+
return mergeDeep<PlotOptions>(
|
|
369
|
+
{},
|
|
370
|
+
{ sortOrdinalDomains: DEFAULTS.sortOrdinalDomains },
|
|
371
|
+
smartDefaultPlotOptions(opts),
|
|
372
|
+
initialOpts
|
|
373
|
+
);
|
|
369
374
|
}
|
|
370
375
|
|
|
371
376
|
function maybeMargin(
|
|
@@ -79,7 +79,16 @@ export function autoScale({ name, type, domain, scaleOptions, plotOptions, plotW
|
|
|
79
79
|
...(type === 'band' || type === 'point'
|
|
80
80
|
? {
|
|
81
81
|
align: scaleOptions.align,
|
|
82
|
-
|
|
82
|
+
...(type === 'point'
|
|
83
|
+
? {
|
|
84
|
+
// point scales don't have paddingInner/Outer, only padding
|
|
85
|
+
padding: maybeNumber(coalesce(scaleOptions.padding, plotOptions.padding, 0.15))
|
|
86
|
+
}
|
|
87
|
+
: {
|
|
88
|
+
// padding: maybeNumber(coalesce(scaleOptions.padding, plotOptions.padding, 0.15)),
|
|
89
|
+
paddingInner: maybeNumber(coalesce(scaleOptions.paddingInner, scaleOptions.padding, plotOptions.padding, 0.15)),
|
|
90
|
+
paddingOuter: maybeNumber(coalesce(scaleOptions.paddingOuter, scaleOptions.padding, plotOptions.padding, 0.15))
|
|
91
|
+
})
|
|
83
92
|
}
|
|
84
93
|
: {})
|
|
85
94
|
};
|
package/dist/helpers/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { Snippet } from 'svelte';
|
|
|
3
3
|
/**
|
|
4
4
|
* Returns first argument that is not null or undefined
|
|
5
5
|
*/
|
|
6
|
-
export declare function coalesce(...args: (RawValue | undefined | null)[]):
|
|
6
|
+
export declare function coalesce(...args: (RawValue | undefined | null)[]): string | number | boolean | symbol | object | null;
|
|
7
7
|
export declare function testFilter<T>(datum: T, options: Channels<T>): true | T | null;
|
|
8
8
|
export declare function randomId(): string;
|
|
9
9
|
export declare function isSnippet(value: unknown): value is Snippet;
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { isDate } from './typeChecks.js';
|
|
2
2
|
export default function (value) {
|
|
3
3
|
const t = typeof value;
|
|
4
|
-
return t === 'string' ||
|
|
4
|
+
return (t === 'string' ||
|
|
5
|
+
t === 'number' ||
|
|
6
|
+
t === 'boolean' ||
|
|
7
|
+
t === 'object' ||
|
|
8
|
+
isDate(value) ||
|
|
9
|
+
t === null);
|
|
5
10
|
}
|
package/dist/helpers/reduce.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { min, max, mode, sum, mean, median, variance, deviation, quantile } from 'd3-array';
|
|
2
2
|
import { resolveChannel } from './resolve.js';
|
|
3
3
|
import { POSITION_CHANNELS } from './index.js';
|
|
4
|
+
import { ORIGINAL_NAME_KEYS } from '../constants.js';
|
|
4
5
|
const niceReduceNames = {
|
|
5
6
|
count: 'Frequency',
|
|
6
7
|
deviation: 'Standard Deviation',
|
|
@@ -72,10 +73,10 @@ export function reduceOutputs(newDatum, data, options, outputs, channels, newCha
|
|
|
72
73
|
if (typeof channels[k] === 'string') {
|
|
73
74
|
// the named reducer is applied to a column name, so we can use a combination
|
|
74
75
|
// of both as axis labels, e.g. MEAN(weight)
|
|
75
|
-
newChannels[
|
|
76
|
+
newChannels[ORIGINAL_NAME_KEYS[k]] = `${reducerName} ( ${channels[k]} )`;
|
|
76
77
|
}
|
|
77
78
|
else {
|
|
78
|
-
newChannels[
|
|
79
|
+
newChannels[ORIGINAL_NAME_KEYS[k]] = reducerName;
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
82
|
}
|
package/dist/helpers/scales.d.ts
CHANGED
|
@@ -40,3 +40,4 @@ export declare function looksLikeANumber(input: string | number): boolean;
|
|
|
40
40
|
export declare function projectXY(scales: PlotScales, x: RawValue, y: RawValue, useXScale?: boolean, useYScale?: boolean): [number, number];
|
|
41
41
|
export declare function projectX(channel: 'x' | 'x1' | 'x2', scales: PlotScales, value: RawValue): number;
|
|
42
42
|
export declare function projectY(channel: 'y' | 'y1' | 'y2', scales: PlotScales, value: RawValue): number;
|
|
43
|
+
export declare function isOrdinalScale(scaleType: ScaleType): scaleType is "ordinal" | "point" | "band" | "categorical" | "threshold";
|
package/dist/helpers/scales.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { extent, ascending } from 'd3-array';
|
|
2
2
|
import { isColorOrNull, isDate, isDateOrNull, isNumberOrNull, isNumberOrNullOrNaN, isStringOrNull } from './typeChecks.js';
|
|
3
|
-
import { CHANNEL_SCALE, VALID_SCALE_TYPES } from '../constants.js';
|
|
3
|
+
import { CHANNEL_SCALE, ORIGINAL_NAME_KEYS, VALID_SCALE_TYPES } from '../constants.js';
|
|
4
4
|
import { isSymbolOrNull } from './typeChecks.js';
|
|
5
5
|
import { resolveProp, toChannelOption } from './resolve.js';
|
|
6
6
|
import isDataRecord from './isDataRecord.js';
|
|
@@ -48,7 +48,7 @@ export function createScale(name, scaleOptions, marks, plotOptions, plotWidth, p
|
|
|
48
48
|
let manualActiveMarks = 0;
|
|
49
49
|
const propNames = new Set();
|
|
50
50
|
const uniqueScaleProps = new Set();
|
|
51
|
-
let sortOrdinalDomain = true;
|
|
51
|
+
let sortOrdinalDomain = plotOptions.sortOrdinalDomains ?? true;
|
|
52
52
|
for (const mark of marks) {
|
|
53
53
|
// we only sort the scale domain alphabetically, if none of the
|
|
54
54
|
// marks that map to it are using the `sort` transform. Note that
|
|
@@ -126,9 +126,9 @@ export function createScale(name, scaleOptions, marks, plotOptions, plotWidth, p
|
|
|
126
126
|
}
|
|
127
127
|
// special handling of marks using the stackX/stackY transform
|
|
128
128
|
if ((name === 'x' || name === 'y') &&
|
|
129
|
-
mark.options[
|
|
130
|
-
!mark.options[
|
|
131
|
-
propNames.add(mark.options[
|
|
129
|
+
mark.options[ORIGINAL_NAME_KEYS[name]] &&
|
|
130
|
+
!mark.options[ORIGINAL_NAME_KEYS[name]].startsWith('__')) {
|
|
131
|
+
propNames.add(mark.options[ORIGINAL_NAME_KEYS[name]]);
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
else {
|
|
@@ -152,11 +152,7 @@ export function createScale(name, scaleOptions, marks, plotOptions, plotWidth, p
|
|
|
152
152
|
throw new Error(`Invalid scale type ${type} for scale
|
|
153
153
|
${name}. Valid types are ${[...VALID_SCALE_TYPES[name]].join(', ')}`);
|
|
154
154
|
}
|
|
155
|
-
const isOrdinal = type
|
|
156
|
-
type === 'point' ||
|
|
157
|
-
type === 'ordinal' ||
|
|
158
|
-
type === 'categorical' ||
|
|
159
|
-
type === 'threshold';
|
|
155
|
+
const isOrdinal = isOrdinalScale(type);
|
|
160
156
|
if (isOrdinal && sortOrdinalDomain) {
|
|
161
157
|
valueArr.sort(ascending);
|
|
162
158
|
}
|
|
@@ -351,3 +347,10 @@ export function projectY(channel, scales, value) {
|
|
|
351
347
|
? scales.y.fn.bandwidth()
|
|
352
348
|
: 0));
|
|
353
349
|
}
|
|
350
|
+
export function isOrdinalScale(scaleType) {
|
|
351
|
+
return (scaleType === 'band' ||
|
|
352
|
+
scaleType === 'point' ||
|
|
353
|
+
scaleType === 'ordinal' ||
|
|
354
|
+
scaleType === 'categorical' ||
|
|
355
|
+
scaleType === 'threshold');
|
|
356
|
+
}
|
package/dist/marks/Area.svelte
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
sort?: ConstantAccessor<RawValue> | { channel: 'stroke' | 'fill' };
|
|
15
15
|
stack?: Partial<StackOptions>;
|
|
16
16
|
canvas?: boolean;
|
|
17
|
+
areaClass?: ConstantAccessor<string, Datum>;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
import Mark from '../Mark.svelte';
|
|
@@ -37,7 +38,6 @@
|
|
|
37
38
|
ChannelAccessor,
|
|
38
39
|
ScaledDataRecord,
|
|
39
40
|
LinkableMarkProps,
|
|
40
|
-
PlotDefaults,
|
|
41
41
|
RawValue
|
|
42
42
|
} from '../types/index.js';
|
|
43
43
|
import type { StackOptions } from '../transforms/stack.js';
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
curve = 'linear' as CurveName,
|
|
60
60
|
tension = 0,
|
|
61
61
|
class: className = '',
|
|
62
|
+
areaClass,
|
|
62
63
|
canvas = false,
|
|
63
64
|
...options
|
|
64
65
|
}: AreaMarkProps = $derived({ ...DEFAULTS, ...markProps });
|
|
@@ -114,13 +115,14 @@
|
|
|
114
115
|
{data}
|
|
115
116
|
channels={['x1', 'x2', 'y1', 'y2', 'fill', 'stroke', 'opacity', 'fillOpacity', 'strokeOpacity']}
|
|
116
117
|
required={['x1', 'y1']}
|
|
118
|
+
{...markProps}
|
|
117
119
|
{...options}>
|
|
118
120
|
{#snippet children({ mark, usedScales, scaledData })}
|
|
119
121
|
{@const grouped = groupAndSort(scaledData)}
|
|
120
122
|
{#if canvas}
|
|
121
123
|
<AreaCanvas groupedAreaData={grouped} {mark} {usedScales} {areaPath} />
|
|
122
124
|
{:else}
|
|
123
|
-
<GroupMultiple length={grouped.length}>
|
|
125
|
+
<GroupMultiple class={className} length={grouped.length}>
|
|
124
126
|
{#each grouped as areaData, i (i)}
|
|
125
127
|
{@const datum = areaData[0]}
|
|
126
128
|
{#if areaData.length > 0}
|
|
@@ -134,7 +136,11 @@
|
|
|
134
136
|
usedScales
|
|
135
137
|
)}
|
|
136
138
|
<path
|
|
137
|
-
class={[
|
|
139
|
+
class={[
|
|
140
|
+
'area',
|
|
141
|
+
resolveProp(areaClass, areaData[0].datum),
|
|
142
|
+
styleClass
|
|
143
|
+
]}
|
|
138
144
|
clip-path={options.clipPath}
|
|
139
145
|
d={areaPath(areaData)}
|
|
140
146
|
{@attach addEventHandlers({
|
|
@@ -76,6 +76,7 @@ declare class __sveltets_Render<Datum extends DataRow> {
|
|
|
76
76
|
};
|
|
77
77
|
stack?: Partial<import("../transforms/stack.js").StackOptions>;
|
|
78
78
|
canvas?: boolean;
|
|
79
|
+
areaClass?: import("../types/index.js").ConstantAccessor<string, Record<string | symbol, import("../types/data").RawValue>>;
|
|
79
80
|
}, "y1" | "y2"> & {
|
|
80
81
|
x?: ChannelAccessor<Datum>;
|
|
81
82
|
y?: ChannelAccessor<Datum>;
|
package/dist/marks/BarX.svelte
CHANGED
package/dist/marks/BarY.svelte
CHANGED
package/dist/marks/BoxX.svelte
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
import { type ComponentProps } from 'svelte';
|
|
11
11
|
import type BoxY from './BoxY.svelte';
|
|
12
12
|
import { getPlotDefaults } from '../hooks/plotDefaults.js';
|
|
13
|
+
import { IS_SORTED } from '../transforms/sort';
|
|
14
|
+
import { sort } from 'd3-array';
|
|
13
15
|
|
|
14
16
|
let markProps: BoxXMarkProps = $props();
|
|
15
17
|
|
|
@@ -70,6 +72,8 @@
|
|
|
70
72
|
...(fy != null && { fy: FY })
|
|
71
73
|
});
|
|
72
74
|
|
|
75
|
+
const sortProps = { [IS_SORTED]: true };
|
|
76
|
+
|
|
73
77
|
const boxData = $derived(
|
|
74
78
|
grouped
|
|
75
79
|
.map((row) => {
|
|
@@ -107,7 +111,15 @@
|
|
|
107
111
|
</script>
|
|
108
112
|
|
|
109
113
|
<GroupMultiple class="box-x {className || ''}" length={className ? 2 : grouped.length}>
|
|
110
|
-
<RuleY
|
|
114
|
+
<RuleY
|
|
115
|
+
data={boxData}
|
|
116
|
+
y={Y}
|
|
117
|
+
x1={MIN}
|
|
118
|
+
x2={P25}
|
|
119
|
+
{stroke}
|
|
120
|
+
{...rule || {}}
|
|
121
|
+
{...facets}
|
|
122
|
+
{...sortProps} />
|
|
111
123
|
<RuleY data={boxData} y={Y} x1={P75} x2={MAX} {stroke} {...rule || {}} {...facets} />
|
|
112
124
|
<BarX data={boxData} y={Y} x1={P25} x2={P75} {fill} {stroke} {...facets} {...bar || {}} />
|
|
113
125
|
{#if tickMedian}
|
package/dist/marks/BoxY.svelte
CHANGED
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
import { resolveChannel } from '../helpers/resolve.js';
|
|
34
34
|
import { getPlotDefaults } from '../hooks/plotDefaults.js';
|
|
35
35
|
import type { BaseMarkProps, ChannelAccessor, DataRecord } from '../types';
|
|
36
|
+
import { IS_SORTED } from '../transforms/sort';
|
|
36
37
|
|
|
37
38
|
let markProps: BoxYMarkProps = $props();
|
|
38
39
|
|
|
@@ -93,6 +94,8 @@
|
|
|
93
94
|
...(fy != null && { fy: FY })
|
|
94
95
|
});
|
|
95
96
|
|
|
97
|
+
const sortProps = { [IS_SORTED]: true };
|
|
98
|
+
|
|
96
99
|
const boxData = $derived(
|
|
97
100
|
grouped
|
|
98
101
|
.map((row) => {
|
|
@@ -129,7 +132,15 @@
|
|
|
129
132
|
</script>
|
|
130
133
|
|
|
131
134
|
<GroupMultiple class="box-y {className || ''}" length={className ? 2 : grouped.length}>
|
|
132
|
-
<RuleX
|
|
135
|
+
<RuleX
|
|
136
|
+
data={boxData}
|
|
137
|
+
x={X}
|
|
138
|
+
y1={MIN}
|
|
139
|
+
y2={P25}
|
|
140
|
+
{stroke}
|
|
141
|
+
{...sortProps}
|
|
142
|
+
{...rule || {}}
|
|
143
|
+
{...facets} />
|
|
133
144
|
<RuleX data={boxData} x={X} y1={P75} y2={MAX} {stroke} {...rule || {}} {...facets} />
|
|
134
145
|
<BarY data={boxData} x={X} y1={P25} y2={P75} {fill} {stroke} {...facets} {...bar || {}} />
|
|
135
146
|
{#if tickMedian}
|
package/dist/marks/Dot.svelte
CHANGED
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
import { addEventHandlers } from './helpers/events.js';
|
|
33
33
|
import Anchor from './helpers/Anchor.svelte';
|
|
34
34
|
import { getPlotDefaults } from '../hooks/plotDefaults.js';
|
|
35
|
+
import { isOrdinalScale } from '../helpers/scales.js';
|
|
35
36
|
|
|
36
37
|
const DEFAULTS = {
|
|
37
38
|
...getPlotDefaults().dot
|
|
@@ -60,7 +61,11 @@
|
|
|
60
61
|
recordizeXY({
|
|
61
62
|
data,
|
|
62
63
|
// sort by descending radius by default
|
|
63
|
-
...(options.r
|
|
64
|
+
...(options.r &&
|
|
65
|
+
!isOrdinalScale(plot.scales.x.type) &&
|
|
66
|
+
!isOrdinalScale(plot.scales.y.type)
|
|
67
|
+
? { sort: { channel: '-r' } }
|
|
68
|
+
: {}),
|
|
64
69
|
...options,
|
|
65
70
|
...(options.fill === true ? { fill: 'currentColor' } : {})
|
|
66
71
|
})
|
package/dist/marks/Rect.svelte
CHANGED
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
import GroupMultiple from './helpers/GroupMultiple.svelte';
|
|
32
32
|
import RectPath from './helpers/RectPath.svelte';
|
|
33
33
|
import { getPlotDefaults } from '../hooks/plotDefaults.js';
|
|
34
|
+
import { IS_SORTED } from '../transforms/sort';
|
|
34
35
|
|
|
35
36
|
let markProps: RectMarkProps = $props();
|
|
36
37
|
|
|
@@ -59,6 +60,7 @@
|
|
|
59
60
|
type="rect"
|
|
60
61
|
required={[]}
|
|
61
62
|
channels={['x1', 'x2', 'y1', 'y2', 'fill', 'stroke', 'opacity', 'fillOpacity', 'strokeOpacity']}
|
|
63
|
+
{...markProps}
|
|
62
64
|
{...args}>
|
|
63
65
|
{#snippet children({ usedScales, scaledData })}
|
|
64
66
|
<GroupMultiple class={scaledData.length > 1 ? className : null} length={scaledData.length}>
|
package/dist/marks/RuleX.svelte
CHANGED
|
@@ -45,7 +45,11 @@
|
|
|
45
45
|
const args = $derived(recordizeX({ data, ...options }, { withIndex: false }));
|
|
46
46
|
</script>
|
|
47
47
|
|
|
48
|
-
<Mark
|
|
48
|
+
<Mark
|
|
49
|
+
type="ruleX"
|
|
50
|
+
channels={['x', 'y1', 'y2', 'stroke', 'opacity', 'strokeOpacity']}
|
|
51
|
+
{...markProps}
|
|
52
|
+
{...args}>
|
|
49
53
|
{#snippet children({ mark, scaledData, usedScales })}
|
|
50
54
|
<GroupMultiple class="rule-x {className || ''}" length={className ? 2 : scaledData.length}>
|
|
51
55
|
{#each scaledData as d, i (i)}
|
package/dist/marks/RuleY.svelte
CHANGED
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
ChannelAccessor
|
|
25
25
|
} from '../types/index.js';
|
|
26
26
|
import { getPlotDefaults } from '../hooks/plotDefaults.js';
|
|
27
|
+
import { IS_SORTED } from '../transforms/sort';
|
|
27
28
|
|
|
28
29
|
let markProps: RuleYMarkProps = $props();
|
|
29
30
|
const DEFAULTS = {
|
|
@@ -44,7 +45,11 @@
|
|
|
44
45
|
const args = $derived(recordizeY({ data, ...options }, { withIndex: false }));
|
|
45
46
|
</script>
|
|
46
47
|
|
|
47
|
-
<Mark
|
|
48
|
+
<Mark
|
|
49
|
+
type="ruleY"
|
|
50
|
+
channels={['y', 'x1', 'x2', 'stroke', 'opacity', 'strokeOpacity']}
|
|
51
|
+
{...markProps}
|
|
52
|
+
{...args}>
|
|
48
53
|
{#snippet children({ scaledData, usedScales })}
|
|
49
54
|
<GroupMultiple class="rule-y {className || ''}" length={className ? 2 : args.data.length}>
|
|
50
55
|
{#each scaledData as d, i (i)}
|
package/dist/marks/TickX.svelte
CHANGED
|
@@ -61,7 +61,11 @@
|
|
|
61
61
|
let testFacet = $derived(getTestFacet());
|
|
62
62
|
</script>
|
|
63
63
|
|
|
64
|
-
<Mark
|
|
64
|
+
<Mark
|
|
65
|
+
type="tickX"
|
|
66
|
+
channels={['x', 'y', 'stroke', 'opacity', 'strokeOpacity']}
|
|
67
|
+
{...markProps}
|
|
68
|
+
{...args}>
|
|
65
69
|
{#snippet children({ mark, usedScales })}
|
|
66
70
|
<g class="tick-x">
|
|
67
71
|
{#each args.data as datum, i (i)}
|
package/dist/marks/TickY.svelte
CHANGED
|
@@ -60,7 +60,11 @@
|
|
|
60
60
|
let testFacet = $derived(getTestFacet());
|
|
61
61
|
</script>
|
|
62
62
|
|
|
63
|
-
<Mark
|
|
63
|
+
<Mark
|
|
64
|
+
type="tickY"
|
|
65
|
+
channels={['x', 'y', 'stroke', 'opacity', 'strokeOpacity']}
|
|
66
|
+
{...markProps}
|
|
67
|
+
{...args}>
|
|
64
68
|
{#snippet children({ mark, usedScales })}
|
|
65
69
|
<g class="tick-y">
|
|
66
70
|
{#each args.data as datum, i (i)}
|
|
@@ -81,7 +81,9 @@ Helper component for rendering rectangular marks in SVG
|
|
|
81
81
|
useInsetAsFallbackVertically ? inset : 0
|
|
82
82
|
) as number)
|
|
83
83
|
);
|
|
84
|
-
const borderRadius = $derived(
|
|
84
|
+
const borderRadius = $derived(
|
|
85
|
+
resolveProp(options.borderRadius, datum?.datum, 0) as BorderRadius
|
|
86
|
+
);
|
|
85
87
|
const hasBorderRadius = $derived(
|
|
86
88
|
(typeof borderRadius === 'number' && borderRadius > 0) ||
|
|
87
89
|
(typeof borderRadius === 'object' &&
|
package/dist/transforms/bin.js
CHANGED
|
@@ -4,6 +4,15 @@ import { bin as d3Bin, extent, thresholdFreedmanDiaconis, thresholdScott, thresh
|
|
|
4
4
|
import { reduceOutputs } from '../helpers/reduce.js';
|
|
5
5
|
import { groupFacetsAndZ } from '../helpers/group.js';
|
|
6
6
|
import { isDate } from '../helpers/typeChecks.js';
|
|
7
|
+
import { ORIGINAL_NAME_KEYS } from '../constants';
|
|
8
|
+
const CHANNELS = {
|
|
9
|
+
x: Symbol('x'),
|
|
10
|
+
x1: Symbol('x1'),
|
|
11
|
+
x2: Symbol('x2'),
|
|
12
|
+
y: Symbol('y'),
|
|
13
|
+
y1: Symbol('y1'),
|
|
14
|
+
y2: Symbol('y2')
|
|
15
|
+
};
|
|
7
16
|
const ThresholdGenerators = {
|
|
8
17
|
auto: thresholdScott,
|
|
9
18
|
scott: thresholdScott,
|
|
@@ -41,19 +50,19 @@ function binBy(byDim, { data, ...channels }, options) {
|
|
|
41
50
|
[byDim === 'x' ? 'insetLeft' : 'insetTop']: 0.5,
|
|
42
51
|
[byDim === 'x' ? 'insetRight' : 'insetBottom']: 0.5,
|
|
43
52
|
...channels,
|
|
44
|
-
[`${byDim}`]: `__${byDim}`,
|
|
45
|
-
[`${byDim}1`]:
|
|
46
|
-
[`${byDim}2`]:
|
|
47
|
-
[
|
|
53
|
+
[`${byDim}`]: CHANNELS[byDim], // `__${byDim}`,
|
|
54
|
+
[`${byDim}1`]: CHANNELS[`${byDim}1`],
|
|
55
|
+
[`${byDim}2`]: CHANNELS[`${byDim}2`],
|
|
56
|
+
[ORIGINAL_NAME_KEYS[byDim]]: typeof channels[byDim] === 'string' ? channels[byDim] : null
|
|
48
57
|
};
|
|
49
58
|
const newData = [];
|
|
50
59
|
let passedGroups = [];
|
|
51
60
|
const bins = bin(data);
|
|
52
61
|
(options.cumulative < 0 ? bins.toReversed() : bins).forEach((group) => {
|
|
53
62
|
const itemBinProps = {
|
|
54
|
-
[
|
|
55
|
-
[
|
|
56
|
-
[
|
|
63
|
+
[CHANNELS[`${byDim}1`]]: group.x0,
|
|
64
|
+
[CHANNELS[`${byDim}2`]]: group.x1,
|
|
65
|
+
[CHANNELS[`${byDim}`]]: isDate(group.x0)
|
|
57
66
|
? new Date(Math.round((group.x0.getTime() + group.x1.getTime()) * 0.5))
|
|
58
67
|
: (group.x0 + group.x1) * 0.5
|
|
59
68
|
};
|
|
@@ -101,11 +110,12 @@ export function bin({ data, ...channels }, options = { thresholds: 'auto', cumul
|
|
|
101
110
|
// channels.x is the input
|
|
102
111
|
binX.value((d) => resolveChannel('x', d, channels));
|
|
103
112
|
binY.value((d) => resolveChannel('y', d, channels));
|
|
113
|
+
let yThresholds = [];
|
|
104
114
|
if (interval) {
|
|
105
115
|
const [xlo, xhi] = extent(data.map((d) => resolveChannel('x', d, channels)));
|
|
106
116
|
const [ylo, yhi] = extent(data.map((d) => resolveChannel('y', d, channels)));
|
|
107
117
|
binX.thresholds(maybeInterval(interval).range(xlo, xhi));
|
|
108
|
-
binY.thresholds(maybeInterval(interval).range(ylo, yhi));
|
|
118
|
+
binY.thresholds((yThresholds = maybeInterval(interval).range(ylo, yhi)));
|
|
109
119
|
}
|
|
110
120
|
else if (thresholds) {
|
|
111
121
|
// when binning in x and y, we need to ensure we are using consistent thresholds
|
|
@@ -114,7 +124,7 @@ export function bin({ data, ...channels }, options = { thresholds: 'auto', cumul
|
|
|
114
124
|
: thresholds;
|
|
115
125
|
binX.thresholds(t);
|
|
116
126
|
binY.thresholds(t);
|
|
117
|
-
|
|
127
|
+
yThresholds = binY(data)
|
|
118
128
|
.slice(1)
|
|
119
129
|
.map((g) => g.x0);
|
|
120
130
|
binY.thresholds(yThresholds);
|
|
@@ -124,14 +134,14 @@ export function bin({ data, ...channels }, options = { thresholds: 'auto', cumul
|
|
|
124
134
|
let newChannels = {
|
|
125
135
|
inset: 0.5,
|
|
126
136
|
...channels,
|
|
127
|
-
x:
|
|
128
|
-
x1:
|
|
129
|
-
x2:
|
|
130
|
-
y:
|
|
131
|
-
y1:
|
|
132
|
-
y2:
|
|
133
|
-
|
|
134
|
-
|
|
137
|
+
x: CHANNELS.x,
|
|
138
|
+
x1: CHANNELS.x1,
|
|
139
|
+
x2: CHANNELS.x2,
|
|
140
|
+
y: CHANNELS.y,
|
|
141
|
+
y1: CHANNELS.y1,
|
|
142
|
+
y2: CHANNELS.y2,
|
|
143
|
+
[ORIGINAL_NAME_KEYS.x]: typeof channels.x === 'string' ? channels.x : null,
|
|
144
|
+
[ORIGINAL_NAME_KEYS.y]: typeof channels.y === 'string' ? channels.y : null
|
|
135
145
|
};
|
|
136
146
|
const groupBy = channels.z ? 'z' : channels.fill ? 'fill' : channels.stroke ? 'stroke' : true;
|
|
137
147
|
const groupByPropName = groupBy !== true && typeof channels[groupBy] === 'string' ? channels[groupBy] : '__group';
|
|
@@ -141,20 +151,30 @@ export function bin({ data, ...channels }, options = { thresholds: 'auto', cumul
|
|
|
141
151
|
const newData = [];
|
|
142
152
|
binX(data).forEach((groupX) => {
|
|
143
153
|
const newRecordBaseX = {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
154
|
+
[CHANNELS.x1]: groupX.x0,
|
|
155
|
+
[CHANNELS.x2]: groupX.x1,
|
|
156
|
+
[CHANNELS.x]: isDate(groupX.x0)
|
|
147
157
|
? new Date(Math.round((groupX.x0.getTime() + groupX.x1.getTime()) * 0.5))
|
|
148
158
|
: (groupX.x0 + groupX.x1) * 0.5
|
|
149
159
|
};
|
|
150
|
-
|
|
160
|
+
const [ylo, yhi] = extent(groupX.map((d) => resolveChannel('y', d, channels)));
|
|
161
|
+
const tExtentLo = yThresholds.filter((d) => d < ylo).at(-1);
|
|
162
|
+
const tExtentHi = yThresholds.filter((d) => d > yhi).at(0);
|
|
163
|
+
binY(groupX).forEach((groupY, i) => {
|
|
164
|
+
if (groupY.length === 0)
|
|
165
|
+
return;
|
|
166
|
+
// The first bin.x0 is always equal to the minimum domain value,
|
|
167
|
+
// and the last bin.x1 is always equal to the maximum domain value,
|
|
168
|
+
// therefore we need to align with the thresholds
|
|
169
|
+
const y1 = groupY.x0 === ylo ? tExtentLo : groupY.x0;
|
|
170
|
+
const y2 = groupY.x1 === yhi ? tExtentHi : groupY.x1;
|
|
151
171
|
const newRecordBaseY = {
|
|
152
172
|
...newRecordBaseX,
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
? new Date(Math.round((
|
|
157
|
-
: (
|
|
173
|
+
[CHANNELS.y1]: y1,
|
|
174
|
+
[CHANNELS.y2]: y2,
|
|
175
|
+
[CHANNELS.y]: isDate(y1)
|
|
176
|
+
? new Date(Math.round((y1.getTime() + y2.getTime()) * 0.5))
|
|
177
|
+
: (y1 + y2) * 0.5
|
|
158
178
|
};
|
|
159
179
|
const newGroupChannels = groupFacetsAndZ(groupY, channels, (items, itemGroupProps) => {
|
|
160
180
|
const newRecord = {
|
|
@@ -4,10 +4,7 @@ export declare const IS_SORTED: unique symbol;
|
|
|
4
4
|
export declare function sort<T>({ data, ...channels }: TransformArg<T>, options?: {
|
|
5
5
|
reverse?: boolean;
|
|
6
6
|
}): {
|
|
7
|
-
[IS_SORTED]: string | number | true | symbol |
|
|
8
|
-
channel: string;
|
|
9
|
-
order?: "ascending" | "descending";
|
|
10
|
-
} | ((d: T, index: number) => import("../types/index.js").RawValue);
|
|
7
|
+
[IS_SORTED]: string | number | true | symbol | object;
|
|
11
8
|
sort: null;
|
|
12
9
|
filter?: import("../types/index.js").ConstantAccessor<boolean, T>;
|
|
13
10
|
facet?: "auto" | "include" | "exclude" | undefined;
|
|
@@ -5,7 +5,7 @@ export type StackOptions = {
|
|
|
5
5
|
offset: null | StackOffset;
|
|
6
6
|
order: null | StackOrder;
|
|
7
7
|
reverse: boolean;
|
|
8
|
-
};
|
|
8
|
+
} | false;
|
|
9
9
|
export declare function stackY<T>({ data, ...channels }: TransformArg<T>, opts?: Partial<StackOptions>): TransformArg<T>;
|
|
10
10
|
export declare function stackX<T>({ data, ...channels }: TransformArg<T>, opts?: Partial<StackOptions>): TransformArg<T>;
|
|
11
11
|
export declare function stackMosaicX<T>(args: any, opts: any): {
|
package/dist/transforms/stack.js
CHANGED
|
@@ -5,7 +5,7 @@ import { sum, groups as d3Groups, min, range } from 'd3-array';
|
|
|
5
5
|
import { groupFacetsAndZ } from '../helpers/group';
|
|
6
6
|
import { filter } from './filter.js';
|
|
7
7
|
import { sort } from './sort.js';
|
|
8
|
-
import { INDEX } from '../constants.js';
|
|
8
|
+
import { INDEX, ORIGINAL_NAME_KEYS } from '../constants.js';
|
|
9
9
|
import { indexData, RAW_VALUE } from './recordize.js';
|
|
10
10
|
const S = {
|
|
11
11
|
x: Symbol('x'),
|
|
@@ -38,6 +38,10 @@ const STACK_OFFSET = {
|
|
|
38
38
|
normalize: stackOffsetExpand
|
|
39
39
|
};
|
|
40
40
|
function stackXY(byDim, data, channels, options) {
|
|
41
|
+
if (options === false) {
|
|
42
|
+
// no stacking
|
|
43
|
+
return { data, ...channels };
|
|
44
|
+
}
|
|
41
45
|
// we need to stack the data for each facet separately
|
|
42
46
|
const groupFacetsBy = [
|
|
43
47
|
channels.fx != null ? 'fx' : null,
|
|
@@ -143,8 +147,8 @@ function stackXY(byDim, data, channels, options) {
|
|
|
143
147
|
data: out,
|
|
144
148
|
...channels,
|
|
145
149
|
[byDim]: undefined,
|
|
146
|
-
...(typeof channels[byDim] === 'string' && !channels[
|
|
147
|
-
? { [
|
|
150
|
+
...(typeof channels[byDim] === 'string' && !channels[ORIGINAL_NAME_KEYS[byDim]]
|
|
151
|
+
? { [ORIGINAL_NAME_KEYS[byDim]]: channels[byDim] }
|
|
148
152
|
: {}),
|
|
149
153
|
...{ [byLow]: S[byLow], [byHigh]: S[byHigh] }
|
|
150
154
|
};
|
|
@@ -158,6 +162,8 @@ export function stackX({ data, ...channels }, opts = {}) {
|
|
|
158
162
|
return stackXY('x', data, channels, applyDefaults(opts));
|
|
159
163
|
}
|
|
160
164
|
function applyDefaults(opts) {
|
|
165
|
+
if (opts === false)
|
|
166
|
+
return false;
|
|
161
167
|
if (opts.offset === 'wiggle' && opts.order === undefined) {
|
|
162
168
|
return { ...DEFAULT_STACK_OPTIONS, order: 'inside-out', ...opts };
|
|
163
169
|
}
|
package/dist/types/channel.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ConstantAccessor, RawValue } from './index.js';
|
|
2
|
-
export type Channels<T> = Record<string, ChannelAccessor<T> | ConstantAccessor<T, string | number | boolean | symbol>>;
|
|
2
|
+
export type Channels<T> = Record<string | symbol, ChannelAccessor<T> | ConstantAccessor<T, string | number | boolean | symbol>>;
|
|
3
3
|
export type ChannelAccessor<T = Record<string | symbol, RawValue>> = ChannelValue<T> | {
|
|
4
4
|
/** the channel value */
|
|
5
5
|
value: ChannelValue<T>;
|
package/dist/types/data.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ScaledChannelName, ScaledChannelType } from './channel.js';
|
|
2
|
-
export type RawValue = number | Date | boolean | string | symbol;
|
|
2
|
+
export type RawValue = number | Date | boolean | string | symbol | object;
|
|
3
3
|
export type DataRecord<T = Record<string | symbol, RawValue>> = T;
|
|
4
4
|
export type ResolvedDataRecord<T = Record<string | symbol, RawValue>> = Partial<Record<ScaledChannelName, any>> & {
|
|
5
5
|
datum: DataRecord<T>;
|
package/dist/types/mark.d.ts
CHANGED
|
@@ -126,5 +126,5 @@ export type BaseRectMarkProps<T> = {
|
|
|
126
126
|
insetTop?: ConstantAccessor<number, T>;
|
|
127
127
|
insetRight?: ConstantAccessor<number, T>;
|
|
128
128
|
insetBottom?: ConstantAccessor<number, T>;
|
|
129
|
-
borderRadius?: BorderRadius
|
|
129
|
+
borderRadius?: ConstantAccessor<BorderRadius, T>;
|
|
130
130
|
};
|
package/dist/types/plot.d.ts
CHANGED
|
@@ -112,6 +112,10 @@ export type PlotDefaults = {
|
|
|
112
112
|
* default dot radius for line markers, used in dot, circle, and circle-stroke markers
|
|
113
113
|
*/
|
|
114
114
|
markerDotRadius: number;
|
|
115
|
+
/**
|
|
116
|
+
* if set to true, ordinal domains will be sorted alphabetically
|
|
117
|
+
*/
|
|
118
|
+
sortOrdinalDomains: boolean;
|
|
115
119
|
/**
|
|
116
120
|
* default props for area marks, applied to area, areaX, and areaY marks
|
|
117
121
|
*/
|
|
@@ -467,5 +471,9 @@ export type PlotOptions = {
|
|
|
467
471
|
* pass a @emotion/css function to style plot using dynamic classes
|
|
468
472
|
*/
|
|
469
473
|
css: (d: string) => string | undefined;
|
|
474
|
+
/**
|
|
475
|
+
* if set to true, ordinal domains will be sorted alphabetically
|
|
476
|
+
*/
|
|
477
|
+
sortOrdinalDomains: boolean;
|
|
470
478
|
};
|
|
471
479
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelteplot",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Gregor Aisch",
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
"fast-equals": "^5.3.2",
|
|
113
113
|
"interval-tree-1d": "^1.0.4",
|
|
114
114
|
"merge-deep": "^3.0.3",
|
|
115
|
-
"svelte": "5.
|
|
115
|
+
"svelte": "5.43.0"
|
|
116
116
|
},
|
|
117
117
|
"scripts": {
|
|
118
118
|
"dev": "vite dev",
|