@rokkit/chart 1.0.0-next.100

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 (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +55 -0
  3. package/package.json +71 -0
  4. package/src/Chart.svelte +67 -0
  5. package/src/Symbol.svelte +17 -0
  6. package/src/Texture.svelte +16 -0
  7. package/src/elements/Bar.svelte +29 -0
  8. package/src/elements/ColorRamp.svelte +49 -0
  9. package/src/elements/ContinuousLegend.svelte +45 -0
  10. package/src/elements/DefinePatterns.svelte +22 -0
  11. package/src/elements/DiscreteLegend.svelte +39 -0
  12. package/src/elements/Label.svelte +13 -0
  13. package/src/elements/SymbolGrid.svelte +23 -0
  14. package/src/elements/index.js +6 -0
  15. package/src/index.js +3 -0
  16. package/src/lib/brewer.js +25 -0
  17. package/src/lib/chart.js +213 -0
  18. package/src/lib/grid.js +85 -0
  19. package/src/lib/index.js +4 -0
  20. package/src/lib/plots.js +27 -0
  21. package/src/lib/swatch.js +16 -0
  22. package/src/lib/ticks.js +46 -0
  23. package/src/lib/utils.js +8 -0
  24. package/src/patterns/Brick.svelte +17 -0
  25. package/src/patterns/Circles.svelte +18 -0
  26. package/src/patterns/CrossHatch.svelte +14 -0
  27. package/src/patterns/CurvedWave.svelte +9 -0
  28. package/src/patterns/Dots.svelte +19 -0
  29. package/src/patterns/OutlineCircles.svelte +15 -0
  30. package/src/patterns/Tile.svelte +17 -0
  31. package/src/patterns/Triangles.svelte +15 -0
  32. package/src/patterns/Waves.svelte +13 -0
  33. package/src/patterns/index.js +14 -0
  34. package/src/patterns/paths/NamedPattern.svelte +12 -0
  35. package/src/patterns/paths/constants.js +4 -0
  36. package/src/plots/Plot.svelte +40 -0
  37. package/src/plots/index.js +1 -0
  38. package/src/symbols/RoundedSquare.svelte +27 -0
  39. package/src/symbols/Shape.svelte +31 -0
  40. package/src/symbols/constants/index.js +4 -0
  41. package/src/symbols/index.js +9 -0
  42. package/src/symbols/outline.svelte +60 -0
  43. package/src/symbols/solid.svelte +60 -0
  44. package/src/template/Texture.svelte +16 -0
  45. package/src/template/constants.js +43 -0
  46. package/src/template/shapes/Circles.svelte +16 -0
  47. package/src/template/shapes/Lines.svelte +17 -0
  48. package/src/template/shapes/Path.svelte +12 -0
  49. package/src/template/shapes/Polygons.svelte +18 -0
  50. package/src/template/shapes/index.js +4 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Jerry Thomas
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # Charts for Svelte
2
+
3
+ The core idea behind the implementation is to use the respective strengths of both D3 and Svelte to build Themable, Composable, Animated, Responsive, Accessible, and Reactive Data Visualization components.
4
+
5
+ - [D3](https://d3js.org/) makes working with SVG a breeze. It provides a large set of utility functions for graph visualization that includes the computation of scales, interpolation, shapes, and more.
6
+ - [Svelte](https://svelte.dev/) makes UI development fun again. It provides modularization, interactivity, reactivity, and responsiveness.
7
+
8
+ This component library borrows concepts from the following articles, products, and repositories.
9
+
10
+ - [Introduction to Accessible Contrast and Color for Data Visualization](https://observablehq.com/@frankelavsky/chartability-contrast-series) by [Frank Elavsky](https://observablehq.com/@frankelavsky)
11
+ - [Animated, Responsive, and Reactive Data Visualization with Svelte](https://www.infoq.com/news/2020/10/svelte-d3-animation-data-vis/)
12
+ - [Barchart Race using Svelte & D3](https://t.co/iIoJw4f7Jc?amp=1) by [Amelia Wattenberger](https://mobile.twitter.com/Wattenberger) & [Russell Goldenberg](https://mobile.twitter.com/codenberg)
13
+ - [The D3.js Graph Gallery](https://www.d3-graph-gallery.com/index.html)
14
+ - [DC.js](https://dc-js.github.io/dc.js/) is an awesome implementation of interactive animated charts using D3.
15
+
16
+ ## Getting Started
17
+
18
+ Get started quickly using [degit](https://github.com/Rich-Harris/degit).
19
+
20
+ ```bash
21
+ degit jerrythomas/sparsh-ui/sites/playground my-app
22
+ ```
23
+
24
+ ## Features
25
+
26
+ - [x] Fill patterns
27
+ - [x] Symbols
28
+ - [x] Colors
29
+ - [ ] Themes
30
+
31
+ ### Plots
32
+
33
+ - [x] Box
34
+ - [ ] Violin
35
+ - [x] Scatter
36
+ - [ ] Line
37
+ - [ ] Histogram
38
+ - [ ] StackedBar
39
+ - [ ] Funnel
40
+
41
+ ### Chart
42
+
43
+ - [x] Axis
44
+ - [x] Labels
45
+ - [x] Grid
46
+ - [x] Ticks
47
+ - [ ] Margins
48
+ - [ ] Legend
49
+ - [ ] Composable
50
+ - [ ] Facet Grid
51
+ - [ ] Combine multiple plots
52
+ - [ ] Animation
53
+ - [ ] Time lapse
54
+ - [ ] Sliding window
55
+ - [ ] Rolling window
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@rokkit/chart",
3
+ "version": "1.0.0-next.100",
4
+ "description": "Components for making interactive charts.",
5
+ "author": "Jerry Thomas <me@jerrythomas.name>",
6
+ "license": "MIT",
7
+ "main": "src/index.js",
8
+ "module": "src/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "type": "module",
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "devDependencies": {
15
+ "@sveltejs/vite-plugin-svelte": "^3.1.2",
16
+ "@testing-library/svelte": "^5.2.1",
17
+ "@vitest/coverage-v8": "^2.1.1",
18
+ "@vitest/ui": "~2.1.1",
19
+ "js-yaml": "^4.1.0",
20
+ "jsdom": "^25.0.0",
21
+ "svelte": "^4.2.19",
22
+ "typescript": "^5.6.2",
23
+ "vite": "^5.4.6",
24
+ "vitest": "~2.1.1",
25
+ "shared-config": "1.0.0-next.100"
26
+ },
27
+ "dependencies": {
28
+ "@observablehq/plot": "^0.6.16",
29
+ "d3-array": "^3.2.4",
30
+ "d3-collection": "^1.0.7",
31
+ "d3-format": "^3.1.0",
32
+ "d3-interpolate": "^3.0.1",
33
+ "d3-scale": "^4.0.2",
34
+ "d3-shape": "^3.2.0",
35
+ "date-fns": "^4.1.0",
36
+ "ramda": "^0.30.1",
37
+ "yootils": "^0.3.1",
38
+ "@rokkit/atoms": "1.0.0-next.100",
39
+ "@rokkit/core": "1.0.0-next.100",
40
+ "@rokkit/molecules": "1.0.0-next.100",
41
+ "@rokkit/stores": "1.0.0-next.100"
42
+ },
43
+ "files": [
44
+ "src/**/*.js",
45
+ "src/**/*.svelte",
46
+ "!spec"
47
+ ],
48
+ "exports": {
49
+ "./src": "./src",
50
+ "./package.json": "./package.json",
51
+ "./lib": "./src/lib/index.js",
52
+ "./patterns": "./src/patterns/index.js",
53
+ "./symbols": "./src/symbols/index.js",
54
+ "./elements": "./src/elements/index.js",
55
+ ".": {
56
+ "types": "./dist/index.d.ts",
57
+ "import": "./src/index.js",
58
+ "svelte": "./src/index.js"
59
+ }
60
+ },
61
+ "scripts": {
62
+ "format": "prettier --write .",
63
+ "lint": "eslint --fix .",
64
+ "test:ci": "vitest run",
65
+ "test:ui": "vitest --ui",
66
+ "test": "vitest",
67
+ "coverage": "vitest run --coverage",
68
+ "latest": "pnpm upgrade --latest && pnpm test:ci",
69
+ "release": "pnpm publish --access public"
70
+ }
71
+ }
@@ -0,0 +1,67 @@
1
+ <script>
2
+ import { setContext } from 'svelte'
3
+ import { writable } from 'svelte/store'
4
+ import { chart } from './lib'
5
+
6
+ let config = writable({})
7
+ setContext('chart', config)
8
+
9
+ export let data
10
+ export let x
11
+ export let y
12
+ export let value = y
13
+ export let color = x
14
+ export let fill = x
15
+ // export let pattern = x
16
+ export let width = 2048
17
+ export let height = 2048
18
+ export let padding = height / 16
19
+ export let marginLeft = 0
20
+ export let marginRight = 0
21
+ export let marginTop = 0
22
+ export let marginBottom = 0
23
+ export let flipCoords = false
24
+ export let spacing = 0.1
25
+
26
+ $: margin = {
27
+ left: marginLeft,
28
+ right: marginRight,
29
+ top: marginTop,
30
+ bottom: marginBottom
31
+ }
32
+ // $: patterns = [...new Set(data.map((d) => d[pattern]))]
33
+ // $: fills = [...new Set(data.map((d) => d[fill]))]
34
+ // $: colors = [...new Set(data.map((d) => d[color]))]
35
+ $: config.set(
36
+ chart(data, {
37
+ x,
38
+ y,
39
+ value,
40
+ color,
41
+ fill,
42
+ width,
43
+ height,
44
+ padding,
45
+ margin,
46
+ flipCoords,
47
+ spacing
48
+ })
49
+ )
50
+ </script>
51
+
52
+ <svg
53
+ viewBox="0 0 {$config.width} {$config.height}"
54
+ width={$config.width}
55
+ height={$config.height}
56
+ class="w-full h-full chart"
57
+ >
58
+ <rect
59
+ x="0"
60
+ y="0"
61
+ width={$config.width}
62
+ height={$config.height}
63
+ fill="none"
64
+ stroke="currentColor"
65
+ />
66
+ <slot />
67
+ </svg>
@@ -0,0 +1,17 @@
1
+ <script>
2
+ import { components } from './symbols'
3
+
4
+ export let x = 0
5
+ export let y = 0
6
+ export let size = 10
7
+ export let fill = 'currentColor'
8
+ export let stroke = 'currentColor'
9
+ export let name = 'circle'
10
+ /** @type Object<string, any> */
11
+ export let using = components
12
+
13
+ $: component = using[name] || using.default
14
+ $: props = using[name] ? $$restProps : { name, ...$$restProps }
15
+ </script>
16
+
17
+ <svelte:component this={component} {x} {y} {size} {fill} {stroke} {...props} />
@@ -0,0 +1,16 @@
1
+ <script>
2
+ export let id
3
+ export let path
4
+ export let fill = 'currentColor'
5
+ export let stroke = 'currentColor'
6
+ export let thickness = 0.5
7
+ export let patternUnits = 'userSpaceOnUse'
8
+ export let size = 10
9
+ </script>
10
+
11
+ <pattern {id} {patternUnits} width={size} height={size}>
12
+ <rect width={size} height={size} {fill} />
13
+ {#if path}
14
+ <path d={path} fill="none" {stroke} stroke-width={thickness} />
15
+ {/if}
16
+ </pattern>
@@ -0,0 +1,29 @@
1
+ <script>
2
+ import { format } from 'd3-format'
3
+ import Label from './Label.svelte'
4
+
5
+ export let rank
6
+ export let value
7
+ export let name
8
+ export let formatString = '.1%'
9
+ export let scales
10
+ export let height = 60
11
+ export let fill
12
+ export let spaceBetween = 5
13
+
14
+ const textHeight = 16
15
+ const charWidth = 12
16
+ $: y = rank * (height + spaceBetween)
17
+ $: width = $scales.x(value)
18
+
19
+ $: textWidth = name.length * charWidth
20
+ $: textOffset = width <= textWidth ? width + charWidth : width
21
+ $: textAnchor = textOffset > width ? 'start' : 'end'
22
+
23
+ $: formattedValue = format(formatString)(value)
24
+ </script>
25
+
26
+ <rect x={$scales.x(0)} {y} {width} {height} {fill} opacity={0.5} />
27
+ <rect x={$scales.x(0)} {y} width={5} {height} {fill} />
28
+ <Label x={width} y={y + textHeight + 8} anchor={textAnchor} text={name} />
29
+ <Label x={width} y={y + height - 14} anchor={textAnchor} text={formattedValue} small />
@@ -0,0 +1,49 @@
1
+ <script>
2
+ import { scaleLinear } from 'd3-scale'
3
+ import { id as uniqueId } from '@rokkit/core'
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.apply(scale, [tickCount]).map((d) => ({ x: scaleTicks(d), value: d }))
18
+
19
+ $: colors = ticks.map(({ value }) => ({
20
+ color: scale(value),
21
+ offset: `${scalePercent(value)}%`
22
+ }))
23
+ $: id = uniqueId('legend-')
24
+ </script>
25
+
26
+ <defs>
27
+ <linearGradient {id}>
28
+ {#each colors as { color, offset }}
29
+ <stop stop-color={color} {offset} />
30
+ {/each}
31
+ </linearGradient>
32
+ </defs>
33
+ <rect {x} y={y + height} {width} {height} fill="url(#{id})" />
34
+ {#each ticks as { x, value }}
35
+ <line x1={x} y1={y + (2 * height) / 3} x2={x} y2={y + height * 2} />
36
+ <text {x} y={y + height / 2} font-size={textSize}>{value}</text>
37
+ {/each}
38
+ <line x1={x} y1={y + 2 * height} x2={x + 100} y2={y + 2 * height} />
39
+
40
+ <style>
41
+ line {
42
+ stroke: currentColor;
43
+ stroke-width: 0.2;
44
+ }
45
+ text {
46
+ fill: currentColor;
47
+ text-anchor: middle;
48
+ }
49
+ </style>
@@ -0,0 +1,45 @@
1
+ <script>
2
+ import { scaleLinear } from 'd3-scale'
3
+ // import { id as uniqueId } from '@rokkit/core'
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
+ export let id = 'legend'
13
+
14
+ $: scaleTicks = scaleLinear()
15
+ .range([x, x + 100])
16
+ .domain(scale.domain())
17
+ $: ticks = scale.ticks.apply(scale, [tickCount]).map((d) => ({ x: scaleTicks(d), label: d }))
18
+
19
+ $: colors = scale.range()
20
+ // $: id = uniqueId('legend-')
21
+ </script>
22
+
23
+ <defs>
24
+ <linearGradient {id}>
25
+ <stop stop-color={colors[0]} offset="0%" />
26
+ <stop stop-color={colors[1]} offset="100%" />
27
+ </linearGradient>
28
+ </defs>
29
+ <rect {x} y={y + height} {width} {height} fill="url(#{id})" />
30
+ {#each ticks as { x, label }}
31
+ <line x1={x} y1={y + (2 * height) / 3} x2={x} y2={y + height * 2} />
32
+ <text {x} y={y + height / 2} font-size={textSize}>{label}</text>
33
+ {/each}
34
+ <line x1={x} y1={y + 2 * height} x2={x + 100} y2={y + 2 * height} />
35
+
36
+ <style>
37
+ line {
38
+ stroke: currentColor;
39
+ stroke-width: 0.2;
40
+ }
41
+ text {
42
+ fill: currentColor;
43
+ text-anchor: middle;
44
+ }
45
+ </style>
@@ -0,0 +1,22 @@
1
+ <script>
2
+ import { uniq } from 'ramda'
3
+
4
+ export let size = 10
5
+ export let patternUnits = 'userSpaceOnUse'
6
+ /** @type {Array<import('./types').Pattern>} */
7
+ export let patterns = []
8
+
9
+ $: names = uniq(patterns.map(({ id }) => id))
10
+ </script>
11
+
12
+ {#if names.length < patterns.length}
13
+ <error> Patterns should be an array and should have unique names for each pattern </error>
14
+ {:else if patterns.length > 0}
15
+ <defs>
16
+ {#each patterns as { id, component, fill, stroke }}
17
+ <pattern {id} {patternUnits} width={size} height={size}>
18
+ <svelte:component this={component} {size} {fill} {stroke} />
19
+ </pattern>
20
+ {/each}
21
+ </defs>
22
+ {/if}
@@ -0,0 +1,39 @@
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
10
+
11
+ $: sizeWithSpace = size + space
12
+ $: ticks = scale.ticks.apply(scale, [tickCount])
13
+ </script>
14
+
15
+ {#each ticks as tick, i}
16
+ <text x={x + padding + i * sizeWithSpace + size / 2} y={y + size / 2} font-size={textSize}
17
+ >{tick}</text
18
+ >
19
+ <rect
20
+ x={x + padding + i * sizeWithSpace}
21
+ y={y + padding + textSize}
22
+ width={size}
23
+ height={size}
24
+ fill={scale(tick)}
25
+ rx="1"
26
+ ry="1"
27
+ />
28
+ {/each}
29
+
30
+ <style>
31
+ rect {
32
+ stroke: currentColor;
33
+ stroke-width: 0.2;
34
+ }
35
+ text {
36
+ fill: currentColor;
37
+ text-anchor: middle;
38
+ }
39
+ </style>
@@ -0,0 +1,13 @@
1
+ <script>
2
+ export let x
3
+ export let y
4
+ export let text
5
+ export let angle = 0
6
+ export let small = false
7
+ export let anchor = 'middle'
8
+
9
+ $: transform = `translate(${x},${y}) rotate(${angle})`
10
+ $: anchor = ['start', 'middle', 'end'].includes(anchor) ? anchor : 'middle'
11
+ </script>
12
+
13
+ <text class="label" class:small text-anchor={anchor} {transform}>{text}</text>
@@ -0,0 +1,23 @@
1
+ <script>
2
+ import { swatch, swatchGrid } from '../lib'
3
+ import Symbol from '../Symbol.svelte'
4
+
5
+ export let base = 'teal'
6
+ export let size = 4
7
+ export let shade = 600
8
+
9
+ $: grid = swatchGrid($swatch.keys.symbol.length, size, 10)
10
+ </script>
11
+
12
+ <svg viewBox="0 0 {grid.width} {grid.height}">
13
+ {#each grid.data as { x, y, r }, index}
14
+ <Symbol
15
+ {x}
16
+ {y}
17
+ size={r * 2}
18
+ name={$swatch.keys.symbol[index]}
19
+ fill={$swatch.palette[base][shade]}
20
+ stroke={$swatch.palette[base][shade]}
21
+ />
22
+ {/each}
23
+ </svg>
@@ -0,0 +1,6 @@
1
+ export { default as Bar } from './Bar.svelte'
2
+ export { default as ColorRamp } from './ColorRamp.svelte'
3
+ export { default as ContinuousLegend } from './ContinuousLegend.svelte'
4
+ export { default as Label } from './Label.svelte'
5
+ export { default as DefinePatterns } from './DefinePatterns.svelte'
6
+ export { default as SymbolGrid } from './SymbolGrid.svelte'
package/src/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { default as Chart } from './Chart.svelte'
2
+ export { default as Symbol } from './Symbol.svelte'
3
+ export * from './plots'
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Get fill patterns for a set of values
3
+ *
4
+ * @param {Array} values - Array of values
5
+ * @param {Object} swatch - Object with keys for color, gray, and pattern
6
+ * @param {Boolean} gray - Boolean to determine if gray or color
7
+ * @returns {Object} - Object with keys for pattern and color
8
+ */
9
+ export function getFillPatterns(values, swatch, gray = false) {
10
+ const colors = gray ? swatch.keys.gray : swatch.keys.color
11
+ const max_colors = colors.length
12
+ const max_patterns = swatch.keys.pattern.length
13
+
14
+ const mix = values
15
+ .map((value, index) => ({
16
+ [value]: {
17
+ pattern: swatch.keys.pattern[index % max_patterns],
18
+ color: colors[index % max_colors]
19
+ }
20
+ }))
21
+ .reduce((acc, current) => ({ ...acc, ...current }), {})
22
+ return mix
23
+ }
24
+
25
+ // export function getStrokePatterns() {}