svelteplot 0.2.1 → 0.2.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/helpers/resolve.d.ts +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +4 -2
- package/dist/marks/BarX.svelte +11 -37
- package/dist/marks/BarY.svelte +27 -58
- package/dist/marks/BarY.svelte.d.ts +2 -8
- package/dist/marks/Cell.svelte +12 -36
- package/dist/marks/Line.svelte +98 -80
- package/dist/marks/Line.svelte.d.ts +5 -3
- package/dist/marks/Rect.svelte +10 -24
- package/dist/marks/helpers/DotCanvas.svelte +3 -13
- package/dist/marks/helpers/LineCanvas.svelte +116 -0
- package/dist/marks/helpers/LineCanvas.svelte.d.ts +12 -0
- package/dist/marks/helpers/LinearGradientX.svelte +27 -0
- package/dist/marks/helpers/LinearGradientX.svelte.d.ts +11 -0
- package/dist/marks/helpers/LinearGradientY.svelte +27 -0
- package/dist/marks/helpers/LinearGradientY.svelte.d.ts +11 -0
- package/dist/marks/helpers/RectPath.svelte +129 -0
- package/dist/marks/helpers/RectPath.svelte.d.ts +27 -0
- package/dist/marks/helpers/canvas.d.ts +1 -0
- package/dist/marks/helpers/canvas.js +34 -0
- package/dist/types.d.ts +7 -2
- package/package.json +2 -1
|
@@ -3,7 +3,7 @@ import type { ScaleName, ChannelName, ScaledChannelName, ChannelAccessor, DataRo
|
|
|
3
3
|
type ChannelAlias = {
|
|
4
4
|
channel: ScaledChannelName;
|
|
5
5
|
};
|
|
6
|
-
export declare function resolveProp<T>(accessor: ConstantAccessor<T>, datum: DataRecord | null, _defaultValue?: T | null): T |
|
|
6
|
+
export declare function resolveProp<T>(accessor: ConstantAccessor<T>, datum: DataRecord | null, _defaultValue?: T | null): T | typeof _defaultValue;
|
|
7
7
|
type ChannelOptions = {
|
|
8
8
|
value: ChannelAccessor;
|
|
9
9
|
scale?: ScaleName | null;
|
package/dist/index.d.ts
CHANGED
|
@@ -58,10 +58,12 @@ export { normalizeX, normalizeY } from './transforms/normalize.js';
|
|
|
58
58
|
export { group, groupX, groupY, groupZ } from './transforms/group.js';
|
|
59
59
|
export { intervalX, intervalY } from './transforms/interval.js';
|
|
60
60
|
export { recordizeX, recordizeY } from './transforms/recordize.js';
|
|
61
|
-
export { renameChannels } from './transforms/rename.js';
|
|
61
|
+
export { renameChannels, replaceChannels } from './transforms/rename.js';
|
|
62
62
|
export { select, selectFirst, selectLast, selectMaxX, selectMaxY, selectMinX, selectMinY } from './transforms/select.js';
|
|
63
63
|
export { shiftX, shiftY } from './transforms/shift.js';
|
|
64
64
|
export { sort, shuffle, reverse } from './transforms/sort.js';
|
|
65
65
|
export { stackX, stackY } from './transforms/stack.js';
|
|
66
66
|
export { windowX, windowY } from './transforms/window.js';
|
|
67
67
|
export { formatMonth } from './helpers/formats.js';
|
|
68
|
+
export { default as LinearGradientX } from './marks/helpers/LinearGradientX.svelte';
|
|
69
|
+
export { default as LinearGradientY } from './marks/helpers/LinearGradientY.svelte';
|
package/dist/index.js
CHANGED
|
@@ -61,11 +61,13 @@ export { normalizeX, normalizeY } from './transforms/normalize.js';
|
|
|
61
61
|
export { group, groupX, groupY, groupZ } from './transforms/group.js';
|
|
62
62
|
export { intervalX, intervalY } from './transforms/interval.js';
|
|
63
63
|
export { recordizeX, recordizeY } from './transforms/recordize.js';
|
|
64
|
-
export { renameChannels } from './transforms/rename.js';
|
|
64
|
+
export { renameChannels, replaceChannels } from './transforms/rename.js';
|
|
65
65
|
export { select, selectFirst, selectLast, selectMaxX, selectMaxY, selectMinX, selectMinY } from './transforms/select.js';
|
|
66
66
|
export { shiftX, shiftY } from './transforms/shift.js';
|
|
67
67
|
export { sort, shuffle, reverse } from './transforms/sort.js';
|
|
68
68
|
export { stackX, stackY } from './transforms/stack.js';
|
|
69
69
|
export { windowX, windowY } from './transforms/window.js';
|
|
70
|
-
//
|
|
70
|
+
// helpers
|
|
71
71
|
export { formatMonth } from './helpers/formats.js';
|
|
72
|
+
export { default as LinearGradientX } from './marks/helpers/LinearGradientX.svelte';
|
|
73
|
+
export { default as LinearGradientY } from './marks/helpers/LinearGradientY.svelte';
|
package/dist/marks/BarX.svelte
CHANGED
|
@@ -6,13 +6,11 @@
|
|
|
6
6
|
import Mark from '../Mark.svelte';
|
|
7
7
|
import { getContext } from 'svelte';
|
|
8
8
|
import { stackX, recordizeX, intervalX, sort } from '../index.js';
|
|
9
|
-
import { resolveProp, resolveStyles } from '../helpers/resolve.js';
|
|
10
|
-
import { roundedRect } from '../helpers/roundedRect.js';
|
|
11
9
|
import type { PlotContext, BaseMarkProps, RectMarkProps, ChannelAccessor } from '../types.js';
|
|
12
10
|
import type { StackOptions } from '../transforms/stack.js';
|
|
13
11
|
import type { DataRow } from '../types.js';
|
|
14
|
-
import { addEventHandlers } from './helpers/events.js';
|
|
15
12
|
import GroupMultiple from './helpers/GroupMultiple.svelte';
|
|
13
|
+
import RectPath from './helpers/RectPath.svelte';
|
|
16
14
|
|
|
17
15
|
type BarXProps = BaseMarkProps & {
|
|
18
16
|
data: DataRow[];
|
|
@@ -21,14 +19,6 @@
|
|
|
21
19
|
x2?: ChannelAccessor;
|
|
22
20
|
y?: ChannelAccessor;
|
|
23
21
|
stack?: StackOptions;
|
|
24
|
-
borderRadius?:
|
|
25
|
-
| number
|
|
26
|
-
| {
|
|
27
|
-
topLeft?: number;
|
|
28
|
-
topRight?: number;
|
|
29
|
-
bottomRight?: number;
|
|
30
|
-
bottomLeft?: number;
|
|
31
|
-
};
|
|
32
22
|
} & RectMarkProps;
|
|
33
23
|
|
|
34
24
|
let { data = [{}], class: className = null, stack, ...options }: BarXProps = $props();
|
|
@@ -59,33 +49,17 @@
|
|
|
59
49
|
{@const bw = plot.scales.y.fn.bandwidth()}
|
|
60
50
|
{@const minx = Math.min(d.x1, d.x2)}
|
|
61
51
|
{@const maxx = Math.max(d.x1, d.x2)}
|
|
62
|
-
{@const insetLeft = resolveProp(args.insetLeft, d.datum, 0)}
|
|
63
|
-
{@const insetRight = resolveProp(args.insetRight, d.datum, 0)}
|
|
64
|
-
{@const insetTop = resolveProp(args.insetTop || args.inset, d.datum, 0)}
|
|
65
|
-
{@const insetBottom = resolveProp(args.insetBottom || args.inset, d.datum, 0)}
|
|
66
|
-
{@const dx = resolveProp(args.dx, d.datum, 0)}
|
|
67
|
-
{@const dy = resolveProp(args.dy, d.datum, 0)}
|
|
68
52
|
{#if d.valid}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
{style}
|
|
80
|
-
transform="translate({[
|
|
81
|
-
minx + dx + insetLeft,
|
|
82
|
-
d.y + insetTop + dy - bw * 0.5
|
|
83
|
-
]})"
|
|
84
|
-
use:addEventHandlers={{
|
|
85
|
-
getPlotState,
|
|
86
|
-
options: args,
|
|
87
|
-
datum: d.datum
|
|
88
|
-
}} />
|
|
53
|
+
<RectPath
|
|
54
|
+
{usedScales}
|
|
55
|
+
class={className}
|
|
56
|
+
{options}
|
|
57
|
+
datum={d}
|
|
58
|
+
x={minx}
|
|
59
|
+
useInsetAsFallbackHorizontally={false}
|
|
60
|
+
y={d.y - bw * 0.5}
|
|
61
|
+
width={maxx - minx}
|
|
62
|
+
height={bw} />
|
|
89
63
|
{/if}
|
|
90
64
|
{/each}
|
|
91
65
|
</GroupMultiple>
|
package/dist/marks/BarY.svelte
CHANGED
|
@@ -6,40 +6,25 @@
|
|
|
6
6
|
import Mark from '../Mark.svelte';
|
|
7
7
|
import { getContext } from 'svelte';
|
|
8
8
|
import { intervalY, stackY, recordizeY, sort } from '../index.js';
|
|
9
|
-
import {
|
|
10
|
-
import { roundedRect } from '../helpers/roundedRect.js';
|
|
11
|
-
import {
|
|
12
|
-
type PlotContext,
|
|
13
|
-
type BaseMarkProps,
|
|
14
|
-
type ChannelAccessor,
|
|
15
|
-
type DataRow
|
|
16
|
-
} from '../types.js';
|
|
9
|
+
import type { PlotContext, BaseMarkProps, BaseRectMarkProps } from '../types.js';
|
|
17
10
|
import type { StackOptions } from '../transforms/stack.js';
|
|
18
|
-
import { maybeData } from '../helpers/index.js';
|
|
19
|
-
import { addEventHandlers } from './helpers/events.js';
|
|
20
11
|
import GroupMultiple from './helpers/GroupMultiple.svelte';
|
|
12
|
+
import RectPath from './helpers/RectPath.svelte';
|
|
21
13
|
|
|
22
|
-
type BarYProps = BaseMarkProps &
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
| {
|
|
37
|
-
topLeft?: number;
|
|
38
|
-
topRight?: number;
|
|
39
|
-
bottomRight?: number;
|
|
40
|
-
bottomLeft?: number;
|
|
41
|
-
};
|
|
42
|
-
};
|
|
14
|
+
type BarYProps = BaseMarkProps &
|
|
15
|
+
BaseRectMarkProps & {
|
|
16
|
+
data: DataRow[];
|
|
17
|
+
x?: ChannelAccessor;
|
|
18
|
+
y?: ChannelAccessor;
|
|
19
|
+
y1?: ChannelAccessor;
|
|
20
|
+
y2?: ChannelAccessor;
|
|
21
|
+
stack?: StackOptions;
|
|
22
|
+
/**
|
|
23
|
+
* Converts y into y1/y2 ranges based on the provided interval. Disables the
|
|
24
|
+
* implicit stacking
|
|
25
|
+
*/
|
|
26
|
+
interval?: number | string;
|
|
27
|
+
};
|
|
43
28
|
|
|
44
29
|
let { data = [{}], class: className = null, stack, ...options }: BarYProps = $props();
|
|
45
30
|
|
|
@@ -63,39 +48,23 @@
|
|
|
63
48
|
requiredScales={{ x: ['band'] }}
|
|
64
49
|
channels={['x', 'y1', 'y2', 'fill', 'stroke', 'opacity', 'fillOpacity', 'strokeOpacity']}
|
|
65
50
|
{...args}>
|
|
66
|
-
{#snippet children({
|
|
51
|
+
{#snippet children({ scaledData, usedScales })}
|
|
67
52
|
<GroupMultiple class="bar-y" length={scaledData.length}>
|
|
68
53
|
{#each scaledData as d}
|
|
69
54
|
{@const bw = plot.scales.x.fn.bandwidth()}
|
|
70
55
|
{@const miny = Math.min(d.y1, d.y2)}
|
|
71
56
|
{@const maxy = Math.max(d.y1, d.y2)}
|
|
72
|
-
{@const insetLeft = resolveProp(args.insetLeft || args.inset, d.datum, 0)}
|
|
73
|
-
{@const insetRight = resolveProp(args.insetRight || args.inset, d.datum, 0)}
|
|
74
|
-
{@const insetTop = resolveProp(args.insetTop, d.datum, 0)}
|
|
75
|
-
{@const insetBottom = resolveProp(args.insetBottom, d.datum, 0)}
|
|
76
|
-
{@const dx = resolveProp(args.dx, d.datum, 0)}
|
|
77
|
-
{@const dy = resolveProp(args.dy, d.datum, 0)}
|
|
78
57
|
{#if d.valid}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
{style}
|
|
90
|
-
transform="translate({[
|
|
91
|
-
d.x + insetLeft + dx - bw * 0.5,
|
|
92
|
-
miny + dy + insetTop
|
|
93
|
-
]})"
|
|
94
|
-
use:addEventHandlers={{
|
|
95
|
-
getPlotState,
|
|
96
|
-
options: args,
|
|
97
|
-
datum: d.datum
|
|
98
|
-
}} />
|
|
58
|
+
<RectPath
|
|
59
|
+
x={d.x - bw * 0.5}
|
|
60
|
+
y={miny}
|
|
61
|
+
options={args}
|
|
62
|
+
class={className}
|
|
63
|
+
width={bw}
|
|
64
|
+
height={maxy - miny}
|
|
65
|
+
datum={d}
|
|
66
|
+
{usedScales}
|
|
67
|
+
useInsetAsFallbackVertically={false} />
|
|
99
68
|
{/if}
|
|
100
69
|
{/each}
|
|
101
70
|
</GroupMultiple>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { BaseMarkProps, BaseRectMarkProps } from '../types.js';
|
|
2
2
|
import type { StackOptions } from '../transforms/stack.js';
|
|
3
|
-
type BarYProps = BaseMarkProps & {
|
|
3
|
+
type BarYProps = BaseMarkProps & BaseRectMarkProps & {
|
|
4
4
|
data: DataRow[];
|
|
5
5
|
x?: ChannelAccessor;
|
|
6
6
|
y?: ChannelAccessor;
|
|
@@ -12,12 +12,6 @@ type BarYProps = BaseMarkProps & {
|
|
|
12
12
|
* implicit stacking
|
|
13
13
|
*/
|
|
14
14
|
interval?: number | string;
|
|
15
|
-
borderRadius?: number | {
|
|
16
|
-
topLeft?: number;
|
|
17
|
-
topRight?: number;
|
|
18
|
-
bottomRight?: number;
|
|
19
|
-
bottomLeft?: number;
|
|
20
|
-
};
|
|
21
15
|
};
|
|
22
16
|
/** For vertical column charts using a band scale as x axis */
|
|
23
17
|
declare const BarY: import("svelte").Component<BarYProps, {}, "">;
|
package/dist/marks/Cell.svelte
CHANGED
|
@@ -6,9 +6,7 @@
|
|
|
6
6
|
import Mark from '../Mark.svelte';
|
|
7
7
|
import { getContext } from 'svelte';
|
|
8
8
|
import { recordizeY, sort } from '../index.js';
|
|
9
|
-
import {
|
|
10
|
-
import { resolveChannel, resolveProp, resolveStyles } from '../helpers/resolve.js';
|
|
11
|
-
import { coalesce, maybeNumber } from '../helpers/index.js';
|
|
9
|
+
import { resolveChannel } from '../helpers/resolve.js';
|
|
12
10
|
import type {
|
|
13
11
|
PlotContext,
|
|
14
12
|
DataRecord,
|
|
@@ -17,7 +15,7 @@
|
|
|
17
15
|
ChannelAccessor
|
|
18
16
|
} from '../types.js';
|
|
19
17
|
import { isValid } from '../helpers/isValid.js';
|
|
20
|
-
import
|
|
18
|
+
import RectPath from './helpers/RectPath.svelte';
|
|
21
19
|
|
|
22
20
|
type CellProps = BaseMarkProps & {
|
|
23
21
|
data: DataRecord[];
|
|
@@ -59,43 +57,21 @@
|
|
|
59
57
|
requiredScales={{ x: ['band'], y: ['band'] }}
|
|
60
58
|
channels={['x', 'y', 'fill', 'stroke', 'opacity', 'fillOpacity', 'strokeOpacity']}
|
|
61
59
|
{...args}>
|
|
62
|
-
{#snippet children({
|
|
60
|
+
{#snippet children({ scaledData, usedScales })}
|
|
63
61
|
{@const bwx = plot.scales.x.fn.bandwidth()}
|
|
64
62
|
{@const bwy = plot.scales.y.fn.bandwidth()}
|
|
65
63
|
<g class="cell {className || ''}" data-fill={usedScales.fillOpacity}>
|
|
66
64
|
{#each scaledData as d}
|
|
67
|
-
{@const inset = resolveProp(args.inset, d.datum, 0)}
|
|
68
|
-
{@const insetLeft = resolveProp(args.insetLeft, d.datum)}
|
|
69
|
-
{@const insetRight = resolveProp(args.insetRight, d.datum)}
|
|
70
|
-
{@const insetTop = resolveProp(args.insetTop, d.datum)}
|
|
71
|
-
{@const insetBottom = resolveProp(args.insetBottom, d.datum)}
|
|
72
|
-
{@const dx = resolveProp(args.dx, d.datum, 0)}
|
|
73
|
-
{@const dy = resolveProp(args.dy, d.datum, 0)}
|
|
74
|
-
{@const insetL = maybeNumber(coalesce(insetLeft, inset, 0))}
|
|
75
|
-
{@const insetT = maybeNumber(coalesce(insetTop, inset, 0))}
|
|
76
|
-
{@const insetR = maybeNumber(coalesce(insetRight, inset, 0))}
|
|
77
|
-
{@const insetB = maybeNumber(coalesce(insetBottom, inset, 0))}
|
|
78
65
|
{#if d.valid && (args.fill == null || isValid(resolveChannel('fill', d.datum, args)))}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
class={[styleClass]}
|
|
89
|
-
{style}
|
|
90
|
-
transform="translate({[
|
|
91
|
-
d.x + insetL + dx - bwx * 0.5,
|
|
92
|
-
d.y + insetT + dy - bwy * 0.5
|
|
93
|
-
]})"
|
|
94
|
-
use:addEventHandlers={{
|
|
95
|
-
getPlotState,
|
|
96
|
-
options: args,
|
|
97
|
-
datum: d.datum
|
|
98
|
-
}} />
|
|
66
|
+
<RectPath
|
|
67
|
+
datum={d}
|
|
68
|
+
class={className}
|
|
69
|
+
{usedScales}
|
|
70
|
+
options={args}
|
|
71
|
+
x={d.x - bwx * 0.5}
|
|
72
|
+
y={d.y - bwy * 0.5}
|
|
73
|
+
width={bwx}
|
|
74
|
+
height={bwy} />
|
|
99
75
|
{/if}
|
|
100
76
|
{/each}
|
|
101
77
|
</g>
|
package/dist/marks/Line.svelte
CHANGED
|
@@ -18,8 +18,9 @@
|
|
|
18
18
|
data: DataRecord[];
|
|
19
19
|
z?: ChannelAccessor;
|
|
20
20
|
stroke?: ChannelAccessor;
|
|
21
|
-
outlineStroke?:
|
|
22
|
-
outlineStrokeWidth?:
|
|
21
|
+
outlineStroke?: string;
|
|
22
|
+
outlineStrokeWidth?: number;
|
|
23
|
+
outlineStrokeOpacity?: number;
|
|
23
24
|
dx?: ConstantAccessor<number>;
|
|
24
25
|
dy?: ConstantAccessor<number>;
|
|
25
26
|
curve?: CurveName | CurveFactory;
|
|
@@ -31,6 +32,7 @@
|
|
|
31
32
|
textStartOffset?: ConstantAccessor<string>;
|
|
32
33
|
textStrokeWidth?: ConstantAccessor<number>;
|
|
33
34
|
lineClass?: ConstantAccessor<string>;
|
|
35
|
+
canvas?: boolean;
|
|
34
36
|
} & MarkerOptions;
|
|
35
37
|
</script>
|
|
36
38
|
|
|
@@ -39,11 +41,12 @@
|
|
|
39
41
|
import MarkerPath from './helpers/MarkerPath.svelte';
|
|
40
42
|
import { getContext } from 'svelte';
|
|
41
43
|
import { resolveProp, resolveStyles } from '../helpers/resolve.js';
|
|
42
|
-
import { line, type CurveFactory } from 'd3-shape';
|
|
44
|
+
import { line, type CurveFactory, type Line } from 'd3-shape';
|
|
43
45
|
import { geoPath } from 'd3-geo';
|
|
44
46
|
import callWithProps from '../helpers/callWithProps.js';
|
|
45
47
|
import { maybeCurve } from '../helpers/curves.js';
|
|
46
48
|
import { pick } from 'es-toolkit';
|
|
49
|
+
import LineCanvas from './helpers/LineCanvas.svelte';
|
|
47
50
|
|
|
48
51
|
type LineMarkProps = BaseMarkProps & {
|
|
49
52
|
x?: ChannelAccessor;
|
|
@@ -61,6 +64,7 @@
|
|
|
61
64
|
curve = 'auto',
|
|
62
65
|
tension = 0,
|
|
63
66
|
text,
|
|
67
|
+
canvas = false,
|
|
64
68
|
class: className = null,
|
|
65
69
|
lineClass = null,
|
|
66
70
|
...options
|
|
@@ -96,9 +100,7 @@
|
|
|
96
100
|
const { getPlotState } = getContext<PlotContext>('svelteplot');
|
|
97
101
|
const plot = $derived(getPlotState());
|
|
98
102
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const linePath: LinePath = $derived(
|
|
103
|
+
const linePath: Line<ScaledDataRecord> = $derived(
|
|
102
104
|
plot.scales.projection && curve === 'auto'
|
|
103
105
|
? sphereLine(plot.scales.projection)
|
|
104
106
|
: callWithProps(line, [], {
|
|
@@ -111,7 +113,7 @@
|
|
|
111
113
|
|
|
112
114
|
function sphereLine(projection) {
|
|
113
115
|
const path = geoPath(projection);
|
|
114
|
-
|
|
116
|
+
const fn = (lineData: ScaledDataRecord[]) => {
|
|
115
117
|
let line = [];
|
|
116
118
|
const lines = [line];
|
|
117
119
|
for (const { x, y } of lineData) {
|
|
@@ -125,6 +127,8 @@
|
|
|
125
127
|
}
|
|
126
128
|
return path({ type: 'MultiLineString', coordinates: lines });
|
|
127
129
|
};
|
|
130
|
+
fn.context = path.context;
|
|
131
|
+
return fn;
|
|
128
132
|
}
|
|
129
133
|
</script>
|
|
130
134
|
|
|
@@ -135,88 +139,102 @@
|
|
|
135
139
|
{...args}>
|
|
136
140
|
{#snippet children({ mark, usedScales, scaledData })}
|
|
137
141
|
{#if scaledData.length > 0}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
142
|
+
{@const groupedLineData = groupIndex(scaledData, groupByKey)};
|
|
143
|
+
{#if canvas}
|
|
144
|
+
<LineCanvas {groupedLineData} {mark} {usedScales} {linePath} {groupByKey} />
|
|
145
|
+
{:else}
|
|
146
|
+
<g class={['lines', className]}>
|
|
147
|
+
{#each groupedLineData as lineData, i}
|
|
148
|
+
{@const pathString = linePath(lineData)}
|
|
149
|
+
{#if pathString}
|
|
150
|
+
<GroupMultiple class={resolveProp(lineClass, lineData[0])}>
|
|
151
|
+
{#if options.outlineStroke}
|
|
152
|
+
{@const [outlineStyle, outlineStyleClass] = resolveStyles(
|
|
153
|
+
plot,
|
|
154
|
+
{ ...lineData[0], stroke: options.outlineStroke },
|
|
155
|
+
{
|
|
156
|
+
strokeLinejoin: 'round',
|
|
157
|
+
...args,
|
|
158
|
+
stroke: options.outlineStroke,
|
|
159
|
+
strokeOpacity: options.outlineStrokeOpacity ?? 1,
|
|
160
|
+
strokeWidth:
|
|
161
|
+
options.outlineStrokeWidth ||
|
|
162
|
+
resolveProp(
|
|
163
|
+
options.strokeWidth,
|
|
164
|
+
lineData[0].datum,
|
|
165
|
+
1.4
|
|
166
|
+
) + 2
|
|
167
|
+
},
|
|
168
|
+
'stroke',
|
|
169
|
+
usedScales
|
|
170
|
+
)}
|
|
171
|
+
<path
|
|
172
|
+
d={pathString}
|
|
173
|
+
style={outlineStyle}
|
|
174
|
+
class={['is-outline', outlineStyleClass]} />
|
|
175
|
+
{/if}
|
|
176
|
+
{@const [style, styleClass] = resolveStyles(
|
|
145
177
|
plot,
|
|
146
|
-
|
|
178
|
+
lineData[0],
|
|
147
179
|
{
|
|
180
|
+
strokeWidth: 1.4,
|
|
148
181
|
strokeLinejoin: 'round',
|
|
149
182
|
...args,
|
|
150
|
-
stroke:
|
|
151
|
-
strokeWidth:
|
|
152
|
-
options.outlineStrokeWidth ||
|
|
153
|
-
(+options.strokeWidth || 1.4) + 2
|
|
183
|
+
stroke: lineData[0].stroke
|
|
154
184
|
},
|
|
155
185
|
'stroke',
|
|
156
186
|
usedScales
|
|
157
187
|
)}
|
|
158
|
-
|
|
188
|
+
{@const [textStyle, textStyleClass] = resolveStyles(
|
|
189
|
+
plot,
|
|
190
|
+
lineData[0],
|
|
191
|
+
{
|
|
192
|
+
textAnchor: 'middle',
|
|
193
|
+
...pick(args, [
|
|
194
|
+
'fontSize',
|
|
195
|
+
'fontWeight',
|
|
196
|
+
'fontStyle',
|
|
197
|
+
'textAnchor'
|
|
198
|
+
]),
|
|
199
|
+
strokeWidth: args.textStrokeWidth
|
|
200
|
+
? args.textStrokeWidth
|
|
201
|
+
: args.textStroke
|
|
202
|
+
? 2
|
|
203
|
+
: 0,
|
|
204
|
+
fill: args.textFill || args.stroke,
|
|
205
|
+
stroke: args.textStroke
|
|
206
|
+
},
|
|
207
|
+
'fill',
|
|
208
|
+
usedScales,
|
|
209
|
+
true
|
|
210
|
+
)}
|
|
211
|
+
<MarkerPath
|
|
212
|
+
{mark}
|
|
213
|
+
scales={plot.scales}
|
|
214
|
+
markerStart={args.markerStart}
|
|
215
|
+
markerMid={args.markerMid}
|
|
216
|
+
markerEnd={args.markerEnd}
|
|
217
|
+
marker={args.marker}
|
|
218
|
+
strokeWidth={args.strokeWidth}
|
|
219
|
+
datum={lineData[0]}
|
|
159
220
|
d={pathString}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
lineData[0],
|
|
178
|
-
{
|
|
179
|
-
textAnchor: 'middle',
|
|
180
|
-
...pick(args, [
|
|
181
|
-
'fontSize',
|
|
182
|
-
'fontWeight',
|
|
183
|
-
'fontStyle',
|
|
184
|
-
'textAnchor'
|
|
185
|
-
]),
|
|
186
|
-
strokeWidth: args.textStrokeWidth
|
|
187
|
-
? args.textStrokeWidth
|
|
188
|
-
: args.textStroke
|
|
189
|
-
? 2
|
|
190
|
-
: 0,
|
|
191
|
-
fill: args.textFill || args.stroke,
|
|
192
|
-
stroke: args.textStroke
|
|
193
|
-
},
|
|
194
|
-
'fill',
|
|
195
|
-
usedScales,
|
|
196
|
-
true
|
|
197
|
-
)}
|
|
198
|
-
<MarkerPath
|
|
199
|
-
{mark}
|
|
200
|
-
scales={plot.scales}
|
|
201
|
-
markerStart={args.markerStart}
|
|
202
|
-
markerMid={args.markerMid}
|
|
203
|
-
markerEnd={args.markerEnd}
|
|
204
|
-
marker={args.marker}
|
|
205
|
-
strokeWidth={args.strokeWidth}
|
|
206
|
-
datum={lineData[0]}
|
|
207
|
-
d={pathString}
|
|
208
|
-
dInv={text ? linePath(lineData.toReversed()) : null}
|
|
209
|
-
color={lineData[0].stroke || 'currentColor'}
|
|
210
|
-
{style}
|
|
211
|
-
class={styleClass}
|
|
212
|
-
text={text ? resolveProp(text, lineData[0]) : null}
|
|
213
|
-
startOffset={resolveProp(args.textStartOffset, lineData[0], '50%')}
|
|
214
|
-
{textStyle}
|
|
215
|
-
{textStyleClass} />
|
|
216
|
-
</GroupMultiple>
|
|
217
|
-
{/if}
|
|
218
|
-
{/each}
|
|
219
|
-
</g>
|
|
221
|
+
dInv={text ? linePath(lineData.toReversed()) : null}
|
|
222
|
+
color={lineData[0].stroke || 'currentColor'}
|
|
223
|
+
{style}
|
|
224
|
+
class={styleClass}
|
|
225
|
+
text={text ? resolveProp(text, lineData[0]) : null}
|
|
226
|
+
startOffset={resolveProp(
|
|
227
|
+
args.textStartOffset,
|
|
228
|
+
lineData[0],
|
|
229
|
+
'50%'
|
|
230
|
+
)}
|
|
231
|
+
{textStyle}
|
|
232
|
+
{textStyleClass} />
|
|
233
|
+
</GroupMultiple>
|
|
234
|
+
{/if}
|
|
235
|
+
{/each}
|
|
236
|
+
</g>
|
|
237
|
+
{/if}
|
|
220
238
|
{/if}
|
|
221
239
|
{/snippet}
|
|
222
240
|
</Mark>
|
|
@@ -3,8 +3,9 @@ export type BaseLineMarkProps = {
|
|
|
3
3
|
data: DataRecord[];
|
|
4
4
|
z?: ChannelAccessor;
|
|
5
5
|
stroke?: ChannelAccessor;
|
|
6
|
-
outlineStroke?:
|
|
7
|
-
outlineStrokeWidth?:
|
|
6
|
+
outlineStroke?: string;
|
|
7
|
+
outlineStrokeWidth?: number;
|
|
8
|
+
outlineStrokeOpacity?: number;
|
|
8
9
|
dx?: ConstantAccessor<number>;
|
|
9
10
|
dy?: ConstantAccessor<number>;
|
|
10
11
|
curve?: CurveName | CurveFactory;
|
|
@@ -18,8 +19,9 @@ export type BaseLineMarkProps = {
|
|
|
18
19
|
textStartOffset?: ConstantAccessor<string>;
|
|
19
20
|
textStrokeWidth?: ConstantAccessor<number>;
|
|
20
21
|
lineClass?: ConstantAccessor<string>;
|
|
22
|
+
canvas?: boolean;
|
|
21
23
|
} & MarkerOptions;
|
|
22
|
-
import { type CurveFactory } from 'd3-shape';
|
|
24
|
+
import { type CurveFactory, type Line } from 'd3-shape';
|
|
23
25
|
import type { RawValue } from '../types.js';
|
|
24
26
|
type LineMarkProps = BaseMarkProps & {
|
|
25
27
|
x?: ChannelAccessor;
|
package/dist/marks/Rect.svelte
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
} from '../types.js';
|
|
18
18
|
import { addEventHandlers } from './helpers/events.js';
|
|
19
19
|
import GroupMultiple from './helpers/GroupMultiple.svelte';
|
|
20
|
+
import RectPath from './helpers/RectPath.svelte';
|
|
20
21
|
|
|
21
22
|
type RectMarkProps = BaseMarkProps & {
|
|
22
23
|
data: DataRecord[];
|
|
@@ -57,31 +58,16 @@
|
|
|
57
58
|
{@const maxy = Math.max(y1, y2)}
|
|
58
59
|
{@const minx = Math.min(x1, x2)}
|
|
59
60
|
{@const maxx = Math.max(x1, x2)}
|
|
60
|
-
{@const inset = resolveProp(args.inset, d.datum, 0)}
|
|
61
|
-
{@const insetLeft = resolveProp(args.insetLeft, d.datum)}
|
|
62
|
-
{@const insetRight = resolveProp(args.insetRight, d.datum)}
|
|
63
|
-
{@const insetTop = resolveProp(args.insetTop, d.datum)}
|
|
64
|
-
{@const insetBottom = resolveProp(args.insetBottom, d.datum)}
|
|
65
|
-
{@const insetL = maybeNumber(coalesce(insetLeft, inset, 0)) ?? 0}
|
|
66
|
-
{@const insetT = maybeNumber(coalesce(insetTop, inset, 0)) ?? 0}
|
|
67
|
-
{@const insetR = maybeNumber(coalesce(insetRight, inset, 0)) ?? 0}
|
|
68
|
-
{@const insetB = maybeNumber(coalesce(insetBottom, inset, 0)) ?? 0}
|
|
69
61
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
class={
|
|
73
|
-
{
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
ry={resolveProp(args.ry, d.datum, null)}
|
|
80
|
-
use:addEventHandlers={{
|
|
81
|
-
getPlotState,
|
|
82
|
-
options: args,
|
|
83
|
-
datum: d.datum
|
|
84
|
-
}} />
|
|
62
|
+
<RectPath
|
|
63
|
+
datum={d}
|
|
64
|
+
class={scaledData.length === 1 ? className : null}
|
|
65
|
+
x={minx}
|
|
66
|
+
y={miny}
|
|
67
|
+
width={maxx - minx}
|
|
68
|
+
height={maxy - miny}
|
|
69
|
+
options={args}
|
|
70
|
+
{usedScales} />
|
|
85
71
|
{/if}
|
|
86
72
|
{/each}
|
|
87
73
|
</GroupMultiple>
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import CanvasLayer from './CanvasLayer.svelte';
|
|
15
15
|
import { getContext } from 'svelte';
|
|
16
16
|
import { devicePixelRatio } from 'svelte/reactivity/window';
|
|
17
|
+
import { resolveColor } from './canvas';
|
|
17
18
|
|
|
18
19
|
const { getPlotState } = getContext<PlotContext>('svelteplot');
|
|
19
20
|
const plot = $derived(getPlotState());
|
|
@@ -46,19 +47,8 @@
|
|
|
46
47
|
if (datum.valid) {
|
|
47
48
|
let { fill, stroke } = datum;
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
canvas?.parentElement?.parentElement
|
|
52
|
-
).getPropertyValue('color');
|
|
53
|
-
if (`${stroke}`.toLowerCase() === 'currentcolor')
|
|
54
|
-
stroke = getComputedStyle(
|
|
55
|
-
canvas?.parentElement?.parentElement
|
|
56
|
-
).getPropertyValue('color');
|
|
57
|
-
|
|
58
|
-
if (CSS_VAR.test(fill))
|
|
59
|
-
fill = getComputedStyle(canvas).getPropertyValue(fill.slice(4, -1));
|
|
60
|
-
if (CSS_VAR.test(stroke))
|
|
61
|
-
stroke = getComputedStyle(canvas).getPropertyValue(stroke.slice(4, -1));
|
|
50
|
+
fill = resolveColor(fill, canvas);
|
|
51
|
+
stroke = resolveColor(stroke, canvas);
|
|
62
52
|
|
|
63
53
|
if (stroke && stroke !== 'none') {
|
|
64
54
|
const strokeWidth = resolveProp(
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type {
|
|
3
|
+
Mark,
|
|
4
|
+
BaseMarkProps,
|
|
5
|
+
PlotContext,
|
|
6
|
+
ScaledDataRecord,
|
|
7
|
+
UsedScales
|
|
8
|
+
} from '../../types.js';
|
|
9
|
+
import { resolveProp, resolveScaledStyleProps } from '../../helpers/resolve.js';
|
|
10
|
+
import { getContext } from 'svelte';
|
|
11
|
+
import { type Line } from 'd3-shape';
|
|
12
|
+
import CanvasLayer from './CanvasLayer.svelte';
|
|
13
|
+
import type { Attachment } from 'svelte/attachments';
|
|
14
|
+
import { devicePixelRatio } from 'svelte/reactivity/window';
|
|
15
|
+
import { resolveColor } from './canvas';
|
|
16
|
+
|
|
17
|
+
let {
|
|
18
|
+
mark,
|
|
19
|
+
groupedLineData,
|
|
20
|
+
usedScales,
|
|
21
|
+
linePath
|
|
22
|
+
}: {
|
|
23
|
+
mark: Mark<BaseMarkProps>;
|
|
24
|
+
groupedLineData: ScaledDataRecord[][];
|
|
25
|
+
usedScales: UsedScales;
|
|
26
|
+
linePath: Line<ScaledDataRecord>;
|
|
27
|
+
groupByKey?: unknown;
|
|
28
|
+
} = $props();
|
|
29
|
+
|
|
30
|
+
const { getPlotState } = getContext<PlotContext>('svelteplot');
|
|
31
|
+
const plot = $derived(getPlotState());
|
|
32
|
+
|
|
33
|
+
function maybeOpacity(value: unknown) {
|
|
34
|
+
return value == null ? 1 : +value;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const render = ((canvas: HTMLCanvasElement) => {
|
|
38
|
+
const context = canvas.getContext('2d');
|
|
39
|
+
|
|
40
|
+
$effect(() => {
|
|
41
|
+
if (context) {
|
|
42
|
+
linePath.context(context);
|
|
43
|
+
context.resetTransform();
|
|
44
|
+
context.scale(devicePixelRatio.current ?? 1, devicePixelRatio.current ?? 1);
|
|
45
|
+
context.lineJoin = 'round';
|
|
46
|
+
context.lineCap = 'round';
|
|
47
|
+
|
|
48
|
+
for (const group of groupedLineData) {
|
|
49
|
+
if (group.length < 2) continue;
|
|
50
|
+
|
|
51
|
+
// Get the first point to determine line styles
|
|
52
|
+
const firstPoint = group[0];
|
|
53
|
+
if (!firstPoint || !firstPoint.valid) continue;
|
|
54
|
+
|
|
55
|
+
let { stroke, ...restStyles } = resolveScaledStyleProps(
|
|
56
|
+
firstPoint.datum,
|
|
57
|
+
mark.options,
|
|
58
|
+
usedScales,
|
|
59
|
+
plot,
|
|
60
|
+
'stroke'
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const opacity = maybeOpacity(restStyles['opacity']);
|
|
64
|
+
const strokeOpacity = maybeOpacity(restStyles['stroke-opacity']);
|
|
65
|
+
|
|
66
|
+
const strokeWidth = resolveProp(
|
|
67
|
+
mark.options.strokeWidth,
|
|
68
|
+
firstPoint.datum,
|
|
69
|
+
1.4
|
|
70
|
+
) as number;
|
|
71
|
+
|
|
72
|
+
if (mark.options.outlineStroke) {
|
|
73
|
+
// draw stroke outline first
|
|
74
|
+
const outlineStroke = resolveColor(mark.options.outlineStroke, canvas);
|
|
75
|
+
const outlineStrokeWidth =
|
|
76
|
+
mark.options.outlineStrokeWidth ?? strokeWidth + 2;
|
|
77
|
+
const outlineStrokeOpacity = mark.options.outlineStrokeOpacity ?? 1;
|
|
78
|
+
|
|
79
|
+
context.lineWidth = outlineStrokeWidth;
|
|
80
|
+
context.strokeStyle = outlineStroke;
|
|
81
|
+
context.globalAlpha = opacity * outlineStrokeOpacity;
|
|
82
|
+
context.beginPath();
|
|
83
|
+
linePath(group);
|
|
84
|
+
context.stroke();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
stroke = resolveColor(stroke, canvas);
|
|
88
|
+
|
|
89
|
+
if (stroke && stroke !== 'none') {
|
|
90
|
+
context.lineWidth = strokeWidth ?? 1.4;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
context.strokeStyle = stroke ? stroke : 'currentColor';
|
|
94
|
+
context.globalAlpha = opacity * strokeOpacity;
|
|
95
|
+
|
|
96
|
+
// Start drawing the line
|
|
97
|
+
context.beginPath();
|
|
98
|
+
linePath(group);
|
|
99
|
+
context.stroke();
|
|
100
|
+
}
|
|
101
|
+
linePath.context(null);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return () => {
|
|
105
|
+
context?.clearRect(
|
|
106
|
+
0,
|
|
107
|
+
0,
|
|
108
|
+
plot.width * (devicePixelRatio.current ?? 1),
|
|
109
|
+
plot.height * (devicePixelRatio.current ?? 1)
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
});
|
|
113
|
+
}) as Attachment;
|
|
114
|
+
</script>
|
|
115
|
+
|
|
116
|
+
<CanvasLayer {@attach render} />
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Mark, BaseMarkProps, ScaledDataRecord, UsedScales } from '../../types.js';
|
|
2
|
+
import { type Line } from 'd3-shape';
|
|
3
|
+
type $$ComponentProps = {
|
|
4
|
+
mark: Mark<BaseMarkProps>;
|
|
5
|
+
groupedLineData: ScaledDataRecord[][];
|
|
6
|
+
usedScales: UsedScales;
|
|
7
|
+
linePath: Line<ScaledDataRecord>;
|
|
8
|
+
groupByKey?: unknown;
|
|
9
|
+
};
|
|
10
|
+
declare const LineCanvas: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
11
|
+
type LineCanvas = ReturnType<typeof LineCanvas>;
|
|
12
|
+
export default LineCanvas;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getContext } from 'svelte';
|
|
3
|
+
import type { PlotContext, RawValue } from '../../types';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
id,
|
|
7
|
+
stops
|
|
8
|
+
}: {
|
|
9
|
+
id: string;
|
|
10
|
+
stops: { x: RawValue; color: string }[];
|
|
11
|
+
} = $props();
|
|
12
|
+
|
|
13
|
+
const { getPlotState } = getContext<PlotContext>('svelteplot');
|
|
14
|
+
const plot = $derived(getPlotState());
|
|
15
|
+
|
|
16
|
+
const projectedStops = $derived(
|
|
17
|
+
stops
|
|
18
|
+
.map((d) => ({ ...d, px: plot.scales.x.fn(d.x) / plot.width }))
|
|
19
|
+
.sort((a, b) => a.px - b.px)
|
|
20
|
+
);
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<linearGradient {id} gradientUnits="userSpaceOnUse" x1={0} y2={0} y1={0} x2={plot.width}>
|
|
24
|
+
{#each projectedStops as { px, color }}
|
|
25
|
+
<stop stop-color={color} offset={px} />
|
|
26
|
+
{/each}
|
|
27
|
+
</linearGradient>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RawValue } from '../../types';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
id: string;
|
|
4
|
+
stops: {
|
|
5
|
+
x: RawValue;
|
|
6
|
+
color: string;
|
|
7
|
+
}[];
|
|
8
|
+
};
|
|
9
|
+
declare const LinearGradientX: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
10
|
+
type LinearGradientX = ReturnType<typeof LinearGradientX>;
|
|
11
|
+
export default LinearGradientX;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getContext } from 'svelte';
|
|
3
|
+
import type { PlotContext, RawValue } from '../../types';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
id,
|
|
7
|
+
stops
|
|
8
|
+
}: {
|
|
9
|
+
id: string;
|
|
10
|
+
stops: { y: RawValue; color: string }[];
|
|
11
|
+
} = $props();
|
|
12
|
+
|
|
13
|
+
const { getPlotState } = getContext<PlotContext>('svelteplot');
|
|
14
|
+
const plot = $derived(getPlotState());
|
|
15
|
+
|
|
16
|
+
const projectedStops = $derived(
|
|
17
|
+
stops
|
|
18
|
+
.map((d) => ({ ...d, py: plot.scales.y.fn(d.y) / plot.height }))
|
|
19
|
+
.sort((a, b) => a.py - b.py)
|
|
20
|
+
);
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<linearGradient {id} gradientUnits="userSpaceOnUse" x1={0} x2={0} y1={0} y2={plot.height}>
|
|
24
|
+
{#each projectedStops as { py, color }}
|
|
25
|
+
<stop stop-color={color} offset={py} />
|
|
26
|
+
{/each}
|
|
27
|
+
</linearGradient>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RawValue } from '../../types';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
id: string;
|
|
4
|
+
stops: {
|
|
5
|
+
y: RawValue;
|
|
6
|
+
color: string;
|
|
7
|
+
}[];
|
|
8
|
+
};
|
|
9
|
+
declare const LinearGradientY: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
10
|
+
type LinearGradientY = ReturnType<typeof LinearGradientY>;
|
|
11
|
+
export default LinearGradientY;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
<!-- @component
|
|
2
|
+
Helper component for rendering rectangular marks in SVG
|
|
3
|
+
-->
|
|
4
|
+
<script lang="ts">
|
|
5
|
+
import { resolveProp, resolveStyles } from '../../helpers/resolve';
|
|
6
|
+
import { roundedRect } from '../../helpers/roundedRect';
|
|
7
|
+
import type {
|
|
8
|
+
BaseMarkProps,
|
|
9
|
+
BaseRectMarkProps,
|
|
10
|
+
BorderRadius,
|
|
11
|
+
ScaledDataRecord,
|
|
12
|
+
UsedScales,
|
|
13
|
+
PlotContext
|
|
14
|
+
} from '../../types';
|
|
15
|
+
import { addEventHandlers } from './events';
|
|
16
|
+
import { getContext } from 'svelte';
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
datum,
|
|
20
|
+
options,
|
|
21
|
+
class: className = null,
|
|
22
|
+
x,
|
|
23
|
+
y,
|
|
24
|
+
width,
|
|
25
|
+
height,
|
|
26
|
+
useInsetAsFallbackVertically = true,
|
|
27
|
+
useInsetAsFallbackHorizontally = true,
|
|
28
|
+
usedScales
|
|
29
|
+
}: {
|
|
30
|
+
datum: ScaledDataRecord;
|
|
31
|
+
class: string | null;
|
|
32
|
+
x: number;
|
|
33
|
+
y: number;
|
|
34
|
+
width: number;
|
|
35
|
+
height: number;
|
|
36
|
+
options: BaseRectMarkProps & BaseMarkProps;
|
|
37
|
+
/**
|
|
38
|
+
* By default, the `inset` property is applied to all four insets. Mark components
|
|
39
|
+
* can tweak this behavior for insetTop and insetBottom by setting the
|
|
40
|
+
* useInsetAsFallbackVertically prop to false.
|
|
41
|
+
*/
|
|
42
|
+
useInsetAsFallbackVertically?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* By default, the `inset` property is applied to all four insets. Mark components
|
|
45
|
+
* can tweak this behavior for insetLeft and insetRight by setting the
|
|
46
|
+
* useInsetAsFallbackHorizontally prop to false.
|
|
47
|
+
*/
|
|
48
|
+
useInsetAsFallbackHorizontally?: boolean;
|
|
49
|
+
usedScales: UsedScales;
|
|
50
|
+
} = $props();
|
|
51
|
+
|
|
52
|
+
const { getPlotState } = getContext<PlotContext>('svelteplot');
|
|
53
|
+
const plot = $derived(getPlotState());
|
|
54
|
+
|
|
55
|
+
const dx = $derived(+(resolveProp(options.dx, datum.datum, 0) as number));
|
|
56
|
+
const dy = $derived(+(resolveProp(options.dy, datum.datum, 0) as number));
|
|
57
|
+
const inset = $derived(+(resolveProp(options.inset, datum.datum, 0) as number));
|
|
58
|
+
const insetLeft = $derived(
|
|
59
|
+
+(resolveProp(
|
|
60
|
+
options.insetLeft,
|
|
61
|
+
datum.datum,
|
|
62
|
+
useInsetAsFallbackHorizontally ? inset : 0
|
|
63
|
+
) as number)
|
|
64
|
+
);
|
|
65
|
+
const insetRight = $derived(
|
|
66
|
+
+(resolveProp(
|
|
67
|
+
options.insetRight,
|
|
68
|
+
datum.datum,
|
|
69
|
+
useInsetAsFallbackHorizontally ? inset : 0
|
|
70
|
+
) as number)
|
|
71
|
+
);
|
|
72
|
+
const insetTop = $derived(
|
|
73
|
+
+(resolveProp(
|
|
74
|
+
options.insetTop,
|
|
75
|
+
datum.datum,
|
|
76
|
+
useInsetAsFallbackVertically ? inset : 0
|
|
77
|
+
) as number)
|
|
78
|
+
);
|
|
79
|
+
const insetBottom = $derived(
|
|
80
|
+
+(resolveProp(
|
|
81
|
+
options.insetBottom,
|
|
82
|
+
datum.datum,
|
|
83
|
+
useInsetAsFallbackVertically ? inset : 0
|
|
84
|
+
) as number)
|
|
85
|
+
);
|
|
86
|
+
const borderRadius = $derived((options.borderRadius ?? 0) as BorderRadius);
|
|
87
|
+
const hasBorderRadius = $derived(
|
|
88
|
+
(typeof borderRadius === 'number' && borderRadius > 0) ||
|
|
89
|
+
(typeof borderRadius === 'object' &&
|
|
90
|
+
Math.max(
|
|
91
|
+
borderRadius.topRight ?? 0,
|
|
92
|
+
borderRadius.bottomRight ?? 0,
|
|
93
|
+
borderRadius.topLeft ?? 0,
|
|
94
|
+
borderRadius.bottomLeft ?? 0
|
|
95
|
+
) > 0)
|
|
96
|
+
);
|
|
97
|
+
const [style, styleClass] = $derived(resolveStyles(plot, datum, options, 'fill', usedScales));
|
|
98
|
+
</script>
|
|
99
|
+
|
|
100
|
+
{#if hasBorderRadius}
|
|
101
|
+
<path
|
|
102
|
+
transform="translate({[x + dx + insetLeft, y + insetBottom + dy]})"
|
|
103
|
+
d={roundedRect(
|
|
104
|
+
0,
|
|
105
|
+
0,
|
|
106
|
+
width - insetLeft - insetRight,
|
|
107
|
+
height - insetTop - insetBottom,
|
|
108
|
+
borderRadius
|
|
109
|
+
)}
|
|
110
|
+
class={[styleClass, className]}
|
|
111
|
+
{style}
|
|
112
|
+
use:addEventHandlers={{
|
|
113
|
+
getPlotState,
|
|
114
|
+
options,
|
|
115
|
+
datum: datum.datum
|
|
116
|
+
}} />
|
|
117
|
+
{:else}
|
|
118
|
+
<rect
|
|
119
|
+
transform="translate({[x + dx + insetLeft, y + insetBottom + dy]})"
|
|
120
|
+
width={width - insetLeft - insetRight}
|
|
121
|
+
height={height - insetTop - insetBottom}
|
|
122
|
+
class={[styleClass, className]}
|
|
123
|
+
{style}
|
|
124
|
+
use:addEventHandlers={{
|
|
125
|
+
getPlotState,
|
|
126
|
+
options,
|
|
127
|
+
datum: datum.datum
|
|
128
|
+
}} />
|
|
129
|
+
{/if}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { BaseMarkProps, BaseRectMarkProps, ScaledDataRecord, UsedScales } from '../../types';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
datum: ScaledDataRecord;
|
|
4
|
+
class: string | null;
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
options: BaseRectMarkProps & BaseMarkProps;
|
|
10
|
+
/**
|
|
11
|
+
* By default, the `inset` property is applied to all four insets. Mark components
|
|
12
|
+
* can tweak this behavior for insetTop and insetBottom by setting the
|
|
13
|
+
* useInsetAsFallbackVertically prop to false.
|
|
14
|
+
*/
|
|
15
|
+
useInsetAsFallbackVertically?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* By default, the `inset` property is applied to all four insets. Mark components
|
|
18
|
+
* can tweak this behavior for insetLeft and insetRight by setting the
|
|
19
|
+
* useInsetAsFallbackHorizontally prop to false.
|
|
20
|
+
*/
|
|
21
|
+
useInsetAsFallbackHorizontally?: boolean;
|
|
22
|
+
usedScales: UsedScales;
|
|
23
|
+
};
|
|
24
|
+
/** Helper component for rendering rectangular marks in SVG */
|
|
25
|
+
declare const RectPath: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
26
|
+
type RectPath = ReturnType<typeof RectPath>;
|
|
27
|
+
export default RectPath;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function resolveColor(color: string, canvas: HTMLCanvasElement): string | CanvasGradient;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { CSS_URL, CSS_VAR } from "../../constants";
|
|
2
|
+
export function resolveColor(color, canvas) {
|
|
3
|
+
if (`${color}`.toLowerCase() === 'currentcolor') {
|
|
4
|
+
color = getComputedStyle(canvas?.parentElement?.parentElement).getPropertyValue('color');
|
|
5
|
+
}
|
|
6
|
+
if (CSS_VAR.test(color)) {
|
|
7
|
+
color = getComputedStyle(canvas).getPropertyValue(color.slice(4, -1));
|
|
8
|
+
}
|
|
9
|
+
if (CSS_URL.test(color)) {
|
|
10
|
+
// might be a gradient we can parse!
|
|
11
|
+
const m = color.match(/^url\((#[^\)]+)\)/);
|
|
12
|
+
const gradientId = m[1];
|
|
13
|
+
const gradient = canvas.ownerDocument.querySelector(gradientId);
|
|
14
|
+
if (gradient) {
|
|
15
|
+
// parse gradient
|
|
16
|
+
if (gradient.nodeName.toLowerCase() === 'lineargradient') {
|
|
17
|
+
const x0 = +gradient.getAttribute('x1');
|
|
18
|
+
const x1 = +gradient.getAttribute('x2');
|
|
19
|
+
const y0 = +gradient.getAttribute('y1');
|
|
20
|
+
const y1 = +gradient.getAttribute('y2');
|
|
21
|
+
const ctxGradient = canvas
|
|
22
|
+
.getContext('2d')
|
|
23
|
+
.createLinearGradient(x0, y0, x1, y1);
|
|
24
|
+
for (const stop of gradient.querySelectorAll('stop')) {
|
|
25
|
+
const offset = +stop.getAttribute('offset');
|
|
26
|
+
const color = resolveColor(stop.getAttribute('stop-color'), canvas);
|
|
27
|
+
ctxGradient.addColorStop(offset, color);
|
|
28
|
+
}
|
|
29
|
+
return ctxGradient;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return color;
|
|
34
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -555,14 +555,19 @@ export type BaseMarkProps = Partial<{
|
|
|
555
555
|
class: string;
|
|
556
556
|
cursor: ConstantAccessor<CSS.Property.Cursor>;
|
|
557
557
|
}>;
|
|
558
|
+
export type BorderRadius = number | {
|
|
559
|
+
topLeft?: number;
|
|
560
|
+
topRight?: number;
|
|
561
|
+
bottomRight?: number;
|
|
562
|
+
bottomLeft?: number;
|
|
563
|
+
};
|
|
558
564
|
export type BaseRectMarkProps = {
|
|
559
|
-
rx?: ConstantAccessor<number>;
|
|
560
|
-
ry?: ConstantAccessor<number>;
|
|
561
565
|
inset?: ConstantAccessor<number>;
|
|
562
566
|
insetLeft?: ConstantAccessor<number>;
|
|
563
567
|
insetTop?: ConstantAccessor<number>;
|
|
564
568
|
insetRight?: ConstantAccessor<number>;
|
|
565
569
|
insetBottom?: ConstantAccessor<number>;
|
|
570
|
+
borderRadius?: BorderRadius;
|
|
566
571
|
};
|
|
567
572
|
export type Channels = Record<string, ChannelAccessor | ConstantAccessor<string | number | boolean | symbol>>;
|
|
568
573
|
export type TransformArg<K> = Channels & {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelteplot",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Gregor Aisch",
|
|
@@ -87,6 +87,7 @@
|
|
|
87
87
|
"svelte-check": "^4.2.1",
|
|
88
88
|
"svelte-eslint-parser": "1.2.0",
|
|
89
89
|
"svelte-highlight": "^7.8.3",
|
|
90
|
+
"svg-path-parser": "^1.1.0",
|
|
90
91
|
"topojson-client": "^3.1.0",
|
|
91
92
|
"tslib": "^2.8.1",
|
|
92
93
|
"typedoc": "^0.28.4",
|