@rokkit/chart 1.0.0-next.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +55 -0
- package/package.json +63 -0
- package/src/chart/FacetGrid.svelte +51 -0
- package/src/chart/Grid.svelte +34 -0
- package/src/chart/Legend.svelte +16 -0
- package/src/chart/PatternDefs.svelte +13 -0
- package/src/chart/Swatch.svelte +93 -0
- package/src/chart/SwatchButton.svelte +29 -0
- package/src/chart/SwatchGrid.svelte +55 -0
- package/src/chart/Symbol.svelte +37 -0
- package/src/chart/Texture.svelte +16 -0
- package/src/chart/TexturedShape.svelte +27 -0
- package/src/chart/TimelapseChart.svelte +97 -0
- package/src/chart/Timer.svelte +27 -0
- package/src/chart.js +9 -0
- package/src/components/charts/Axis.svelte +66 -0
- package/src/components/charts/Chart.svelte +35 -0
- package/src/components/index.js +23 -0
- package/src/components/lib/axis.js +0 -0
- package/src/components/lib/chart.js +187 -0
- package/src/components/lib/color.js +327 -0
- package/src/components/lib/funnel.js +204 -0
- package/src/components/lib/index.js +19 -0
- package/src/components/lib/pattern.js +190 -0
- package/src/components/lib/rollup.js +55 -0
- package/src/components/lib/shape.js +199 -0
- package/src/components/lib/summary.js +145 -0
- package/src/components/lib/theme.js +23 -0
- package/src/components/lib/timer.js +41 -0
- package/src/components/lib/utils.js +165 -0
- package/src/components/plots/BarPlot.svelte +36 -0
- package/src/components/plots/BoxPlot.svelte +54 -0
- package/src/components/plots/ScatterPlot.svelte +30 -0
- package/src/components/store.js +70 -0
- package/src/constants.js +66 -0
- package/src/elements/Bar.svelte +35 -0
- package/src/elements/ColorRamp.svelte +51 -0
- package/src/elements/ContinuousLegend.svelte +46 -0
- package/src/elements/DiscreteLegend.svelte +41 -0
- package/src/elements/Label.svelte +12 -0
- package/src/elements/PatternDefs.svelte +13 -0
- package/src/elements/PatternMask.svelte +20 -0
- package/src/elements/Symbol.svelte +38 -0
- package/src/elements/Tooltip.svelte +23 -0
- package/src/funnel.svelte +35 -0
- package/src/geom.js +105 -0
- package/src/index.js +16 -0
- package/src/lib/axis.js +75 -0
- package/src/lib/colors.js +32 -0
- package/src/lib/geom.js +4 -0
- package/src/lib/shapes.js +144 -0
- package/src/lib/timer.js +44 -0
- package/src/lib/utils.js +157 -0
- package/src/lookup.js +29 -0
- package/src/plots/BarPlot.svelte +55 -0
- package/src/plots/BoxPlot.svelte +0 -0
- package/src/plots/FunnelPlot.svelte +33 -0
- package/src/plots/HeatMap.svelte +5 -0
- package/src/plots/HeatMapCalendar.svelte +129 -0
- package/src/plots/LinePlot.svelte +55 -0
- package/src/plots/Plot.svelte +25 -0
- package/src/plots/RankBarPlot.svelte +38 -0
- package/src/plots/ScatterPlot.svelte +20 -0
- package/src/plots/ViolinPlot.svelte +11 -0
- package/src/plots/heatmap.js +70 -0
- package/src/plots/index.js +10 -0
- package/src/swatch.js +11 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { scaleLinear } from 'd3-scale'
|
|
3
|
+
import { uniqueId } from '../lib/utils'
|
|
4
|
+
|
|
5
|
+
export let x = 0
|
|
6
|
+
export let y = 0
|
|
7
|
+
export let textSize = 5
|
|
8
|
+
export let height = 10
|
|
9
|
+
export let width = 100
|
|
10
|
+
export let tickCount = 5
|
|
11
|
+
export let scale
|
|
12
|
+
|
|
13
|
+
$: scaleTicks = scaleLinear()
|
|
14
|
+
.range([x, x + width])
|
|
15
|
+
.domain(scale.domain())
|
|
16
|
+
$: scalePercent = scaleLinear().range([0, 100]).domain(scale.domain())
|
|
17
|
+
$: ticks = scale.ticks
|
|
18
|
+
.apply(scale, [tickCount])
|
|
19
|
+
.map((d) => ({ x: scaleTicks(d), value: d }))
|
|
20
|
+
|
|
21
|
+
$: colors = ticks.map(({ value }) => ({
|
|
22
|
+
color: scale(value),
|
|
23
|
+
offset: `${scalePercent(value)}%`,
|
|
24
|
+
}))
|
|
25
|
+
$: id = uniqueId('legend-')
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<defs>
|
|
29
|
+
<linearGradient {id}>
|
|
30
|
+
{#each colors as { color, offset }}
|
|
31
|
+
<stop stop-color={color} {offset} />
|
|
32
|
+
{/each}
|
|
33
|
+
</linearGradient>
|
|
34
|
+
</defs>
|
|
35
|
+
<rect {x} y={y + height} {width} {height} fill="url(#{id})" />
|
|
36
|
+
{#each ticks as { x, value }}
|
|
37
|
+
<line x1={x} y1={y + (2 * height) / 3} x2={x} y2={y + height * 2} />
|
|
38
|
+
<text {x} y={y + height / 2} font-size={textSize}>{value}</text>
|
|
39
|
+
{/each}
|
|
40
|
+
<line x1={x} y1={y + 2 * height} x2={x + 100} y2={y + 2 * height} />
|
|
41
|
+
|
|
42
|
+
<style>
|
|
43
|
+
line {
|
|
44
|
+
stroke: currentColor;
|
|
45
|
+
stroke-width: 0.2;
|
|
46
|
+
}
|
|
47
|
+
text {
|
|
48
|
+
fill: currentColor;
|
|
49
|
+
text-anchor: middle;
|
|
50
|
+
}
|
|
51
|
+
</style>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { scaleLinear } from 'd3-scale'
|
|
3
|
+
import { uniqueId } from '../components/lib/utils'
|
|
4
|
+
|
|
5
|
+
export let x = 0
|
|
6
|
+
export let y = 0
|
|
7
|
+
export let textSize = 5
|
|
8
|
+
export let height = 10
|
|
9
|
+
export let width = 100
|
|
10
|
+
export let tickCount = 5
|
|
11
|
+
export let scale
|
|
12
|
+
|
|
13
|
+
$: scaleTicks = scaleLinear()
|
|
14
|
+
.range([x, x + 100])
|
|
15
|
+
.domain(scale.domain())
|
|
16
|
+
$: ticks = scale.ticks
|
|
17
|
+
.apply(scale, [tickCount])
|
|
18
|
+
.map((d) => ({ x: scaleTicks(d), label: d }))
|
|
19
|
+
|
|
20
|
+
$: colors = scale.range()
|
|
21
|
+
$: id = uniqueId('legend-')
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<defs>
|
|
25
|
+
<linearGradient {id}>
|
|
26
|
+
<stop stop-color={colors[0]} offset="0%" />
|
|
27
|
+
<stop stop-color={colors[1]} offset="100%" />
|
|
28
|
+
</linearGradient>
|
|
29
|
+
</defs>
|
|
30
|
+
<rect {x} y={y + height} {width} {height} fill="url(#{id})" />
|
|
31
|
+
{#each ticks as { x, label }}
|
|
32
|
+
<line x1={x} y1={y + (2 * height) / 3} x2={x} y2={y + height * 2} />
|
|
33
|
+
<text {x} y={y + height / 2} font-size={textSize}>{label}</text>
|
|
34
|
+
{/each}
|
|
35
|
+
<line x1={x} y1={y + 2 * height} x2={x + 100} y2={y + 2 * height} />
|
|
36
|
+
|
|
37
|
+
<style>
|
|
38
|
+
line {
|
|
39
|
+
stroke: currentColor;
|
|
40
|
+
stroke-width: 0.2;
|
|
41
|
+
}
|
|
42
|
+
text {
|
|
43
|
+
fill: currentColor;
|
|
44
|
+
text-anchor: middle;
|
|
45
|
+
}
|
|
46
|
+
</style>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
export let x = 0
|
|
3
|
+
export let y = 0
|
|
4
|
+
export let textSize = 5
|
|
5
|
+
export let size = 10
|
|
6
|
+
export let space = 2
|
|
7
|
+
export let padding = 5
|
|
8
|
+
export let scale
|
|
9
|
+
export let tickCount
|
|
10
|
+
|
|
11
|
+
$: sizeWithSpace = size + space
|
|
12
|
+
$: ticks = scale.ticks.apply(scale, [tickCount])
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
{#each ticks as tick, i}
|
|
16
|
+
<text
|
|
17
|
+
x={x + padding + i * sizeWithSpace + size / 2}
|
|
18
|
+
y={y + size / 2}
|
|
19
|
+
font-size={textSize}>{tick}</text
|
|
20
|
+
>
|
|
21
|
+
<rect
|
|
22
|
+
x={x + padding + i * sizeWithSpace}
|
|
23
|
+
y={y + padding + textSize}
|
|
24
|
+
width={size}
|
|
25
|
+
height={size}
|
|
26
|
+
fill={scale(tick)}
|
|
27
|
+
rx="1"
|
|
28
|
+
ry="1"
|
|
29
|
+
/>
|
|
30
|
+
{/each}
|
|
31
|
+
|
|
32
|
+
<style>
|
|
33
|
+
rect {
|
|
34
|
+
stroke: currentColor;
|
|
35
|
+
stroke-width: 0.2;
|
|
36
|
+
}
|
|
37
|
+
text {
|
|
38
|
+
fill: currentColor;
|
|
39
|
+
text-anchor: middle;
|
|
40
|
+
}
|
|
41
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
export let small = false
|
|
3
|
+
export let label
|
|
4
|
+
export let angle = 0
|
|
5
|
+
export let anchor = 'middle'
|
|
6
|
+
export let x
|
|
7
|
+
export let y
|
|
8
|
+
|
|
9
|
+
$: transform = `translate(${x},${y}) rotate(${angle})`
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<text class="label" class:small text-anchor={anchor} {transform}>{label}</text>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import PatternMask from './PatternMask.svelte'
|
|
3
|
+
const size = 10
|
|
4
|
+
|
|
5
|
+
export let thickness = 0.5
|
|
6
|
+
export let patterns
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<defs>
|
|
10
|
+
{#each patterns as pattern}
|
|
11
|
+
<PatternMask {...pattern} {thickness} {size} />
|
|
12
|
+
{/each}
|
|
13
|
+
</defs>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
export let id
|
|
3
|
+
// export let fill
|
|
4
|
+
export let path
|
|
5
|
+
// export let contrast
|
|
6
|
+
export let thickness = 0.5
|
|
7
|
+
export let patternUnits = 'userSpaceOnUse'
|
|
8
|
+
export let size = 10
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<pattern id="p-{id}" {patternUnits} width={size} height={size}>
|
|
12
|
+
{#if path}
|
|
13
|
+
<path d={path} fill="none" stroke="white" stroke-width={thickness} />
|
|
14
|
+
<!-- {:else}
|
|
15
|
+
<rect width={size} height={size} {fill} /> -->
|
|
16
|
+
{/if}
|
|
17
|
+
</pattern>
|
|
18
|
+
<mask {id}>
|
|
19
|
+
<rect x="0" y="0" width="100%" height="100%" fill="url(#p-{id})" />
|
|
20
|
+
</mask>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
// import { namedShapes } from '../lib/shape'
|
|
3
|
+
import { swatchStore } from '../swatch'
|
|
4
|
+
|
|
5
|
+
export let x = 0
|
|
6
|
+
export let y = 0
|
|
7
|
+
export let size = 10
|
|
8
|
+
export let fill = 'none'
|
|
9
|
+
export let stroke = 'currentColor'
|
|
10
|
+
export let thickness = 0.5
|
|
11
|
+
|
|
12
|
+
export let name = 'circle'
|
|
13
|
+
// export let shape = null
|
|
14
|
+
|
|
15
|
+
$: x = x - size / 2
|
|
16
|
+
$: y = y - size / 2
|
|
17
|
+
|
|
18
|
+
$: d = $swatchStore[shapes][name](size)
|
|
19
|
+
// typeof shape === 'function'
|
|
20
|
+
// ? shape(size)
|
|
21
|
+
// : (shape || name) in namedShapes
|
|
22
|
+
// ? namedShapes[shape || name](size)
|
|
23
|
+
// : namedShapes.circle(size)
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
|
27
|
+
<path
|
|
28
|
+
{d}
|
|
29
|
+
{fill}
|
|
30
|
+
{stroke}
|
|
31
|
+
transform="translate({x},{y})"
|
|
32
|
+
stroke-width={thickness}
|
|
33
|
+
fill-rule="evenodd"
|
|
34
|
+
on:click
|
|
35
|
+
on:mouseover
|
|
36
|
+
on:mouseleave
|
|
37
|
+
on:focus
|
|
38
|
+
/>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
export let left
|
|
3
|
+
export let top
|
|
4
|
+
export let hidden = true
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<div
|
|
8
|
+
class="tooltip {$$props.class}"
|
|
9
|
+
{hidden}
|
|
10
|
+
style="--top: {top};--left: {left}"
|
|
11
|
+
>
|
|
12
|
+
<slot />
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<style lang="postcss">
|
|
16
|
+
.tooltip {
|
|
17
|
+
padding: 1em 0.5em;
|
|
18
|
+
left: var(--left);
|
|
19
|
+
top: var(--top);
|
|
20
|
+
position: absolute;
|
|
21
|
+
z-index: 10;
|
|
22
|
+
}
|
|
23
|
+
</style>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<script context="module">
|
|
2
|
+
/**
|
|
3
|
+
* @type {import('@sveltejs/kit').Load}
|
|
4
|
+
*/
|
|
5
|
+
export async function load({ page, fetch, session, stuff }) {
|
|
6
|
+
const url = '/api/data/funnel'
|
|
7
|
+
const response = await fetch(url)
|
|
8
|
+
|
|
9
|
+
if (response.ok) {
|
|
10
|
+
let result = await response.json()
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
props: { data: result }
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
status: response.status,
|
|
19
|
+
error: new Error(`Could not load ${url}`)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<script>
|
|
25
|
+
import { FunnelPlot, Chart } from '@rokkit/chart'
|
|
26
|
+
export let data
|
|
27
|
+
|
|
28
|
+
// let names = ['one', 'two', 'three'] // each one is a separate segment. max of Combined value at each stage is overall height/width
|
|
29
|
+
// let groups = ['a', 'b', 'c'] // each group should have a value. individual values are used for each band height
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<h1>Funnel</h1>
|
|
33
|
+
<Chart {data} width={1200}>
|
|
34
|
+
<FunnelPlot x="stage" y="count" fill="client" stat="sum" />
|
|
35
|
+
</Chart>
|
package/src/geom.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import {
|
|
2
|
+
sum,
|
|
3
|
+
min,
|
|
4
|
+
max,
|
|
5
|
+
mean,
|
|
6
|
+
mode,
|
|
7
|
+
median,
|
|
8
|
+
deviation,
|
|
9
|
+
variance,
|
|
10
|
+
flatRollup
|
|
11
|
+
} from 'd3-array'
|
|
12
|
+
|
|
13
|
+
const summaries = {
|
|
14
|
+
identity: (value) => value,
|
|
15
|
+
count: (values) => values.length,
|
|
16
|
+
sum: (values) => sum(values),
|
|
17
|
+
min: (values) => min(values),
|
|
18
|
+
max: (values) => max(values),
|
|
19
|
+
mean: (values) => mean(values),
|
|
20
|
+
median: (values) => median(values),
|
|
21
|
+
mode: (values) => mode(values),
|
|
22
|
+
variance: (values) => variance(values),
|
|
23
|
+
deviation: (values) => deviation(values)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Returns an aggregator function for an input string or function.
|
|
28
|
+
*
|
|
29
|
+
* @param {string|function} stat
|
|
30
|
+
* @returns
|
|
31
|
+
*/
|
|
32
|
+
export function rollup(stat) {
|
|
33
|
+
if (typeof stat === 'function') return stat
|
|
34
|
+
if (typeof stat !== 'string')
|
|
35
|
+
throw new TypeError('stat must be a string or function')
|
|
36
|
+
if (!(stat in summaries)) throw new TypeError('Unknown stat: ' + stat)
|
|
37
|
+
|
|
38
|
+
return summaries[stat]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Aesthetics for a chart.
|
|
43
|
+
*
|
|
44
|
+
* @typedef Aesthetics
|
|
45
|
+
* @property {string} x
|
|
46
|
+
* @property {string} y
|
|
47
|
+
* @property {string} [fill]
|
|
48
|
+
* @property {string} [size]
|
|
49
|
+
* @property {string} [color]
|
|
50
|
+
* @property {string} [shape]
|
|
51
|
+
* @property {string} [pattern]
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
*
|
|
56
|
+
* @param {Array<any>} data
|
|
57
|
+
* @param {Aesthetics} aes
|
|
58
|
+
* @param {function|string} stat
|
|
59
|
+
* @returns
|
|
60
|
+
*/
|
|
61
|
+
export function aggregate(data, aes, stat = 'identity') {
|
|
62
|
+
const agg = rollup(stat)
|
|
63
|
+
const keys = ['color', 'fill', 'pattern', 'shape', 'size'].filter((k) =>
|
|
64
|
+
Object.keys(aes).includes(k)
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
let groups = keys.map((k) => (d) => d[aes[k]])
|
|
68
|
+
|
|
69
|
+
return flatRollup(
|
|
70
|
+
data,
|
|
71
|
+
(v) => agg(v.map((d) => d[aes.y])),
|
|
72
|
+
(d) => d[aes.x],
|
|
73
|
+
...groups
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// export function geomBars(chart, aes) {
|
|
78
|
+
// const { x, y, fill, color, pattern } = { ...aes, ...chart.aes }
|
|
79
|
+
// return aggregate(chart.data, { x, y, fill, color, pattern })
|
|
80
|
+
// }
|
|
81
|
+
|
|
82
|
+
// export function geomLines(chart, aes) {
|
|
83
|
+
// const { x, y, color } = { ...aes, ...chart.aes }
|
|
84
|
+
// return aggregate(chart.data, { x, y, color })
|
|
85
|
+
// }
|
|
86
|
+
|
|
87
|
+
// export function geomViolin(chart, aes) {
|
|
88
|
+
// const { x, y, fill, color, pattern } = { ...aes, ...chart.aes }
|
|
89
|
+
// return { x, y, fill, color, pattern, ...opts }
|
|
90
|
+
// }
|
|
91
|
+
|
|
92
|
+
// export function geomArea(chart, aes) {
|
|
93
|
+
// const { x, y, fill, color, pattern } = { ...aes, ...chart.aes }
|
|
94
|
+
// return { x, y, fill, color, pattern, ...opts }
|
|
95
|
+
// }
|
|
96
|
+
|
|
97
|
+
// export function geomTrend(chart, aes) {
|
|
98
|
+
// const { x, y, fill, color, pattern } = { ...aes, ...chart.aes }
|
|
99
|
+
// return { x, y, fill, color, pattern, ...opts }
|
|
100
|
+
// }
|
|
101
|
+
|
|
102
|
+
// export function geomPoints(chart, aes) {
|
|
103
|
+
// const { x, y, fill, color, shape, size } = { ...aes, ...chart.aes }
|
|
104
|
+
// return { x, y, fill, color, shape, size, ...opts }
|
|
105
|
+
// }
|
package/src/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// export { aes } from './lib/aes'
|
|
2
|
+
// export { timelapse } from './lib/timelapse'
|
|
3
|
+
// export { axisTicks } from './lib/axis'
|
|
4
|
+
export { timer, elapsed } from './lib/timer'
|
|
5
|
+
// export { default as Chart } from './chart/Chart.svelte'
|
|
6
|
+
// export { default as Axis } from './chart/Axis.svelte'
|
|
7
|
+
export { default as Swatch } from './chart/Swatch.svelte'
|
|
8
|
+
// export { default as AxisTicks } from './chart/AxisTicks.svelte'
|
|
9
|
+
// export { default as AxisGrid } from './chart/AxisGrid.svelte'
|
|
10
|
+
// export { default as AxisLabels } from './chart/AxisLabels.svelte'
|
|
11
|
+
export { default as BarPlot } from './plots/BarPlot.svelte'
|
|
12
|
+
export { default as LinePlot } from './plots/LinePlot.svelte'
|
|
13
|
+
export { default as BoxPlot } from './plots/BoxPlot.svelte'
|
|
14
|
+
export { default as ViolinPlot } from './plots/ViolinPlot.svelte'
|
|
15
|
+
export { default as ScatterPlot } from './plots/ScatterPlot.svelte'
|
|
16
|
+
export * from './components/lib'
|
package/src/lib/axis.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export function axis(scale) {
|
|
2
|
+
const origin = {
|
|
3
|
+
x: scale.x.ticks
|
|
4
|
+
? scale.x(Math.max(0, Math.min(...scale.x.domain())))
|
|
5
|
+
: scale.x.range()[0],
|
|
6
|
+
y: scale.y.ticks
|
|
7
|
+
? scale.y(Math.max(0, Math.min(...scale.y.domain())))
|
|
8
|
+
: scale.y.range()[0]
|
|
9
|
+
}
|
|
10
|
+
const ticks = {
|
|
11
|
+
x: axisTicks(scale.x, { axis: 'x', origin }),
|
|
12
|
+
y: axisTicks(scale.y, { axis: 'y', origin })
|
|
13
|
+
}
|
|
14
|
+
return { origin, ticks }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function axisTicks(scale, opts) {
|
|
18
|
+
let [minRange, maxRange] = scale.range()
|
|
19
|
+
let count = Math.abs((maxRange - minRange) / 40)
|
|
20
|
+
let ticks = scale.domain()
|
|
21
|
+
let offset = 0
|
|
22
|
+
let { axis, format, origin } = {
|
|
23
|
+
axis: 'x',
|
|
24
|
+
format: (x) => x,
|
|
25
|
+
origin: { x: 0, y: 0 },
|
|
26
|
+
...opts
|
|
27
|
+
}
|
|
28
|
+
if (scale.ticks) {
|
|
29
|
+
ticks = scale.ticks(count)
|
|
30
|
+
} else {
|
|
31
|
+
offset = Math.sign(maxRange - minRange) * (scale.bandwidth() / 2)
|
|
32
|
+
count = Math.min(Math.round(count), scale.domain().length)
|
|
33
|
+
if (count < scale.domain().length) {
|
|
34
|
+
let diff = scale.domain().length - count
|
|
35
|
+
ticks = ticks.filter((d, i) => i % diff == 0)
|
|
36
|
+
}
|
|
37
|
+
// let diff = scale.domain().length - count
|
|
38
|
+
}
|
|
39
|
+
ticks = ticks
|
|
40
|
+
.map((t) => ({
|
|
41
|
+
label: format(t),
|
|
42
|
+
pos: scale(t)
|
|
43
|
+
}))
|
|
44
|
+
.map(({ label, pos }) => ({
|
|
45
|
+
label,
|
|
46
|
+
offset: { x: axis === 'x' ? offset : 0, y: axis === 'y' ? offset : 0 },
|
|
47
|
+
x: axis === 'x' ? pos : origin.x,
|
|
48
|
+
y: axis === 'y' ? pos : origin.y
|
|
49
|
+
}))
|
|
50
|
+
|
|
51
|
+
return ticks
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export class Axis {
|
|
55
|
+
constructor(name, chart, offset) {
|
|
56
|
+
this.name = ['x', 'y'].includes(name) ? name : 'x'
|
|
57
|
+
this.chart = chart
|
|
58
|
+
this.offset = offset
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
set offset(value) {
|
|
62
|
+
const [min, max] = this.chart.scale[this.name].range()
|
|
63
|
+
const otherAxis = this.name === 'x' ? 'y' : 'x'
|
|
64
|
+
const origin = this.chart.origin[otherAxis]
|
|
65
|
+
|
|
66
|
+
this.offset = value * (origin == min ? 1 : origin == max ? -1 : 0)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// get domain() {
|
|
70
|
+
// let coords =
|
|
71
|
+
// (coords[axis + '1'] =
|
|
72
|
+
// coords[axis + '2'] =
|
|
73
|
+
// origin[axis] - offset[axis])
|
|
74
|
+
// }
|
|
75
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// import { writable } from 'svelte/store'
|
|
2
|
+
import { repeatAcross } from '../lib/utils'
|
|
3
|
+
|
|
4
|
+
const palette = [
|
|
5
|
+
'#FFDE6B',
|
|
6
|
+
'#EF89EE',
|
|
7
|
+
'#F79F1E',
|
|
8
|
+
'#02B8FF',
|
|
9
|
+
'#9F84EC',
|
|
10
|
+
'#15CBC4',
|
|
11
|
+
'#0092FD',
|
|
12
|
+
'#F63A57',
|
|
13
|
+
'#A2CB39',
|
|
14
|
+
'#FF6E2F',
|
|
15
|
+
'#FEB8B9',
|
|
16
|
+
'#af7aa1',
|
|
17
|
+
'#7EFFF5'
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
export class Palette {
|
|
21
|
+
constructor(colors = palette) {
|
|
22
|
+
this.colors = colors
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
set colors(value) {
|
|
26
|
+
if (value && Array.isArray(value)) this.colors = value
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function colorBrewer(values) {
|
|
31
|
+
return repeatAcross(palette, [...new Set(values)])
|
|
32
|
+
}
|
package/src/lib/geom.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
const names = [
|
|
2
|
+
'circle',
|
|
3
|
+
'square',
|
|
4
|
+
'triangle',
|
|
5
|
+
'diamond',
|
|
6
|
+
'star',
|
|
7
|
+
'rhombus',
|
|
8
|
+
'heart'
|
|
9
|
+
]
|
|
10
|
+
const data = {
|
|
11
|
+
square: ['M', 1, 1, 'L', 9, 1, 'L', 9, 9, 'L', 1, 9, 'Z'],
|
|
12
|
+
circle: [
|
|
13
|
+
['M', 0, 5],
|
|
14
|
+
['A', 5, 5, 0, 0, 0, 10, 5],
|
|
15
|
+
['A', 5, 5, 0, 0, 0, 0, 5]
|
|
16
|
+
],
|
|
17
|
+
triangle: ['M', 5, 0, 'L', 10, 10, 'L', 0, 10, 'Z'],
|
|
18
|
+
diamond: [
|
|
19
|
+
['M', 5, 0],
|
|
20
|
+
['A', 7, 7, 0, 0, 0, 10, 5],
|
|
21
|
+
['A', 7, 7, 0, 0, 0, 5, 10],
|
|
22
|
+
['A', 7, 7, 0, 0, 0, 0, 5],
|
|
23
|
+
['A', 7, 7, 0, 0, 0, 5, 0]
|
|
24
|
+
],
|
|
25
|
+
rhombus: ['M', 0, 5, 'L', 5, 0, 'L', 10, 5, 'L', 5, 10, 'Z'],
|
|
26
|
+
heart: [
|
|
27
|
+
['M', 9, 5],
|
|
28
|
+
['A', 0.8, 0.8, 0, 0, 0, 5, 2],
|
|
29
|
+
['A', 0.8, 0.8, 0, 0, 0, 1, 5],
|
|
30
|
+
['L', 5, 9],
|
|
31
|
+
['L', 9, 5]
|
|
32
|
+
],
|
|
33
|
+
star: [
|
|
34
|
+
['M', 4.80001, 0],
|
|
35
|
+
['L', 5.92258, 3.45491],
|
|
36
|
+
['H', 9.55529],
|
|
37
|
+
['L', 6.61637, 5.59017],
|
|
38
|
+
['L', 7.73894, 9.04509],
|
|
39
|
+
['L', 4.80001, 6.90983],
|
|
40
|
+
['L', 1.86108, 9.04509],
|
|
41
|
+
['L', 2.98365, 5.59017],
|
|
42
|
+
['L', 0.0447266, 3.45491],
|
|
43
|
+
['H', 3.67744],
|
|
44
|
+
['L', 4.80001, 0],
|
|
45
|
+
['Z']
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function scaledPath(size, x) {
|
|
50
|
+
if (Array.isArray(x)) return x.map((x) => scaledPath(size, x)).join(' ')
|
|
51
|
+
return typeof size === 'number' ? x * size : x
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const shapes = names.map((name) => ({
|
|
55
|
+
name,
|
|
56
|
+
path: (s) => scaledPath(s, data[name])
|
|
57
|
+
}))
|
|
58
|
+
|
|
59
|
+
export const namedShapes = {
|
|
60
|
+
square: (s) =>
|
|
61
|
+
`M${0.1 * s} 0` +
|
|
62
|
+
`A${0.1 * s} ${0.1 * s} 0 0 0 0 ${0.1 * s}V${0.9 * s}` +
|
|
63
|
+
`A${0.1 * s} ${0.1 * s} 0 0 0 ${0.1 * s} ${s}H${0.9 * s}` +
|
|
64
|
+
`A${0.1 * s} ${0.1 * s} 0 0 0 ${s} ${0.9 * s}V${0.1 * s}` +
|
|
65
|
+
`A${0.1 * s} ${0.1 * s} 0 0 0 ${0.9 * s} 0Z`,
|
|
66
|
+
circle: (s) =>
|
|
67
|
+
`M0 ${0.5 * s}` +
|
|
68
|
+
`A${0.5 * s} ${0.5 * s} 0 0 0 ${s} ${0.5 * s}` +
|
|
69
|
+
`A${0.5 * s} ${0.5 * s} 0 0 0 0 ${0.5 * s}`,
|
|
70
|
+
diamond: (s) =>
|
|
71
|
+
`M${0.5 * s} 0` +
|
|
72
|
+
`A${0.6 * s} ${0.6 * s} 0 0 0 ${s} ${0.5 * s}` +
|
|
73
|
+
`A${0.6 * s} ${0.6 * s} 0 0 0 ${0.5 * s} ${s}` +
|
|
74
|
+
`A${0.6 * s} ${0.6 * s} 0 0 0 0 ${0.5 * s}` +
|
|
75
|
+
`A${0.6 * s} ${0.6 * s} 0 0 0 ${0.5 * s} 0`,
|
|
76
|
+
triangle: (s) =>
|
|
77
|
+
`M${0.5 * s} ${0.0866 * s}L0 ${0.9234 * s}L${s} ${0.9234 * s}Z`,
|
|
78
|
+
rhombus: (s) =>
|
|
79
|
+
`M${0.5 * s} 0` +
|
|
80
|
+
`L${s} ${0.5 * s}` +
|
|
81
|
+
`L${0.5 * s} ${s}` +
|
|
82
|
+
`L0 ${0.5 * s}Z`,
|
|
83
|
+
star: (s) =>
|
|
84
|
+
`M${0.5 * s} ${0.05 * s}` +
|
|
85
|
+
`L${0.606 * s} ${0.36 * s}` +
|
|
86
|
+
`L${s} ${0.36 * s}` +
|
|
87
|
+
`L${0.685 * s} ${0.59 * s}` +
|
|
88
|
+
`L${0.81 * s} ${0.95 * s}` +
|
|
89
|
+
`L${0.5 * s} ${0.725 * s}` +
|
|
90
|
+
`L${0.19 * s} ${0.95 * s}` +
|
|
91
|
+
`L${0.315 * s} ${0.59 * s}` +
|
|
92
|
+
`L0 ${0.36 * s}` +
|
|
93
|
+
`L${0.394 * s} ${0.36 * s}Z`,
|
|
94
|
+
heart: (s) =>
|
|
95
|
+
`M${0.9 * s} ${0.5 * s}` +
|
|
96
|
+
`A${0.08 * s} ${0.08 * s} 0 0 0 ${0.5 * s} ${0.2 * s}` +
|
|
97
|
+
`A${0.08 * s} ${0.08 * s} 0 0 0 ${0.1 * s} ${0.5 * s}` +
|
|
98
|
+
`L${0.5 * s} ${0.9 * s}` +
|
|
99
|
+
`L${0.9 * s} ${0.5 * s}`,
|
|
100
|
+
shurikan: (s) =>
|
|
101
|
+
`M${0.3 * s} ${0.1 * s}L${0.5 * s} 0L${0.7 * s} ${0.1 * s}` +
|
|
102
|
+
`A ${0.05 * s} ${0.05 * s} 0 0 0 ${0.9 * s} ${0.35 * s}` +
|
|
103
|
+
`L${s} ${0.5 * s}L${0.9 * s} ${0.7 * s}` +
|
|
104
|
+
`A ${0.05 * s} ${0.05 * s} 0 0 0 ${0.7 * s} ${0.9 * s}` +
|
|
105
|
+
`L${0.5 * s} ${s}L${0.3 * s} ${0.9 * s}` +
|
|
106
|
+
`A${0.05 * s} ${0.05 * s} 0 0 0 ${0.1 * s} ${0.7 * s}` +
|
|
107
|
+
`L0 ${0.5 * s}L${0.1 * s} ${0.3 * s}` +
|
|
108
|
+
`A${0.05 * s} ${0.05 * s} 0 0 0 ${0.3 * s} ${0.1 * s}` +
|
|
109
|
+
`M${0.4 * s} ${0.5 * s}` +
|
|
110
|
+
`A${0.1 * s} ${0.1 * s} 0 0 0 ${0.6 * s} ${0.5 * s}` +
|
|
111
|
+
`A${0.1 * s} ${0.1 * s} 0 0 0 ${0.4 * s} ${0.5 * s}`,
|
|
112
|
+
crosshair: (s) =>
|
|
113
|
+
`M${0.2 * s} ${0.5 * s}` +
|
|
114
|
+
`A${0.3 * s} ${0.3 * s} 0 0 0 ${0.8 * s} ${0.5 * s}` +
|
|
115
|
+
`A${0.3 * s} ${0.3 * s} 0 0 0 ${0.2 * s} ${0.5 * s}` +
|
|
116
|
+
`M0 ${0.5 * s}` +
|
|
117
|
+
`L${s} ${0.5 * s}` +
|
|
118
|
+
`M${0.5 * s} 0` +
|
|
119
|
+
`L${0.5 * s} ${s}`,
|
|
120
|
+
crossboats: (s) =>
|
|
121
|
+
`M0 ${0.5 * s}` +
|
|
122
|
+
`A${0.6 * s} ${0.4 * s} 0 0 0 ${s} ${0.5 * s}` +
|
|
123
|
+
`A${0.6 * s} ${0.4 * s} 0 0 0 0 ${0.5 * s}` +
|
|
124
|
+
`M${0.5 * s} 0` +
|
|
125
|
+
`A${0.4 * s} ${0.6 * s} 0 0 0 ${0.5 * s} ${s}` +
|
|
126
|
+
`A${0.4 * s} ${0.6 * s} 0 0 0 ${0.5 * s} 0`,
|
|
127
|
+
curvedrhombus: (s) =>
|
|
128
|
+
`M${0.1 * s} ${0.1 * s}` +
|
|
129
|
+
`A${0.5 * s} ${0.5 * s} 0 0 0 ${0.9 * s} ${0.1 * s}` +
|
|
130
|
+
`A${0.5 * s} ${0.5 * s} 0 0 0 ${0.9 * s} ${0.9 * s}` +
|
|
131
|
+
`A${0.5 * s} ${0.5 * s} 0 0 0 ${0.1 * s} ${0.9 * s}` +
|
|
132
|
+
`A${0.5 * s} ${0.5 * s} 0 0 0 ${0.1 * s} ${0.1 * s}`,
|
|
133
|
+
fourflags: (s) =>
|
|
134
|
+
`M${0.5 * s} ${0.3 * s}` +
|
|
135
|
+
`A${0.2 * s} ${0.1 * s} 0 0 0 ${0.5 * s} ${0.1 * s}` +
|
|
136
|
+
`L${0.5 * s} ${0.9 * s}` +
|
|
137
|
+
`M${0.5 * s} ${0.7 * s}` +
|
|
138
|
+
`A${0.2 * s} ${0.1 * s} 0 0 0 ${0.5 * s} ${0.9 * s}` +
|
|
139
|
+
`M${0.3 * s} ${0.5 * s}` +
|
|
140
|
+
`A${0.1 * s} ${0.2 * s} 0 0 0 ${0.1 * s} ${0.5 * s}` +
|
|
141
|
+
`L${0.9 * s} ${0.5 * s}` +
|
|
142
|
+
`M${0.7 * s} ${0.5 * s}` +
|
|
143
|
+
`A${0.1 * s} ${0.2 * s} 0 0 0 ${0.9 * s} ${0.5 * s}`
|
|
144
|
+
}
|