@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.
Files changed (80) hide show
  1. package/package.json +11 -6
  2. package/src/Chart.svelte +67 -0
  3. package/src/PatternDefs.svelte +14 -0
  4. package/src/Symbol.svelte +17 -0
  5. package/src/{chart/Texture.svelte → Texture.svelte} +3 -3
  6. package/src/elements/Bar.svelte +2 -2
  7. package/src/elements/ContinuousLegend.svelte +3 -2
  8. package/src/elements/DefinePatterns.svelte +22 -0
  9. package/src/elements/DiscreteLegend.svelte +1 -1
  10. package/src/elements/Label.svelte +7 -5
  11. package/src/elements/SymbolGrid.svelte +23 -0
  12. package/src/elements/index.js +6 -0
  13. package/src/index.js +5 -15
  14. package/src/lib/brewer.js +17 -0
  15. package/src/lib/chart.js +179 -160
  16. package/src/lib/grid.js +68 -0
  17. package/src/lib/index.js +4 -0
  18. package/src/lib/palette.js +279 -28
  19. package/src/lib/plots.js +23 -0
  20. package/src/lib/swatch.js +24 -8
  21. package/src/lib/ticks.js +19 -0
  22. package/src/patterns/Brick.svelte +17 -0
  23. package/src/patterns/Circles.svelte +18 -0
  24. package/src/patterns/CrossHatch.svelte +14 -0
  25. package/src/patterns/CurvedWave.svelte +9 -0
  26. package/src/patterns/Dots.svelte +19 -0
  27. package/src/patterns/OutlineCircles.svelte +15 -0
  28. package/src/patterns/Texture.svelte +20 -0
  29. package/src/patterns/Tile.svelte +17 -0
  30. package/src/patterns/Triangles.svelte +15 -0
  31. package/src/patterns/Waves.svelte +13 -0
  32. package/src/patterns/constants.js +43 -0
  33. package/src/patterns/index.js +13 -0
  34. package/src/patterns/paths/NamedPattern.svelte +12 -0
  35. package/src/patterns/paths/constants.js +7 -0
  36. package/src/patterns/templates/Circles.svelte +18 -0
  37. package/src/patterns/templates/Lines.svelte +17 -0
  38. package/src/patterns/templates/Path.svelte +17 -0
  39. package/src/patterns/templates/index.js +3 -0
  40. package/src/plots/Plot.svelte +36 -21
  41. package/src/plots/index.js +1 -10
  42. package/src/symbols/RoundedSquare.svelte +27 -0
  43. package/src/symbols/Shape.svelte +31 -0
  44. package/src/symbols/constants/index.js +7 -0
  45. package/src/symbols/index.js +9 -0
  46. package/src/chart/Axis.svelte +0 -81
  47. package/src/chart/AxisGrid.svelte +0 -22
  48. package/src/chart/Chart.svelte +0 -40
  49. package/src/chart/FacetGrid.svelte +0 -49
  50. package/src/chart/Legend.svelte +0 -16
  51. package/src/chart/Swatch.svelte +0 -84
  52. package/src/chart/SwatchButton.svelte +0 -29
  53. package/src/chart/SwatchGrid.svelte +0 -53
  54. package/src/chart/TexturedShape.svelte +0 -20
  55. package/src/chart/TimelapseChart.svelte +0 -90
  56. package/src/chart/Timer.svelte +0 -27
  57. package/src/elements/Tooltip.svelte +0 -19
  58. package/src/lib/axis.js +0 -77
  59. package/src/lib/color.js +0 -55
  60. package/src/lib/constants.js +0 -41
  61. package/src/lib/funnel.js +0 -230
  62. package/src/lib/geom.js +0 -99
  63. package/src/lib/heatmap.js +0 -68
  64. package/src/lib/lookup.js +0 -29
  65. package/src/lib/pattern.js +0 -182
  66. package/src/lib/rollup.js +0 -49
  67. package/src/lib/shape.js +0 -46
  68. package/src/lib/store.js +0 -63
  69. package/src/lib/summary.js +0 -28
  70. package/src/lib/theme.js +0 -31
  71. package/src/lib/utils.js +0 -158
  72. package/src/plots/BarPlot.svelte +0 -51
  73. package/src/plots/BarPlot2.svelte +0 -34
  74. package/src/plots/BoxPlot.svelte +0 -54
  75. package/src/plots/FunnelPlot.svelte +0 -26
  76. package/src/plots/HeatMapCalendar.svelte +0 -121
  77. package/src/plots/LinePlot.svelte +0 -51
  78. package/src/plots/RankBarPlot.svelte +0 -38
  79. package/src/plots/ScatterPlot.svelte +0 -28
  80. package/src/plots/ViolinPlot.svelte +0 -10
@@ -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
- }
@@ -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) {}
@@ -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
- }