@rokkit/chart 1.0.0-next.87 → 1.0.0-next.89
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/package.json +11 -6
- package/src/Chart.svelte +67 -0
- package/src/PatternDefs.svelte +14 -0
- package/src/Symbol.svelte +17 -0
- package/src/{chart/Texture.svelte → Texture.svelte} +3 -3
- package/src/elements/Bar.svelte +2 -2
- package/src/elements/ContinuousLegend.svelte +3 -2
- package/src/elements/DefinePatterns.svelte +22 -0
- package/src/elements/DiscreteLegend.svelte +1 -1
- package/src/elements/Label.svelte +7 -5
- package/src/elements/SymbolGrid.svelte +23 -0
- package/src/elements/index.js +6 -0
- package/src/index.js +5 -15
- package/src/lib/brewer.js +17 -0
- package/src/lib/chart.js +179 -160
- package/src/lib/grid.js +68 -0
- package/src/lib/index.js +4 -0
- package/src/lib/palette.js +279 -28
- package/src/lib/plots.js +23 -0
- package/src/lib/swatch.js +24 -8
- package/src/lib/ticks.js +19 -0
- package/src/patterns/Brick.svelte +17 -0
- package/src/patterns/Circles.svelte +18 -0
- package/src/patterns/CrossHatch.svelte +14 -0
- package/src/patterns/CurvedWave.svelte +9 -0
- package/src/patterns/Dots.svelte +19 -0
- package/src/patterns/OutlineCircles.svelte +15 -0
- package/src/patterns/Texture.svelte +20 -0
- package/src/patterns/Tile.svelte +17 -0
- package/src/patterns/Triangles.svelte +15 -0
- package/src/patterns/Waves.svelte +13 -0
- package/src/patterns/constants.js +43 -0
- package/src/patterns/index.js +13 -0
- package/src/patterns/paths/NamedPattern.svelte +12 -0
- package/src/patterns/paths/constants.js +7 -0
- package/src/patterns/templates/Circles.svelte +18 -0
- package/src/patterns/templates/Lines.svelte +17 -0
- package/src/patterns/templates/Path.svelte +17 -0
- package/src/patterns/templates/index.js +3 -0
- package/src/plots/Plot.svelte +36 -21
- package/src/plots/index.js +1 -10
- package/src/symbols/RoundedSquare.svelte +27 -0
- package/src/symbols/Shape.svelte +31 -0
- package/src/symbols/constants/index.js +7 -0
- package/src/symbols/index.js +9 -0
- package/src/chart/Axis.svelte +0 -81
- package/src/chart/AxisGrid.svelte +0 -22
- package/src/chart/Chart.svelte +0 -40
- package/src/chart/FacetGrid.svelte +0 -49
- package/src/chart/Legend.svelte +0 -16
- package/src/chart/Swatch.svelte +0 -84
- package/src/chart/SwatchButton.svelte +0 -29
- package/src/chart/SwatchGrid.svelte +0 -53
- package/src/chart/TexturedShape.svelte +0 -20
- package/src/chart/TimelapseChart.svelte +0 -90
- package/src/chart/Timer.svelte +0 -27
- package/src/elements/Tooltip.svelte +0 -19
- package/src/lib/axis.js +0 -77
- package/src/lib/color.js +0 -55
- package/src/lib/constants.js +0 -41
- package/src/lib/funnel.js +0 -230
- package/src/lib/geom.js +0 -99
- package/src/lib/heatmap.js +0 -68
- package/src/lib/lookup.js +0 -29
- package/src/lib/pattern.js +0 -182
- package/src/lib/rollup.js +0 -49
- package/src/lib/shape.js +0 -46
- package/src/lib/store.js +0 -63
- package/src/lib/summary.js +0 -28
- package/src/lib/theme.js +0 -31
- package/src/lib/utils.js +0 -158
- package/src/plots/BarPlot.svelte +0 -51
- package/src/plots/BarPlot2.svelte +0 -34
- package/src/plots/BoxPlot.svelte +0 -54
- package/src/plots/FunnelPlot.svelte +0 -26
- package/src/plots/HeatMapCalendar.svelte +0 -121
- package/src/plots/LinePlot.svelte +0 -51
- package/src/plots/RankBarPlot.svelte +0 -38
- package/src/plots/ScatterPlot.svelte +0 -28
- package/src/plots/ViolinPlot.svelte +0 -10
package/src/chart/Timer.svelte
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import { createEventDispatcher } from 'svelte'
|
|
3
|
-
import { elapsed, timer } from '@rokkit/stores'
|
|
4
|
-
|
|
5
|
-
const dispatch = createEventDispatcher()
|
|
6
|
-
|
|
7
|
-
export let isEnabled = false
|
|
8
|
-
export let currentKeyframe = 0
|
|
9
|
-
export let keyframeCount = 0
|
|
10
|
-
export let duration = 1000
|
|
11
|
-
|
|
12
|
-
$: if (isEnabled) currentKeyframe = Math.floor($elapsed / duration)
|
|
13
|
-
$: if (currentKeyframe === keyframeCount) dispatch('end')
|
|
14
|
-
$: isEnabled && currentKeyframe < keyframeCount ? timer.start() : timer.stop()
|
|
15
|
-
|
|
16
|
-
function onReset() {
|
|
17
|
-
timer.reset()
|
|
18
|
-
isEnabled = false
|
|
19
|
-
currentKeyframe = 0
|
|
20
|
-
}
|
|
21
|
-
</script>
|
|
22
|
-
|
|
23
|
-
<div class="timer flex flex-row gap-2">
|
|
24
|
-
<button on:click|preventDefault={() => (isEnabled = true)}>play</button>
|
|
25
|
-
<button on:click|preventDefault={() => (isEnabled = false)}>pause</button>
|
|
26
|
-
<button on:click|preventDefault={onReset}>reset</button>
|
|
27
|
-
</div>
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
export let left
|
|
3
|
-
export let top
|
|
4
|
-
export let hidden = true
|
|
5
|
-
</script>
|
|
6
|
-
|
|
7
|
-
<div class="tooltip {$$props.class}" {hidden} style="--top: {top};--left: {left}">
|
|
8
|
-
<slot />
|
|
9
|
-
</div>
|
|
10
|
-
|
|
11
|
-
<style lang="postcss">
|
|
12
|
-
.tooltip {
|
|
13
|
-
padding: 1em 0.5em;
|
|
14
|
-
left: var(--left);
|
|
15
|
-
top: var(--top);
|
|
16
|
-
position: absolute;
|
|
17
|
-
z-index: 10;
|
|
18
|
-
}
|
|
19
|
-
</style>
|
package/src/lib/axis.js
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
function getOrigin(scale, axis) {
|
|
2
|
-
const origin = scale[axis].ticks
|
|
3
|
-
? scale(Math.max(0, Math.min(...scale[axis].domain())))
|
|
4
|
-
: scale[axis].range()[0]
|
|
5
|
-
return origin
|
|
6
|
-
}
|
|
7
|
-
export function axis(scale) {
|
|
8
|
-
const origin = {
|
|
9
|
-
x: getOrigin(scale, 'x'),
|
|
10
|
-
y: getOrigin(scale, 'y')
|
|
11
|
-
}
|
|
12
|
-
const ticks = {
|
|
13
|
-
x: axisTicks(scale.x, { axis: 'x', origin }),
|
|
14
|
-
y: axisTicks(scale.y, { axis: 'y', origin })
|
|
15
|
-
}
|
|
16
|
-
return { origin, ticks }
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function axisTicks(scale, opts) {
|
|
20
|
-
let [minRange, maxRange] = scale.range()
|
|
21
|
-
let count = Math.abs((maxRange - minRange) / 40)
|
|
22
|
-
let ticks = scale.domain()
|
|
23
|
-
let offset = 0
|
|
24
|
-
let { axis, format, origin } = {
|
|
25
|
-
axis: 'x',
|
|
26
|
-
format: (x) => x,
|
|
27
|
-
origin: { x: 0, y: 0 },
|
|
28
|
-
...opts
|
|
29
|
-
}
|
|
30
|
-
if (scale.ticks) {
|
|
31
|
-
ticks = scale.ticks(count)
|
|
32
|
-
} else {
|
|
33
|
-
offset = Math.sign(maxRange - minRange) * (scale.bandwidth() / 2)
|
|
34
|
-
count = Math.min(Math.round(count), scale.domain().length)
|
|
35
|
-
if (count < scale.domain().length) {
|
|
36
|
-
let diff = scale.domain().length - count
|
|
37
|
-
ticks = ticks.filter((d, i) => i % diff == 0)
|
|
38
|
-
}
|
|
39
|
-
// let diff = scale.domain().length - count
|
|
40
|
-
}
|
|
41
|
-
ticks = ticks
|
|
42
|
-
.map((t) => ({
|
|
43
|
-
label: format(t),
|
|
44
|
-
pos: scale(t)
|
|
45
|
-
}))
|
|
46
|
-
.map(({ label, pos }) => ({
|
|
47
|
-
label,
|
|
48
|
-
offset: { x: axis === 'x' ? offset : 0, y: axis === 'y' ? offset : 0 },
|
|
49
|
-
x: axis === 'x' ? pos : origin.x,
|
|
50
|
-
y: axis === 'y' ? pos : origin.y
|
|
51
|
-
}))
|
|
52
|
-
|
|
53
|
-
return ticks
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export class Axis {
|
|
57
|
-
constructor(name, chart, offset) {
|
|
58
|
-
this.name = ['x', 'y'].includes(name) ? name : 'x'
|
|
59
|
-
this.chart = chart
|
|
60
|
-
this.offset = offset
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
set offset(value) {
|
|
64
|
-
const [min, max] = this.chart.scale[this.name].range()
|
|
65
|
-
const otherAxis = this.name === 'x' ? 'y' : 'x'
|
|
66
|
-
const origin = this.chart.origin[otherAxis]
|
|
67
|
-
|
|
68
|
-
this.offset = value * (origin == min ? 1 : origin == max ? -1 : 0)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// get domain() {
|
|
72
|
-
// let coords =
|
|
73
|
-
// (coords[axis + '1'] =
|
|
74
|
-
// coords[axis + '2'] =
|
|
75
|
-
// origin[axis] - offset[axis])
|
|
76
|
-
// }
|
|
77
|
-
}
|
package/src/lib/color.js
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { palette } from './constants'
|
|
2
|
-
|
|
3
|
-
export class ColorBrewer {
|
|
4
|
-
constructor() {
|
|
5
|
-
this.colors = ['blue', 'pink', 'teal', 'indigo', 'purple', 'amber', 'rose']
|
|
6
|
-
this.palette = palette
|
|
7
|
-
this.grayscale = this.palette['trueGray']
|
|
8
|
-
this.fill = 100
|
|
9
|
-
this.stroke = 600
|
|
10
|
-
this.contrast = 600
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
dark() {
|
|
14
|
-
this.fill = 500
|
|
15
|
-
this.stroke = 700
|
|
16
|
-
this.contrast = 100
|
|
17
|
-
return this
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
mix(fill, stroke, contrast) {
|
|
21
|
-
this.fill = Object.keys(this.grayscale).includes(fill) ? fill : this.fill
|
|
22
|
-
this.stroke = Object.keys(this.grayscale).includes(stroke) ? stroke : this.stroke
|
|
23
|
-
this.contrast = Object.keys(this.grayscale).includes(contrast) ? contrast : this.contrast
|
|
24
|
-
|
|
25
|
-
return this
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
swatch(colors) {
|
|
29
|
-
this.palette = colors
|
|
30
|
-
return this
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
filter(colors) {
|
|
34
|
-
this.colors = colors
|
|
35
|
-
return this
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
gray() {
|
|
39
|
-
return {
|
|
40
|
-
fill: this.grayscale[this.fill],
|
|
41
|
-
stroke: this.grayscale[this.stroke],
|
|
42
|
-
contrast: this.grayscale[this.contrast]
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
brew() {
|
|
47
|
-
const palette = this.colors.map((color) => ({
|
|
48
|
-
fill: this.palette[color][this.fill],
|
|
49
|
-
stroke: this.palette[color][this.stroke],
|
|
50
|
-
contrast: this.palette[color][this.contrast]
|
|
51
|
-
}))
|
|
52
|
-
|
|
53
|
-
return palette
|
|
54
|
-
}
|
|
55
|
-
}
|
package/src/lib/constants.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import patterns__ from './patterns.json'
|
|
2
|
-
import __palette from './palette.json'
|
|
3
|
-
|
|
4
|
-
export const __patterns__ = patterns__
|
|
5
|
-
export const palette = __palette
|
|
6
|
-
export const __muted__ = {
|
|
7
|
-
color: '#eeeeee',
|
|
8
|
-
fill: 'empty',
|
|
9
|
-
shape: 'circle'
|
|
10
|
-
}
|
|
11
|
-
export const __colors__ = [
|
|
12
|
-
'#FFDE6B',
|
|
13
|
-
'#EF89EE',
|
|
14
|
-
'#F79F1E',
|
|
15
|
-
'#02B8FF',
|
|
16
|
-
'#9F84EC',
|
|
17
|
-
'#15CBC4',
|
|
18
|
-
'#0092FD',
|
|
19
|
-
'#F63A57',
|
|
20
|
-
'#A2CB39',
|
|
21
|
-
'#FF6E2F',
|
|
22
|
-
'#FEB8B9',
|
|
23
|
-
'#af7aa1',
|
|
24
|
-
'#7EFFF5'
|
|
25
|
-
]
|
|
26
|
-
|
|
27
|
-
export const colors = [
|
|
28
|
-
'#FFDE6B',
|
|
29
|
-
'#EF89EE',
|
|
30
|
-
'#F79F1E',
|
|
31
|
-
'#02B8FF',
|
|
32
|
-
'#9F84EC',
|
|
33
|
-
'#15CBC4',
|
|
34
|
-
'#0092FD',
|
|
35
|
-
'#F63A57',
|
|
36
|
-
'#A2CB39',
|
|
37
|
-
'#FF6E2F',
|
|
38
|
-
'#FEB8B9',
|
|
39
|
-
'#af7aa1',
|
|
40
|
-
'#7EFFF5'
|
|
41
|
-
]
|
package/src/lib/funnel.js
DELETED
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
import { max, cumsum } from 'd3-array'
|
|
2
|
-
import { nest } from 'd3-collection'
|
|
3
|
-
import { flatten } from 'ramda'
|
|
4
|
-
import { area, curveBasis, curveBumpX, curveBumpY } from 'd3-shape'
|
|
5
|
-
import { scaleLinear } from 'd3-scale'
|
|
6
|
-
import { summarize } from './summary'
|
|
7
|
-
|
|
8
|
-
export function getUniques(input, aes) {
|
|
9
|
-
const attrs = ['x', 'y', 'fill']
|
|
10
|
-
let values = {}
|
|
11
|
-
|
|
12
|
-
attrs.map((attr) => {
|
|
13
|
-
if (attr in aes) {
|
|
14
|
-
values[attr] = [...new Set(input.map((d) => d[aes[attr]]))]
|
|
15
|
-
}
|
|
16
|
-
})
|
|
17
|
-
return values
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function fillMissing(fill, rows, key, aes) {
|
|
21
|
-
const filled = fill.map((f) => {
|
|
22
|
-
let matched = rows.filter((r) => r[aes.fill] === f)
|
|
23
|
-
if (matched.length == 0) {
|
|
24
|
-
let row = {}
|
|
25
|
-
row[key] = rows[0][key]
|
|
26
|
-
row[aes.fill] = f
|
|
27
|
-
row[aes.stat] = 0
|
|
28
|
-
return row
|
|
29
|
-
} else {
|
|
30
|
-
return matched[0]
|
|
31
|
-
}
|
|
32
|
-
})
|
|
33
|
-
return filled
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Determines if the layout is vertical based on the given parameters.
|
|
38
|
-
* @param {Object} uniques - The unique values.
|
|
39
|
-
* @param {Object} aes - The aes object.
|
|
40
|
-
* @returns {boolean} - Returns true if the layout is vertical, false otherwise.
|
|
41
|
-
*/
|
|
42
|
-
function determineLayout(uniques, aes) {
|
|
43
|
-
let vertical = 'y' in uniques && uniques.y.some(isNaN)
|
|
44
|
-
const horizontal = 'x' in uniques && uniques.x.some(isNaN)
|
|
45
|
-
|
|
46
|
-
if (horizontal && vertical) {
|
|
47
|
-
if ((aes.stat || 'count') === 'count') {
|
|
48
|
-
vertical = false
|
|
49
|
-
console.warn('Assuming horizontal layout because stat is count')
|
|
50
|
-
} else {
|
|
51
|
-
console.error('Cannot plot without at least one axis having numeric values')
|
|
52
|
-
return null
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return vertical
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Converts to phases.
|
|
60
|
-
* @param {Array} input - The input data.
|
|
61
|
-
* @param {Object} aes - The aes object.
|
|
62
|
-
* @returns {Object} - The phases, uniques, and vertical properties.
|
|
63
|
-
*/
|
|
64
|
-
export function convertToPhases(input, aes) {
|
|
65
|
-
const uniques = getUniques(input, aes)
|
|
66
|
-
const vertical = determineLayout(uniques, aes)
|
|
67
|
-
if (vertical === null) return { uniques, vertical }
|
|
68
|
-
|
|
69
|
-
const key = vertical ? aes.y : aes.x
|
|
70
|
-
const value = vertical ? aes.x : aes.y
|
|
71
|
-
|
|
72
|
-
let by = [key]
|
|
73
|
-
if ('fill' in aes) by.push(aes.fill)
|
|
74
|
-
|
|
75
|
-
const summary = summarize(input, by, value, aes.stat)
|
|
76
|
-
const phases = nest()
|
|
77
|
-
.key((d) => d[key])
|
|
78
|
-
.rollup((rows) => ('fill' in aes ? fillMissing(uniques.fill, rows, key, aes) : rows))
|
|
79
|
-
.entries(summary)
|
|
80
|
-
|
|
81
|
-
return { phases, uniques, vertical }
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export function getScales(input, width, height) {
|
|
85
|
-
let scale
|
|
86
|
-
if (input.vertical) {
|
|
87
|
-
scale = {
|
|
88
|
-
x: scaleLinear()
|
|
89
|
-
.domain([-input.domain * 1.4, input.domain * 1.4])
|
|
90
|
-
.range([0, width]),
|
|
91
|
-
y: scaleLinear().domain([0, input.uniques.y.length]).range([0, height])
|
|
92
|
-
}
|
|
93
|
-
} else {
|
|
94
|
-
scale = {
|
|
95
|
-
x: scaleLinear().domain([0, input.uniques.x.length]).range([0, width]),
|
|
96
|
-
y: scaleLinear()
|
|
97
|
-
.domain([-input.domain, input.domain * 1.4])
|
|
98
|
-
.range([height - 20, 0])
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return scale
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function getLabels(data) {
|
|
105
|
-
let key = data.vertical ? 'y' : 'x'
|
|
106
|
-
let opp = key === 'x' ? 'y' : 'x'
|
|
107
|
-
|
|
108
|
-
let labels = data.uniques[key].map((label, index) => {
|
|
109
|
-
let row = { label }
|
|
110
|
-
let domain = data.scale[opp].domain()
|
|
111
|
-
row[`${key}1`] = row[`${key}2`] = data.scale[key](index + 1)
|
|
112
|
-
row[`${opp}1`] = data.scale[opp](domain[0])
|
|
113
|
-
row[`${opp}2`] = data.scale[opp](domain[1])
|
|
114
|
-
row[opp] = 20
|
|
115
|
-
row[key] = data.scale[key](index) + 20
|
|
116
|
-
return row
|
|
117
|
-
})
|
|
118
|
-
return labels
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export function getPaths(vertical, scale, curve) {
|
|
122
|
-
return vertical
|
|
123
|
-
? area()
|
|
124
|
-
.x0((d) => scale.x(d.x0))
|
|
125
|
-
.x1((d) => scale.x(d.x1))
|
|
126
|
-
.y((d) => scale.y(d.y))
|
|
127
|
-
.curve(curve)
|
|
128
|
-
: area()
|
|
129
|
-
.x((d) => scale.x(d.x))
|
|
130
|
-
.y0((d) => scale.y(d.y0))
|
|
131
|
-
.y1((d) => scale.y(d.y1))
|
|
132
|
-
.curve(curve)
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Mirrors the input based on the provided aesthetic mappings.
|
|
137
|
-
*
|
|
138
|
-
* @param {Object} input - The input data with phases and uniques.
|
|
139
|
-
* @param {Object} aes - The aesthetic mappings.
|
|
140
|
-
* @returns {Object} The mirrored input with updated stats and domain.
|
|
141
|
-
*/
|
|
142
|
-
export function mirror(input, aes) {
|
|
143
|
-
const domain = calculateDomain(input)
|
|
144
|
-
const stats = calculateStats(input, aes, domain)
|
|
145
|
-
|
|
146
|
-
return { ...input, stats, domain }
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Calculates the domain for the mirror operation.
|
|
151
|
-
*
|
|
152
|
-
* @param {Object} input - The input data with phases.
|
|
153
|
-
* @returns {number} The calculated domain.
|
|
154
|
-
*/
|
|
155
|
-
function calculateDomain(input) {
|
|
156
|
-
return input.phases.reduce((maxDomain, phase) => {
|
|
157
|
-
const stat = cumsum(phase.value.map((row) => row[aes.stat]))
|
|
158
|
-
const midpoint = max(stat) / 2
|
|
159
|
-
return Math.max(maxDomain, midpoint)
|
|
160
|
-
}, 0)
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Calculates the stats for the mirror operation.
|
|
165
|
-
*
|
|
166
|
-
* @param {Object} input - The input data with phases and uniques.
|
|
167
|
-
* @param {Object} aes - The aesthetic mappings.
|
|
168
|
-
* @param {number} domain - The domain for the mirror operation.
|
|
169
|
-
* @returns {Array} The calculated stats.
|
|
170
|
-
*/
|
|
171
|
-
function calculateStats(input, aes, domain) {
|
|
172
|
-
return input.phases.map((phase) => {
|
|
173
|
-
const stat = cumsum(phase.value.map((row) => row[aes.stat]))
|
|
174
|
-
const midpoint = max(stat) / 2
|
|
175
|
-
|
|
176
|
-
phase.value = phase.value.map((row, index) => {
|
|
177
|
-
const position = calculatePosition(input, aes, row, stat, index, midpoint)
|
|
178
|
-
return { ...row, ...position }
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
return phase
|
|
182
|
-
})
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Calculates the position for the mirror operation.
|
|
187
|
-
*
|
|
188
|
-
* @param {Object} input - The input data with uniques.
|
|
189
|
-
* @param {Object} aes - The aesthetic mappings.
|
|
190
|
-
* @param {Object} row - The current row.
|
|
191
|
-
* @param {Array} stat - The stat data.
|
|
192
|
-
* @param {number} index - The current index.
|
|
193
|
-
* @param {number} midpoint - The midpoint for the mirror operation.
|
|
194
|
-
* @returns {Object} The calculated position.
|
|
195
|
-
*/
|
|
196
|
-
function calculatePosition(input, aes, row, stat, index, midpoint) {
|
|
197
|
-
const axis = input.vertical ? 'y' : 'x'
|
|
198
|
-
const oppositeAxis = input.vertical ? 'x' : 'y'
|
|
199
|
-
const axisValue = input.uniques[axis].indexOf(row[aes[axis]])
|
|
200
|
-
const position1 = stat[index] - midpoint
|
|
201
|
-
const position0 = position1 - row[aes.stat]
|
|
202
|
-
|
|
203
|
-
return {
|
|
204
|
-
[axis]: axisValue,
|
|
205
|
-
[`${oppositeAxis}1`]: position1,
|
|
206
|
-
[`${oppositeAxis}0`]: position0
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
export function funnel(input, aes, width, height) {
|
|
211
|
-
let data = convertToPhases(input, aes)
|
|
212
|
-
data = mirror(data, aes)
|
|
213
|
-
const curve = aes.curve === 'basis' ? curveBasis : data.vertical ? curveBumpY : curveBumpX
|
|
214
|
-
|
|
215
|
-
if ('fill' in aes) {
|
|
216
|
-
let stats = flatten(data.stats.map((phase) => phase.value))
|
|
217
|
-
|
|
218
|
-
data.stats = nest()
|
|
219
|
-
.key((d) => d[aes.fill])
|
|
220
|
-
.rollup((rows) => {
|
|
221
|
-
let last = data.vertical ? { y: rows.length } : { x: rows.length }
|
|
222
|
-
return [...rows, { ...rows[rows.length - 1], ...last }]
|
|
223
|
-
})
|
|
224
|
-
.entries(stats)
|
|
225
|
-
}
|
|
226
|
-
data.scale = getScales(data, width, height)
|
|
227
|
-
data.path = getPaths(data.vertical, data.scale, curve)
|
|
228
|
-
data.labels = getLabels(data)
|
|
229
|
-
return data
|
|
230
|
-
}
|
package/src/lib/geom.js
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { sum, min, max, mean, mode, median, deviation, variance, flatRollup } from 'd3-array'
|
|
2
|
-
|
|
3
|
-
const summaries = {
|
|
4
|
-
identity: (value) => value,
|
|
5
|
-
count: (values) => values.length,
|
|
6
|
-
sum: (values) => sum(values),
|
|
7
|
-
min: (values) => min(values),
|
|
8
|
-
max: (values) => max(values),
|
|
9
|
-
mean: (values) => mean(values),
|
|
10
|
-
median: (values) => median(values),
|
|
11
|
-
mode: (values) => mode(values),
|
|
12
|
-
variance: (values) => variance(values),
|
|
13
|
-
deviation: (values) => deviation(values)
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Returns an aggregator function for an input string or function.
|
|
18
|
-
*
|
|
19
|
-
* @param {string|function} stat
|
|
20
|
-
* @returns
|
|
21
|
-
*/
|
|
22
|
-
export function rollup(stat) {
|
|
23
|
-
if (typeof stat === 'function') return stat
|
|
24
|
-
if (typeof stat !== 'string') throw new TypeError('stat must be a string or function')
|
|
25
|
-
if (!(stat in summaries)) throw new TypeError('Unknown stat: ' + stat)
|
|
26
|
-
|
|
27
|
-
return summaries[stat]
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Aesthetics for a chart.
|
|
32
|
-
*
|
|
33
|
-
* @typedef Aesthetics
|
|
34
|
-
* @property {string} x
|
|
35
|
-
* @property {string} y
|
|
36
|
-
* @property {string} [fill]
|
|
37
|
-
* @property {string} [size]
|
|
38
|
-
* @property {string} [color]
|
|
39
|
-
* @property {string} [shape]
|
|
40
|
-
* @property {string} [pattern]
|
|
41
|
-
*/
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
*
|
|
45
|
-
* @param {Array<any>} data
|
|
46
|
-
* @param {Aesthetics} aes
|
|
47
|
-
* @param {function|string} stat
|
|
48
|
-
* @returns
|
|
49
|
-
*/
|
|
50
|
-
export function aggregate(data, aes, stat = 'identity') {
|
|
51
|
-
const agg = rollup(stat)
|
|
52
|
-
const keys = ['color', 'fill', 'pattern', 'shape', 'size'].filter((k) =>
|
|
53
|
-
Object.keys(aes).includes(k)
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
let groups = keys.map((k) => (d) => d[aes[k]])
|
|
57
|
-
|
|
58
|
-
return flatRollup(
|
|
59
|
-
data,
|
|
60
|
-
(v) => agg(v.map((d) => d[aes.y])),
|
|
61
|
-
(d) => d[aes.x],
|
|
62
|
-
...groups
|
|
63
|
-
)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// export function geomBars(chart, aes) {
|
|
67
|
-
// const { x, y, fill, color, pattern } = { ...aes, ...chart.aes }
|
|
68
|
-
// return aggregate(chart.data, { x, y, fill, color, pattern })
|
|
69
|
-
// }
|
|
70
|
-
|
|
71
|
-
// export function geomLines(chart, aes) {
|
|
72
|
-
// const { x, y, color } = { ...aes, ...chart.aes }
|
|
73
|
-
// return aggregate(chart.data, { x, y, color })
|
|
74
|
-
// }
|
|
75
|
-
|
|
76
|
-
// export function geomViolin(chart, aes) {
|
|
77
|
-
// const { x, y, fill, color, pattern } = { ...aes, ...chart.aes }
|
|
78
|
-
// return { x, y, fill, color, pattern, ...opts }
|
|
79
|
-
// }
|
|
80
|
-
|
|
81
|
-
// export function geomArea(chart, aes) {
|
|
82
|
-
// const { x, y, fill, color, pattern } = { ...aes, ...chart.aes }
|
|
83
|
-
// return { x, y, fill, color, pattern, ...opts }
|
|
84
|
-
// }
|
|
85
|
-
|
|
86
|
-
// export function geomTrend(chart, aes) {
|
|
87
|
-
// const { x, y, fill, color, pattern } = { ...aes, ...chart.aes }
|
|
88
|
-
// return { x, y, fill, color, pattern, ...opts }
|
|
89
|
-
// }
|
|
90
|
-
|
|
91
|
-
// export function geomPoints(chart, aes) {
|
|
92
|
-
// const { x, y, fill, color, shape, size } = { ...aes, ...chart.aes }
|
|
93
|
-
// return { x, y, fill, color, shape, size, ...opts }
|
|
94
|
-
// }
|
|
95
|
-
|
|
96
|
-
// export function violin(data, mapping) {}
|
|
97
|
-
// export function bar(data, mapping) {}
|
|
98
|
-
// export function scatter(data, mapping) {}
|
|
99
|
-
// export function line(data, mapping) {}
|
package/src/lib/heatmap.js
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isSunday,
|
|
3
|
-
addDays,
|
|
4
|
-
subMonths,
|
|
5
|
-
startOfMonth,
|
|
6
|
-
format,
|
|
7
|
-
previousSunday,
|
|
8
|
-
differenceInDays,
|
|
9
|
-
differenceInWeeks,
|
|
10
|
-
endOfWeek
|
|
11
|
-
} from 'date-fns'
|
|
12
|
-
import { nest } from 'd3-collection'
|
|
13
|
-
// import { group } from 'd3-array'
|
|
14
|
-
|
|
15
|
-
const DATE_FORMAT = 'yyyy-MM-dd'
|
|
16
|
-
const weekdays = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']
|
|
17
|
-
|
|
18
|
-
export function summarize(data, dateField = 'date', valueField) {
|
|
19
|
-
// const summary = group(data, d => d[dateField])
|
|
20
|
-
const nested = nest()
|
|
21
|
-
.key((d) => format(new Date(d[dateField]), DATE_FORMAT))
|
|
22
|
-
.rollup((d) =>
|
|
23
|
-
valueField ? d.map((value) => value[valueField]).reduce((a, b) => a + b, 0) : d.length
|
|
24
|
-
)
|
|
25
|
-
.entries(data)
|
|
26
|
-
|
|
27
|
-
return nested.reduce((obj, item) => ((obj[item.key] = item.value), obj), {})
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function heatmap(data, numberOfMonths) {
|
|
31
|
-
const today = new Date()
|
|
32
|
-
const firstDay = getFirstDay(numberOfMonths, today)
|
|
33
|
-
const months = {}
|
|
34
|
-
const grid = generateGrid(firstDay, today).map((d) => ({
|
|
35
|
-
...d,
|
|
36
|
-
value: d.date in data ? data[d.date] : 0
|
|
37
|
-
}))
|
|
38
|
-
|
|
39
|
-
grid.map((d) => {
|
|
40
|
-
const month = format(endOfWeek(new Date(d.date)), 'MMM')
|
|
41
|
-
|
|
42
|
-
if (!(month in months)) {
|
|
43
|
-
months[month] = d.x
|
|
44
|
-
}
|
|
45
|
-
})
|
|
46
|
-
return {
|
|
47
|
-
grid,
|
|
48
|
-
months,
|
|
49
|
-
weekdays,
|
|
50
|
-
numberOfWeeks: differenceInWeeks(today, firstDay) + 1
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function getFirstDay(months, lastDay = new Date()) {
|
|
55
|
-
const firstDay = subMonths(startOfMonth(lastDay), months)
|
|
56
|
-
return isSunday(firstDay) ? firstDay : previousSunday(firstDay)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function generateGrid(firstDay, lastDay) {
|
|
60
|
-
const days = differenceInDays(lastDay, firstDay) + 1
|
|
61
|
-
|
|
62
|
-
const grid = [...Array(days).keys()].map((day) => ({
|
|
63
|
-
x: Math.floor(day / 7),
|
|
64
|
-
y: day % 7,
|
|
65
|
-
date: format(addDays(firstDay, day), DATE_FORMAT)
|
|
66
|
-
}))
|
|
67
|
-
return grid
|
|
68
|
-
}
|
package/src/lib/lookup.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { writable } from 'svelte/store'
|
|
2
|
-
|
|
3
|
-
// import { __patterns__, __colors__, __shapes__ } from './constants'
|
|
4
|
-
|
|
5
|
-
export const swatchStore = writable({})
|
|
6
|
-
|
|
7
|
-
// export function swatch(colors, patterns, shapes, defaults) {
|
|
8
|
-
// const limit = min([colors.length, patterns.length, shapes.length])
|
|
9
|
-
|
|
10
|
-
// swatchStore.set({
|
|
11
|
-
// colors: colors.slice(0, limit),
|
|
12
|
-
// patterns: patterns.slice(0, limit),
|
|
13
|
-
// shapes: shapes.slice(0, limit),
|
|
14
|
-
// defaults: {
|
|
15
|
-
// color: '#eeeeee',
|
|
16
|
-
// shape: __shapes__.circle,
|
|
17
|
-
// pattern: __patterns__.empty,
|
|
18
|
-
// ...defaults
|
|
19
|
-
// }
|
|
20
|
-
// })
|
|
21
|
-
// }
|
|
22
|
-
|
|
23
|
-
export function spread(values, across, filler) {
|
|
24
|
-
const unique = [...new Set(values)]
|
|
25
|
-
const lookup = unique.map((k, i) => ({
|
|
26
|
-
[k]: i < across.length ? across[i] : filler
|
|
27
|
-
}))
|
|
28
|
-
return (k) => lookup[k]
|
|
29
|
-
}
|