svelteplot 0.0.1-alpha.11 → 0.0.1-alpha.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Plot.svelte +6 -6
- package/dist/classes/Mark.svelte.js +4 -0
- package/dist/classes/Plot.svelte.js +47 -20
- package/dist/classes/Scale.svelte.js +95 -0
- package/dist/contants.d.ts +3 -3
- package/dist/contants.js +18 -16
- package/dist/helpers/autoTimeFormat.d.ts +2 -2
- package/dist/helpers/createScale.js +19 -6
- package/dist/helpers/curves.d.ts +3 -0
- package/dist/helpers/curves.js +42 -0
- package/dist/helpers/getBaseStyles.js +2 -2
- package/dist/helpers/getLogTicks.js +5 -5
- package/dist/helpers/isRawValue.d.ts +2 -0
- package/dist/helpers/isRawValue.js +5 -0
- package/dist/helpers/resolveChannel.d.ts +6 -2
- package/dist/helpers/resolveChannel.js +15 -5
- package/dist/helpers/wrapValueArray.d.ts +6 -0
- package/dist/helpers/wrapValueArray.js +5 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.js +11 -3
- package/dist/marks/Area.svelte +35 -21
- package/dist/marks/Area.svelte.d.ts +14 -1
- package/dist/marks/AreaX.svelte +8 -15
- package/dist/marks/AreaX.svelte.d.ts +17 -4
- package/dist/marks/AreaY.svelte +9 -10
- package/dist/marks/AreaY.svelte.d.ts +17 -4
- package/dist/marks/AxisX.svelte +7 -8
- package/dist/marks/AxisY.svelte +10 -6
- package/dist/marks/BarX.svelte +52 -0
- package/dist/marks/BarX.svelte.d.ts +25 -0
- package/dist/marks/BarY.svelte +52 -0
- package/dist/marks/BarY.svelte.d.ts +25 -0
- package/dist/marks/BaseMark.svelte +9 -2
- package/dist/marks/BaseMark.svelte.d.ts +2 -1
- package/dist/marks/Dot.svelte +26 -40
- package/dist/marks/Dot.svelte.d.ts +9 -1
- package/dist/marks/GridX.svelte +26 -15
- package/dist/marks/GridX.svelte.d.ts +3 -3
- package/dist/marks/GridY.svelte +15 -4
- package/dist/marks/Line.svelte +16 -27
- package/dist/marks/Line.svelte.d.ts +13 -1
- package/dist/marks/LineX.svelte +5 -7
- package/dist/marks/LineX.svelte.d.ts +11 -4
- package/dist/marks/LineY.svelte +4 -6
- package/dist/marks/LineY.svelte.d.ts +11 -3
- package/dist/marks/RuleX.svelte +15 -7
- package/dist/marks/RuleY.svelte +15 -7
- package/dist/marks/TickX.svelte +30 -0
- package/dist/marks/TickX.svelte.d.ts +15 -0
- package/dist/marks/TickY.svelte +30 -0
- package/dist/marks/TickY.svelte.d.ts +15 -0
- package/dist/transforms/normalize.d.ts +18 -0
- package/dist/transforms/normalize.js +25 -0
- package/dist/transforms/recordize.d.ts +3 -0
- package/dist/transforms/recordize.js +28 -0
- package/dist/transforms/rename.d.ts +4 -0
- package/dist/transforms/rename.js +10 -0
- package/dist/transforms/stack.d.ts +11 -0
- package/dist/transforms/stack.js +73 -0
- package/dist/types.d.ts +51 -35
- package/package.json +20 -15
- package/dist/classes/Channel.svelte.js +0 -74
package/dist/Plot.svelte
CHANGED
|
@@ -21,6 +21,7 @@ let {
|
|
|
21
21
|
title = "",
|
|
22
22
|
subtitle = "",
|
|
23
23
|
caption = "",
|
|
24
|
+
testid,
|
|
24
25
|
// scales
|
|
25
26
|
radius = null,
|
|
26
27
|
color = null,
|
|
@@ -70,11 +71,10 @@ function onMouseMove(evt) {
|
|
|
70
71
|
if (onmousemove)
|
|
71
72
|
onmousemove({ ...evt, plot });
|
|
72
73
|
}
|
|
73
|
-
$inspect(plot.x.domain);
|
|
74
74
|
let hasLegend = $derived(color?.legend || symbol?.legend);
|
|
75
75
|
</script>
|
|
76
76
|
|
|
77
|
-
<figure class="svelteplot" bind:clientWidth={width} style:max-width={maxWidth}>
|
|
77
|
+
<figure data-testid={testid} class="svelteplot" bind:clientWidth={width} style:max-width={maxWidth}>
|
|
78
78
|
<svg
|
|
79
79
|
role="document"
|
|
80
80
|
{width}
|
|
@@ -82,10 +82,10 @@ let hasLegend = $derived(color?.legend || symbol?.legend);
|
|
|
82
82
|
onmousemove={onmousemove ? onMouseMove : null}
|
|
83
83
|
>
|
|
84
84
|
<!-- automatic grids -->
|
|
85
|
-
{#if (grid || x?.grid
|
|
86
|
-
{#if (grid || y?.grid
|
|
85
|
+
{#if (grid && plot.hasScaleX) || x?.grid}<GridX automatic />{/if}
|
|
86
|
+
{#if (grid && plot.hasScaleY) || y?.grid}<GridY automatic />{/if}
|
|
87
87
|
|
|
88
|
-
{#if !plot.hasAxisXMark && plot.
|
|
88
|
+
{#if !plot.hasAxisXMark && (plot.hasScaleX || x?.grid)}
|
|
89
89
|
<!-- automatic x axis -->
|
|
90
90
|
{#if plot.options.x.axis === 'bottom' || plot.options.x.axis === 'both'}
|
|
91
91
|
<AxisX anchor="bottom" automatic />
|
|
@@ -94,7 +94,7 @@ let hasLegend = $derived(color?.legend || symbol?.legend);
|
|
|
94
94
|
<AxisX anchor="top" automatic />
|
|
95
95
|
{/if}
|
|
96
96
|
{/if}
|
|
97
|
-
{#if !plot.hasAxisYMark && plot.
|
|
97
|
+
{#if !plot.hasAxisYMark && (plot.hasScaleY || y?.grid)}
|
|
98
98
|
<!-- automatic y axis -->
|
|
99
99
|
{#if plot.options.y.axis === 'left' || plot.options.y.axis === 'both'}
|
|
100
100
|
<AxisY anchor="left" automatic />
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import { CHANNEL_SCALE } from '../contants.js';
|
|
1
2
|
export class Mark {
|
|
2
3
|
id;
|
|
3
4
|
type;
|
|
4
5
|
automatic;
|
|
5
6
|
channels = $state(new Set());
|
|
6
7
|
props = $state();
|
|
8
|
+
scales = $derived(new Set(Array.from(this.channels.values())
|
|
9
|
+
.filter((channel) => this.props[channel] != null && !(typeof this.props[channel] === 'number'))
|
|
10
|
+
.map((channel) => CHANNEL_SCALE[channel])));
|
|
7
11
|
constructor(type, channels, automatic, props) {
|
|
8
12
|
this.id = Symbol();
|
|
9
13
|
this.type = type;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createScale, createColorScale } from '../helpers/createScale.js';
|
|
2
2
|
import mergeDeep from '../helpers/mergeDeep.js';
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
3
|
+
import { Scale } from './Scale.svelte.js';
|
|
4
|
+
import pick from 'underscore/modules/pick.js';
|
|
5
5
|
export const DEFAULT_PLOT_OPTIONS = {
|
|
6
6
|
title: '',
|
|
7
7
|
subtitle: '',
|
|
@@ -37,16 +37,24 @@ export class Plot {
|
|
|
37
37
|
_height = $state(400);
|
|
38
38
|
options = $state(DEFAULT_PLOT_OPTIONS);
|
|
39
39
|
marks = $state([]);
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
hasScaleX = $derived(!!this.marks.find((mark) => !mark.automatic && mark.scales.has('x')));
|
|
41
|
+
hasScaleY = $derived(!!this.marks.find((mark) => !mark.automatic && mark.scales.has('y')));
|
|
42
42
|
hasFilledDotMarks = $derived(!!this.marks.find((d) => d.type === 'dot' && d.props?.fill));
|
|
43
43
|
manualMarks = $derived(this.marks.filter((mark) => !mark.automatic));
|
|
44
|
-
|
|
44
|
+
singlePosScaleMark = $derived(this.manualMarks.length === 1 &&
|
|
45
45
|
(!this.manualMarks[0].channels.has('x') || !this.manualMarks[0].channels.has('y')));
|
|
46
|
-
height = $derived(this._height === 'auto'
|
|
46
|
+
height = $derived(this._height === 'auto'
|
|
47
|
+
? this.hasScaleY
|
|
48
|
+
? this.y.scaleType === 'band'
|
|
49
|
+
? this.y.domain.length * 30
|
|
50
|
+
: this.y.scaleType === 'point'
|
|
51
|
+
? this.y.domain.length * 18
|
|
52
|
+
: 400
|
|
53
|
+
: 90
|
|
54
|
+
: this._height);
|
|
47
55
|
inset = $derived(typeof this.options.inset === 'number'
|
|
48
56
|
? this.options.inset
|
|
49
|
-
: this.
|
|
57
|
+
: this.singlePosScaleMark
|
|
50
58
|
? 10
|
|
51
59
|
: 0);
|
|
52
60
|
// derived props
|
|
@@ -59,24 +67,28 @@ export class Plot {
|
|
|
59
67
|
// margins = $state<Margins>({ left: 0, right: 0, top: 0, bottom: 0 });
|
|
60
68
|
plotWidth = $derived(this.width - this.margins.left - this.margins.right);
|
|
61
69
|
plotHeight = $derived(this.height - this.margins.top - this.margins.bottom);
|
|
62
|
-
x = new
|
|
63
|
-
y = new
|
|
64
|
-
radius = new
|
|
65
|
-
color = new
|
|
66
|
-
symbol = new
|
|
70
|
+
x = new Scale('x', this);
|
|
71
|
+
y = new Scale('y', this);
|
|
72
|
+
radius = new Scale('radius', this);
|
|
73
|
+
color = new Scale('color', this);
|
|
74
|
+
symbol = new Scale('symbol', this);
|
|
67
75
|
colorSymbolRedundant = $derived(this.color.uniqueMarkProps.length === 1 &&
|
|
68
76
|
this.symbol.uniqueMarkProps.length === 1 &&
|
|
69
77
|
this.color.uniqueMarkProps[0] === this.symbol.uniqueMarkProps[0]);
|
|
70
|
-
xScale = $derived(createScale(this.x.scaleType
|
|
78
|
+
xScale = $derived(createScale(this.x.scaleType, this.options?.x?.domain || this.x.domain, this.options?.x?.reverse
|
|
71
79
|
? [this.margins.left + this.plotWidth - this.inset, this.margins.left + this.inset]
|
|
72
|
-
: [this.margins.left + this.inset, this.margins.left + this.plotWidth - this.inset],
|
|
73
|
-
|
|
80
|
+
: [this.margins.left + this.inset, this.margins.left + this.plotWidth - this.inset],
|
|
81
|
+
// options
|
|
82
|
+
getScaleOptions(this.x.scaleType, this.options.x)));
|
|
83
|
+
yScale = $derived(createScale(this.y.scaleType, this.options.y?.domain || this.y.domain, this.options.y?.reverse
|
|
74
84
|
? [this.margins.top + this.inset, this.height - this.margins.bottom - this.inset]
|
|
75
|
-
: [this.height - this.margins.bottom - this.inset, this.margins.top + this.inset],
|
|
85
|
+
: [this.height - this.margins.bottom - this.inset, this.margins.top + this.inset],
|
|
86
|
+
// options
|
|
87
|
+
getScaleOptions(this.y.scaleType, this.options.y)));
|
|
76
88
|
radiusScale = $derived(createScale(this.radius.scaleType, [0, Math.max(this.radius.domain[0], this.radius.domain[1])], this.options.radius.range));
|
|
77
89
|
symbolScale = $derived(createScale('ordinal', this.symbol.domain, this.options.symbol?.range || this.hasFilledDotMarks
|
|
78
90
|
? ['circle', 'cross', 'diamond', 'square', 'star', 'triangle', 'wye']
|
|
79
|
-
: ['circle', 'plus', 'times', 'triangle2', 'asterisk', 'square2', 'diamond2']));
|
|
91
|
+
: ['circle', 'plus', 'times', 'triangle2', 'asterisk', 'square2', 'diamond2'], getScaleOptions(this.symbol.scaleType, this.options.symbol || {})));
|
|
80
92
|
colorScale = $derived(createColorScale(this.color.scaleType, this.color.domain, this.options.color?.range || null, this.options.color?.scheme));
|
|
81
93
|
hasAxisXMark = $derived(!!this.marks.find((mark) => mark.type === 'axis-x' && !mark.automatic));
|
|
82
94
|
hasAxisYMark = $derived(!!this.marks.find((mark) => mark.type === 'axis-y' && !mark.automatic));
|
|
@@ -87,12 +99,27 @@ export class Plot {
|
|
|
87
99
|
this.options = opts;
|
|
88
100
|
}
|
|
89
101
|
addMark(mark) {
|
|
90
|
-
// console.log('addMark: ' + mark);
|
|
91
102
|
this.marks = [...this.marks, mark];
|
|
92
|
-
// add mark to respective channels
|
|
93
|
-
console.log('y', this.hasChannelY, this.marks.filter(m => !m.automatic && m.channels.has('y')));
|
|
94
103
|
}
|
|
95
104
|
removeMark(removeMark) {
|
|
96
105
|
this.marks = this.marks.filter((mark) => mark.id !== removeMark.id);
|
|
97
106
|
}
|
|
98
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* users can pass options to the D3 scale function
|
|
110
|
+
*/
|
|
111
|
+
function getScaleOptions(scaleType, options = {}) {
|
|
112
|
+
return scaleType === 'linear'
|
|
113
|
+
? pick(options, ['clamp', 'unknown'])
|
|
114
|
+
: scaleType === 'pow'
|
|
115
|
+
? pick(options, ['exponent'])
|
|
116
|
+
: scaleType === 'log'
|
|
117
|
+
? { base: 10, ...pick(options, ['base']) }
|
|
118
|
+
: scaleType === 'symlog'
|
|
119
|
+
? pick(options, ['constant'])
|
|
120
|
+
: scaleType === 'point'
|
|
121
|
+
? pick(options, ['padding', 'align', 'round'])
|
|
122
|
+
: scaleType === 'band'
|
|
123
|
+
? pick(options, ['padding', 'paddingInner', 'paddingOuter', 'align', 'round'])
|
|
124
|
+
: {};
|
|
125
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import resolveChannel from '../helpers/resolveChannel.js';
|
|
2
|
+
import { extent } from 'd3-array';
|
|
3
|
+
import { CHANNEL_SCALE } from '../contants.js';
|
|
4
|
+
import { isBooleanOrNull, isColorOrNull, isDateOrNull, isNumberOrNull, isStringOrNull } from '../helpers/typeChecks.js';
|
|
5
|
+
import { uniq } from 'underscore';
|
|
6
|
+
import isDataRecord from '../helpers/isDataRecord.js';
|
|
7
|
+
const FUNCTION = '(function)';
|
|
8
|
+
export class Scale {
|
|
9
|
+
name = undefined;
|
|
10
|
+
plot = $state(undefined);
|
|
11
|
+
constructor(name, plot) {
|
|
12
|
+
this.name = name;
|
|
13
|
+
this.plot = plot;
|
|
14
|
+
}
|
|
15
|
+
// readonly type: ScaleType = SCALE_TYPES.position;
|
|
16
|
+
// all marks that have this channel
|
|
17
|
+
marks = $derived(this.plot?.marks ?? []);
|
|
18
|
+
scaleOptions = $derived(this.plot && this.name !== undefined ? this.plot.options[this.name] || {} : {});
|
|
19
|
+
forceDomain = $derived(this.plot && (this.name === 'x' || this.name === 'y')
|
|
20
|
+
? this.scaleOptions?.domain || null
|
|
21
|
+
: null);
|
|
22
|
+
possibleChannels = $derived(Object.entries(CHANNEL_SCALE)
|
|
23
|
+
.filter(([, channel]) => channel === this.name)
|
|
24
|
+
.map(([prop]) => prop));
|
|
25
|
+
activeMarks = $derived(this.marks.filter((mark) => this.possibleChannels.find((channel) => mark.channels.has(channel) && mark.props[channel] !== undefined)));
|
|
26
|
+
manualActiveMarks = $derived(this.activeMarks.filter((mark) => !mark.automatic));
|
|
27
|
+
propNames = $derived(uniq(this.manualActiveMarks
|
|
28
|
+
.map((mark) => this.possibleChannels
|
|
29
|
+
.filter((channel) => mark.channels.has(channel) &&
|
|
30
|
+
(typeof mark.props[channel] === 'string' ||
|
|
31
|
+
typeof mark.props[channel] === 'function') &&
|
|
32
|
+
!String(mark.props[channel]).startsWith('__'))
|
|
33
|
+
.map((channel) => typeof mark.props[channel] === 'string' ? mark.props[channel] : FUNCTION))
|
|
34
|
+
.flat(2)));
|
|
35
|
+
autoTitle = $derived(this.propNames.length === 1 && this.propNames[0] !== FUNCTION ? this.propNames[0] : null);
|
|
36
|
+
uniqueMarkProps = $derived(uniq(this.manualActiveMarks
|
|
37
|
+
.map((mark) => this.possibleChannels
|
|
38
|
+
.filter((prop) => mark.props[prop] !== undefined)
|
|
39
|
+
.map((prop) => mark.props[prop]))
|
|
40
|
+
.flat(2)));
|
|
41
|
+
dataValues = $derived([
|
|
42
|
+
...this.activeMarks
|
|
43
|
+
// only check marks with data
|
|
44
|
+
.filter((mark) => mark.props.data.length)
|
|
45
|
+
.map((mark) => this.possibleChannels.map((prop) => mark.props.data.map((row) => resolveChannel(prop, row, mark.props))))
|
|
46
|
+
.flat(3)
|
|
47
|
+
.filter((d) => d != null),
|
|
48
|
+
...(this.forceDomain || [])
|
|
49
|
+
]);
|
|
50
|
+
isPosition = $derived(this.name === 'x' || this.name === 'y');
|
|
51
|
+
isColor = $derived(this.name === 'color');
|
|
52
|
+
scaleType = $derived(this.scaleOptions.type ||
|
|
53
|
+
inferScaleType(this.dataValues, { isPosition: this.isPosition, isColor: this.isColor }));
|
|
54
|
+
// readonly valueType = $derived(
|
|
55
|
+
// this.dataValues.every((v) => v == null)
|
|
56
|
+
// ? 'null'
|
|
57
|
+
// : this.dataValues.every(isColorOrNull)
|
|
58
|
+
// ? 'color'
|
|
59
|
+
// : this.dataValues.every(isBooleanOrNull)
|
|
60
|
+
// ? 'boolean'
|
|
61
|
+
// : this.dataValues.every(isStringOrNull)
|
|
62
|
+
// ? 'text'
|
|
63
|
+
// : this.dataValues.every(isNumberOrNull)
|
|
64
|
+
// ? 'number'
|
|
65
|
+
// : this.dataValues.every(isDateOrNull)
|
|
66
|
+
// ? 'date'
|
|
67
|
+
// : 'mixed'
|
|
68
|
+
// );
|
|
69
|
+
domain = $derived(this.scaleOptions.domain || inferScaleDomain(this.dataValues, this.scaleType));
|
|
70
|
+
}
|
|
71
|
+
// opacity: typeof === 'number' && between [0,1]
|
|
72
|
+
function inferScaleType(dataValues, { isPosition, isColor }) {
|
|
73
|
+
if (!dataValues.length)
|
|
74
|
+
return 'linear';
|
|
75
|
+
if (dataValues.every(isNumberOrNull))
|
|
76
|
+
return 'linear';
|
|
77
|
+
if (dataValues.every(isDateOrNull))
|
|
78
|
+
return 'time';
|
|
79
|
+
if (dataValues.every(isStringOrNull))
|
|
80
|
+
return 'band';
|
|
81
|
+
return 'linear';
|
|
82
|
+
}
|
|
83
|
+
function inferScaleDomain(dataValues, scaleType) {
|
|
84
|
+
if (scaleType === 'point' || scaleType === 'band') {
|
|
85
|
+
return uniq(dataValues);
|
|
86
|
+
}
|
|
87
|
+
if (scaleType === 'linear' ||
|
|
88
|
+
scaleType === 'pow' ||
|
|
89
|
+
scaleType === 'log' ||
|
|
90
|
+
scaleType === 'sqrt' ||
|
|
91
|
+
scaleType === 'sequential' ||
|
|
92
|
+
scaleType === 'time') {
|
|
93
|
+
return extent(dataValues);
|
|
94
|
+
}
|
|
95
|
+
}
|
package/dist/contants.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare const
|
|
3
|
-
export declare const
|
|
1
|
+
import type { ScaleName, ChannelName } from './types';
|
|
2
|
+
export declare const SCALE_TYPES: Record<ScaleName, symbol>;
|
|
3
|
+
export declare const CHANNEL_SCALE: Record<ChannelName, ScaleName>;
|
package/dist/contants.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const
|
|
1
|
+
export const SCALE_TYPES = {
|
|
2
2
|
opacity: Symbol('opacity'),
|
|
3
3
|
color: Symbol('color'),
|
|
4
4
|
x: Symbol('position'),
|
|
@@ -6,9 +6,10 @@ export const CHANNEL_TYPES = {
|
|
|
6
6
|
angle: Symbol('angle'),
|
|
7
7
|
symbol: Symbol('symbol'),
|
|
8
8
|
radius: Symbol('radius'),
|
|
9
|
-
width: Symbol('width')
|
|
9
|
+
width: Symbol('width'),
|
|
10
|
+
fontSize: Symbol('width')
|
|
10
11
|
};
|
|
11
|
-
export const
|
|
12
|
+
export const CHANNEL_SCALE = {
|
|
12
13
|
x: 'x',
|
|
13
14
|
x1: 'x',
|
|
14
15
|
x2: 'x',
|
|
@@ -23,18 +24,19 @@ export const MARK_PROP_CHANNEL = {
|
|
|
23
24
|
opacity: 'opacity',
|
|
24
25
|
fillOpacity: 'opacity',
|
|
25
26
|
strokeOpacity: 'opacity',
|
|
26
|
-
strokeWidth: 'width'
|
|
27
|
+
strokeWidth: 'width',
|
|
28
|
+
fontSize: 'fontSize'
|
|
27
29
|
};
|
|
28
|
-
// export const CHANNEL_MAP: Record<
|
|
29
|
-
// x:
|
|
30
|
-
// y:
|
|
31
|
-
// opacity:
|
|
32
|
-
// strokeOpacity:
|
|
33
|
-
// strokeWidth:
|
|
34
|
-
// fillOpacity:
|
|
35
|
-
// stroke:
|
|
36
|
-
// fill:
|
|
37
|
-
// r:
|
|
38
|
-
// rotate:
|
|
39
|
-
// symbol:
|
|
30
|
+
// export const CHANNEL_MAP: Record<ScaleName, ValueOf<typeof SCALE_TYPES>> = {
|
|
31
|
+
// x: SCALE_TYPES.x,
|
|
32
|
+
// y: SCALE_TYPES.y,
|
|
33
|
+
// opacity: SCALE_TYPES.opacity,
|
|
34
|
+
// strokeOpacity: SCALE_TYPES.opacity,
|
|
35
|
+
// strokeWidth: SCALE_TYPES.width,
|
|
36
|
+
// fillOpacity: SCALE_TYPES.opacity,
|
|
37
|
+
// stroke: SCALE_TYPES.color,
|
|
38
|
+
// fill: SCALE_TYPES.color,
|
|
39
|
+
// r: SCALE_TYPES.radius,
|
|
40
|
+
// rotate: SCALE_TYPES.angle,
|
|
41
|
+
// symbol: SCALE_TYPES.symbol
|
|
40
42
|
// };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export default function autoTimeFormat(x:
|
|
1
|
+
import type { Scale } from '../classes/Scale.svelte';
|
|
2
|
+
export default function autoTimeFormat(x: Scale, plotWidth: number): (date: Date) => string[];
|
|
@@ -1,32 +1,45 @@
|
|
|
1
|
-
import { scaleBand, scaleLinear, scaleTime, scaleSqrt, scaleLog, scaleOrdinal } from 'd3-scale';
|
|
1
|
+
import { scaleBand, scaleLinear, scaleTime, scaleSqrt, scaleLog, scaleOrdinal, scalePoint, scaleSymlog, scalePow } from 'd3-scale';
|
|
2
2
|
import { scaleSequential, scaleDiverging } from 'd3-scale';
|
|
3
3
|
import { getLogTicks } from './getLogTicks.js';
|
|
4
4
|
import { categoricalSchemes, isCategoricalScheme, isQuantitativeScheme, ordinalScheme, quantitativeScheme } from './colors.js';
|
|
5
5
|
import { isColorOrNull } from './typeChecks.js';
|
|
6
|
+
import callWithProps from './callWithProps.js';
|
|
7
|
+
import { count, nice } from 'd3-array';
|
|
6
8
|
const Scales = {
|
|
9
|
+
point: scalePoint,
|
|
7
10
|
band: scaleBand,
|
|
8
11
|
linear: scaleLinear,
|
|
9
12
|
time: scaleTime,
|
|
10
13
|
sqrt: scaleSqrt,
|
|
14
|
+
pow: scalePow,
|
|
11
15
|
log: scaleLog,
|
|
16
|
+
symlog: scaleSymlog,
|
|
12
17
|
ordinal: scaleOrdinal,
|
|
13
18
|
sequential: scaleSequential,
|
|
14
19
|
diverging: scaleDiverging
|
|
15
20
|
};
|
|
16
21
|
export function createScale(type, domain, range, options = {}) {
|
|
17
|
-
const scale = Scales[type](
|
|
22
|
+
const scale = Scales[type]();
|
|
23
|
+
// scale defaults
|
|
24
|
+
if (type === 'band' &&
|
|
25
|
+
options.padding === undefined &&
|
|
26
|
+
options.paddingInner === undefined &&
|
|
27
|
+
options.paddingOuter === undefined) {
|
|
28
|
+
options.padding = 0.2;
|
|
29
|
+
}
|
|
18
30
|
// allow setting arbiraty scale options
|
|
19
|
-
|
|
31
|
+
// callWithProps(scale, { domain,})
|
|
32
|
+
for (const [key, val] of Object.entries({ domain, range, ...options })) {
|
|
20
33
|
if (typeof scale[key] === 'function')
|
|
21
34
|
scale[key](val);
|
|
22
35
|
else
|
|
23
36
|
console.warn('unknown scale setter ' + key);
|
|
24
37
|
}
|
|
38
|
+
if (type === 'band' || type === 'point') {
|
|
39
|
+
scale.ticks = () => domain;
|
|
40
|
+
}
|
|
25
41
|
if (type === 'log') {
|
|
26
|
-
// overwrite scaleLog's internal ticks() method
|
|
27
42
|
scale.ticks = (count) => getLogTicks(domain, count);
|
|
28
|
-
// console.log({domain})
|
|
29
|
-
// console.log(getLogTicks(domain, 5))
|
|
30
43
|
}
|
|
31
44
|
return scale;
|
|
32
45
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { curveBasis, curveBasisClosed, curveBasisOpen, curveBundle, curveBumpX, curveBumpY, curveCardinal, curveCardinalClosed, curveCardinalOpen, curveCatmullRom, curveCatmullRomClosed, curveCatmullRomOpen, curveLinear, curveLinearClosed, curveMonotoneX, curveMonotoneY, curveNatural, curveStep, curveStepAfter, curveStepBefore } from 'd3-shape';
|
|
2
|
+
const curves = new Map([
|
|
3
|
+
['basis', curveBasis],
|
|
4
|
+
['basis-closed', curveBasisClosed],
|
|
5
|
+
['basis-open', curveBasisOpen],
|
|
6
|
+
['bundle', curveBundle],
|
|
7
|
+
['bump-x', curveBumpX],
|
|
8
|
+
['bump-y', curveBumpY],
|
|
9
|
+
['cardinal', curveCardinal],
|
|
10
|
+
['cardinal-closed', curveCardinalClosed],
|
|
11
|
+
['cardinal-open', curveCardinalOpen],
|
|
12
|
+
['catmull-rom', curveCatmullRom],
|
|
13
|
+
['catmull-rom-closed', curveCatmullRomClosed],
|
|
14
|
+
['catmull-rom-open', curveCatmullRomOpen],
|
|
15
|
+
['linear', curveLinear],
|
|
16
|
+
['linear-closed', curveLinearClosed],
|
|
17
|
+
['monotone-x', curveMonotoneX],
|
|
18
|
+
['monotone-y', curveMonotoneY],
|
|
19
|
+
['natural', curveNatural],
|
|
20
|
+
['step', curveStep],
|
|
21
|
+
['step-after', curveStepAfter],
|
|
22
|
+
['step-before', curveStepBefore]
|
|
23
|
+
]);
|
|
24
|
+
export function maybeCurve(curve = curveLinear, tension) {
|
|
25
|
+
if (typeof curve === 'function')
|
|
26
|
+
return curve; // custom curve
|
|
27
|
+
const c = curves.get(`${curve}`.toLowerCase());
|
|
28
|
+
if (!c)
|
|
29
|
+
throw new Error(`unknown curve: ${curve}`);
|
|
30
|
+
if (tension !== undefined) {
|
|
31
|
+
if ('beta' in c) {
|
|
32
|
+
return c.beta(tension);
|
|
33
|
+
}
|
|
34
|
+
else if ('tension' in c) {
|
|
35
|
+
return c.tension(tension);
|
|
36
|
+
}
|
|
37
|
+
else if ('alpha' in c) {
|
|
38
|
+
return c.alpha(tension);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return c;
|
|
42
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CHANNEL_SCALE } from '../contants.js';
|
|
2
2
|
import resolveChannel from './resolveChannel.js';
|
|
3
3
|
const styleProps = {
|
|
4
4
|
fill: 'fill',
|
|
@@ -13,6 +13,6 @@ const styleProps = {
|
|
|
13
13
|
export default function (datum, props) {
|
|
14
14
|
return Object.entries(styleProps)
|
|
15
15
|
.filter(([key, cssKey]) => cssKey && props[key] != null)
|
|
16
|
-
.map(([key, cssKey]) => `${cssKey}: ${resolveChannel(
|
|
16
|
+
.map(([key, cssKey]) => `${cssKey}: ${resolveChannel(key, datum, props)}`)
|
|
17
17
|
.join(';');
|
|
18
18
|
}
|
|
@@ -10,7 +10,7 @@ export function getLogTicks(domain, count = 6) {
|
|
|
10
10
|
return [];
|
|
11
11
|
if (domain[0] === 0)
|
|
12
12
|
return ticksArray(domain[0], domain[1], count - 2);
|
|
13
|
-
let mult = Math.pow(10, Math.floor(Math.log10(Math.abs(domain[1] - domain[0]))) - 1);
|
|
13
|
+
let mult = 1; //Math.pow(10, Math.floor(Math.log10(Math.abs(domain[1] - domain[0]))) - 1);
|
|
14
14
|
count += 2;
|
|
15
15
|
let candidates = getTickCandidates(domain, mult);
|
|
16
16
|
if (candidates[0].num > count) {
|
|
@@ -46,12 +46,12 @@ function getTickCandidates(domain, mult = 1) {
|
|
|
46
46
|
return logSeries.map((factors) => {
|
|
47
47
|
let i = Math.pow(10, Math.floor(Math.log10(domain[0])));
|
|
48
48
|
let f = 0;
|
|
49
|
-
const
|
|
50
|
-
while (i < domain[1] &&
|
|
49
|
+
const ticks = [i];
|
|
50
|
+
while (i < domain[1] && ticks.length < 50) {
|
|
51
51
|
i *= factors[f] * mult;
|
|
52
|
-
|
|
52
|
+
ticks.push(i);
|
|
53
53
|
f = (f + 1) % factors.length;
|
|
54
54
|
}
|
|
55
|
-
return { ticks
|
|
55
|
+
return { ticks, num: ticks.length };
|
|
56
56
|
});
|
|
57
57
|
}
|
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
import type { ChannelName, ChannelAccessor, DataRow, RawValue } from '../types';
|
|
2
|
-
|
|
1
|
+
import type { ChannelName, ChannelAccessor, DataRow, RawValue } from '../types.js';
|
|
2
|
+
type ChannelAlias = {
|
|
3
|
+
channel: ChannelName;
|
|
4
|
+
};
|
|
5
|
+
export default function (channel: ChannelName, datum: DataRow, channels: Partial<Record<ChannelName, ChannelAccessor | ChannelAlias>>): RawValue;
|
|
6
|
+
export {};
|
|
@@ -1,5 +1,14 @@
|
|
|
1
|
+
import { CHANNEL_SCALE } from '../contants.js';
|
|
1
2
|
import isDataRecord from './isDataRecord.js';
|
|
2
|
-
|
|
3
|
+
import isRawValue from './isRawValue.js';
|
|
4
|
+
export default function (channel, datum, channels) {
|
|
5
|
+
const scale = CHANNEL_SCALE[channel];
|
|
6
|
+
const maybeAccessor = channel === 'z' ? channels.z || channels.fill || channels.stroke : channels[channel];
|
|
7
|
+
const accessor = isDataRecord(maybeAccessor) && maybeAccessor?.channel
|
|
8
|
+
? channels[maybeAccessor?.channel]
|
|
9
|
+
: maybeAccessor;
|
|
10
|
+
if (isDataRecord(accessor) && accessor.channel)
|
|
11
|
+
throw new Error('multiple channel aliases are not allowed');
|
|
3
12
|
if ((channel === 'x' || channel === 'y') && Array.isArray(datum) && accessor === null) {
|
|
4
13
|
// special case for [[x0,y0], [x1,y1], ...] format
|
|
5
14
|
return datum[channel === 'x' ? 0 : 1];
|
|
@@ -14,15 +23,16 @@ export default function (channel, datum, accessor = null) {
|
|
|
14
23
|
// fallback to channel name as accessor
|
|
15
24
|
if (accessor === null && datum[channel] !== undefined)
|
|
16
25
|
return datum[channel];
|
|
17
|
-
|
|
18
|
-
return accessor;
|
|
26
|
+
return isRawValue(accessor) ? accessor : null;
|
|
19
27
|
}
|
|
20
28
|
else {
|
|
21
29
|
// return single value or accessor
|
|
22
30
|
return typeof accessor === 'function'
|
|
23
31
|
? accessor(datum)
|
|
24
|
-
: accessor !== null
|
|
32
|
+
: accessor !== null && isRawValue(accessor)
|
|
25
33
|
? accessor
|
|
26
|
-
: datum
|
|
34
|
+
: !Array.isArray(datum) && (scale === 'x' || scale === 'y')
|
|
35
|
+
? datum
|
|
36
|
+
: null;
|
|
27
37
|
}
|
|
28
38
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
export { default as AxisX } from './marks/AxisX.svelte';
|
|
2
|
-
export { default as AxisY } from './marks/AxisY.svelte';
|
|
3
|
-
export { default as ColorLegend } from './marks/ColorLegend.svelte';
|
|
4
1
|
export { default as Area } from './marks/Area.svelte';
|
|
5
2
|
export { default as AreaX } from './marks/AreaX.svelte';
|
|
6
3
|
export { default as AreaY } from './marks/AreaY.svelte';
|
|
4
|
+
export { default as AxisX } from './marks/AxisX.svelte';
|
|
5
|
+
export { default as AxisY } from './marks/AxisY.svelte';
|
|
6
|
+
export { default as BarX } from './marks/BarX.svelte';
|
|
7
|
+
export { default as BarY } from './marks/BarY.svelte';
|
|
8
|
+
export { default as ColorLegend } from './marks/ColorLegend.svelte';
|
|
7
9
|
export { default as Dot } from './marks/Dot.svelte';
|
|
8
10
|
export { default as DotX } from './marks/DotX.svelte';
|
|
9
11
|
export { default as DotY } from './marks/DotY.svelte';
|
|
@@ -17,3 +19,8 @@ export { default as Plot } from './Plot.svelte';
|
|
|
17
19
|
export { default as RuleX } from './marks/RuleX.svelte';
|
|
18
20
|
export { default as RuleY } from './marks/RuleY.svelte';
|
|
19
21
|
export { default as SymbolLegend } from './marks/SymbolLegend.svelte';
|
|
22
|
+
export { default as TickX } from './marks/TickX.svelte';
|
|
23
|
+
export { default as TickY } from './marks/TickY.svelte';
|
|
24
|
+
export { stackX, stackY } from './transforms/stack.js';
|
|
25
|
+
export { recordizeX, recordizeY } from './transforms/recordize.js';
|
|
26
|
+
export { renameChannels } from './transforms/rename.js';
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
// Reexport your entry components here
|
|
2
|
-
export { default as AxisX } from './marks/AxisX.svelte';
|
|
3
|
-
export { default as AxisY } from './marks/AxisY.svelte';
|
|
4
|
-
export { default as ColorLegend } from './marks/ColorLegend.svelte';
|
|
5
2
|
export { default as Area } from './marks/Area.svelte';
|
|
6
3
|
export { default as AreaX } from './marks/AreaX.svelte';
|
|
7
4
|
export { default as AreaY } from './marks/AreaY.svelte';
|
|
5
|
+
export { default as AxisX } from './marks/AxisX.svelte';
|
|
6
|
+
export { default as AxisY } from './marks/AxisY.svelte';
|
|
7
|
+
export { default as BarX } from './marks/BarX.svelte';
|
|
8
|
+
export { default as BarY } from './marks/BarY.svelte';
|
|
9
|
+
export { default as ColorLegend } from './marks/ColorLegend.svelte';
|
|
8
10
|
export { default as Dot } from './marks/Dot.svelte';
|
|
9
11
|
export { default as DotX } from './marks/DotX.svelte';
|
|
10
12
|
export { default as DotY } from './marks/DotY.svelte';
|
|
@@ -18,3 +20,9 @@ export { default as Plot } from './Plot.svelte';
|
|
|
18
20
|
export { default as RuleX } from './marks/RuleX.svelte';
|
|
19
21
|
export { default as RuleY } from './marks/RuleY.svelte';
|
|
20
22
|
export { default as SymbolLegend } from './marks/SymbolLegend.svelte';
|
|
23
|
+
export { default as TickX } from './marks/TickX.svelte';
|
|
24
|
+
export { default as TickY } from './marks/TickY.svelte';
|
|
25
|
+
// transforms
|
|
26
|
+
export { stackX, stackY } from './transforms/stack.js';
|
|
27
|
+
export { recordizeX, recordizeY } from './transforms/recordize.js';
|
|
28
|
+
export { renameChannels } from './transforms/rename.js';
|