layerchart 0.0.1 → 0.0.5

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 (89) hide show
  1. package/README.md +2 -37
  2. package/components/Arc.svelte +77 -0
  3. package/components/Arc.svelte.d.ts +34 -0
  4. package/components/Area.svelte +47 -0
  5. package/components/Area.svelte.d.ts +31 -0
  6. package/components/AreaStack.svelte +40 -0
  7. package/components/AreaStack.svelte.d.ts +20 -0
  8. package/components/AxisX.svelte +52 -0
  9. package/components/AxisX.svelte.d.ts +23 -0
  10. package/components/AxisY.svelte +66 -0
  11. package/components/AxisY.svelte.d.ts +23 -0
  12. package/components/Bars.svelte +102 -0
  13. package/components/Bars.svelte.d.ts +35 -0
  14. package/components/Baseline.svelte +21 -0
  15. package/components/Baseline.svelte.d.ts +17 -0
  16. package/components/Chart.svelte +50 -0
  17. package/components/Chart.svelte.d.ts +24 -0
  18. package/components/Circle.svelte +15 -0
  19. package/components/Circle.svelte.d.ts +22 -0
  20. package/components/ConnectedPoints.svelte +68 -0
  21. package/components/ConnectedPoints.svelte.d.ts +18 -0
  22. package/components/Group.svelte +26 -0
  23. package/components/Group.svelte.d.ts +21 -0
  24. package/components/HighlightLine.svelte +52 -0
  25. package/components/HighlightLine.svelte.d.ts +17 -0
  26. package/components/HighlightRect.svelte +27 -0
  27. package/components/HighlightRect.svelte.d.ts +18 -0
  28. package/components/Labels.svelte +96 -0
  29. package/components/Labels.svelte.d.ts +22 -0
  30. package/components/Line.svelte +18 -0
  31. package/components/Line.svelte.d.ts +23 -0
  32. package/components/LinearGradient.svelte +27 -0
  33. package/components/LinearGradient.svelte.d.ts +27 -0
  34. package/components/Path.svelte +40 -0
  35. package/components/Path.svelte.d.ts +27 -0
  36. package/components/Points.svelte +58 -0
  37. package/components/Points.svelte.d.ts +19 -0
  38. package/components/Rect.svelte +25 -0
  39. package/components/Rect.svelte.d.ts +25 -0
  40. package/components/Text.svelte +129 -0
  41. package/components/Text.svelte.d.ts +28 -0
  42. package/components/Threshold.svelte +86 -0
  43. package/components/Threshold.svelte.d.ts +35 -0
  44. package/components/Tooltip.svelte +120 -0
  45. package/components/Tooltip.svelte.d.ts +33 -0
  46. package/components/index.d.ts +19 -0
  47. package/components/index.js +19 -0
  48. package/docs/Blockquote.svelte +7 -0
  49. package/docs/Blockquote.svelte.d.ts +23 -0
  50. package/docs/Code.svelte +6 -0
  51. package/docs/Code.svelte.d.ts +23 -0
  52. package/docs/Layout.svelte +20 -0
  53. package/docs/Layout.svelte.d.ts +29 -0
  54. package/docs/Link.svelte +7 -0
  55. package/docs/Link.svelte.d.ts +27 -0
  56. package/docs/Preview.svelte +23 -0
  57. package/docs/Preview.svelte.d.ts +21 -0
  58. package/index.d.ts +3 -0
  59. package/index.js +3 -0
  60. package/package.json +84 -26
  61. package/stores/motionStore.d.ts +8 -0
  62. package/stores/motionStore.js +16 -0
  63. package/utils/event.d.ts +4 -0
  64. package/utils/event.js +42 -0
  65. package/utils/genData.d.ts +32 -0
  66. package/utils/genData.js +59 -0
  67. package/utils/index.d.ts +1 -0
  68. package/utils/index.js +1 -0
  69. package/utils/math.d.ts +4 -0
  70. package/utils/math.js +6 -0
  71. package/utils/path.d.ts +5 -0
  72. package/utils/path.js +14 -0
  73. package/utils/pivot.d.ts +14 -0
  74. package/utils/pivot.js +36 -0
  75. package/utils/scales.d.ts +10 -0
  76. package/utils/scales.js +21 -0
  77. package/utils/stack.d.ts +14 -0
  78. package/utils/stack.js +69 -0
  79. package/utils/string.d.ts +4 -0
  80. package/utils/string.js +27 -0
  81. package/utils/ticks.d.ts +3 -0
  82. package/utils/ticks.js +157 -0
  83. package/.prettierrc +0 -6
  84. package/src/app.html +0 -12
  85. package/src/global.d.ts +0 -1
  86. package/src/routes/index.svelte +0 -2
  87. package/static/favicon.png +0 -0
  88. package/svelte.config.js +0 -15
  89. package/tsconfig.json +0 -31
@@ -0,0 +1,27 @@
1
+ /** @typedef {typeof __propDef.props} LinkProps */
2
+ /** @typedef {typeof __propDef.events} LinkEvents */
3
+ /** @typedef {typeof __propDef.slots} LinkSlots */
4
+ export default class Link extends SvelteComponentTyped<{
5
+ [x: string]: any;
6
+ }, {
7
+ [evt: string]: CustomEvent<any>;
8
+ }, {
9
+ default: {};
10
+ }> {
11
+ }
12
+ export type LinkProps = typeof __propDef.props;
13
+ export type LinkEvents = typeof __propDef.events;
14
+ export type LinkSlots = typeof __propDef.slots;
15
+ import { SvelteComponentTyped } from "svelte";
16
+ declare const __propDef: {
17
+ props: {
18
+ [x: string]: any;
19
+ };
20
+ events: {
21
+ [evt: string]: CustomEvent<any>;
22
+ };
23
+ slots: {
24
+ default: {};
25
+ };
26
+ };
27
+ export {};
@@ -0,0 +1,23 @@
1
+ <script >import Prism from 'prismjs';
2
+ import 'prism-svelte';
3
+ export let code = null;
4
+ export let language = 'svelte';
5
+ export let highlight = false;
6
+ $: displayCode = highlight ? Prism.highlight(code, Prism.languages.svelte, 'svelte') : code;
7
+ </script>
8
+
9
+ <div class="border border-black/20 rounded bg-white">
10
+ <div class="p-4">
11
+ <slot />
12
+ </div>
13
+
14
+ {#if displayCode}
15
+ <pre
16
+ class="language-{language} rounded"
17
+ style="margin: 0">
18
+ <code class="language-{language}">
19
+ {@html displayCode}
20
+ </code>
21
+ </pre>
22
+ {/if}
23
+ </div>
@@ -0,0 +1,21 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ import 'prism-svelte';
3
+ declare const __propDef: {
4
+ props: {
5
+ code?: any;
6
+ language?: string;
7
+ highlight?: boolean;
8
+ };
9
+ events: {
10
+ [evt: string]: CustomEvent<any>;
11
+ };
12
+ slots: {
13
+ default: {};
14
+ };
15
+ };
16
+ export declare type PreviewProps = typeof __propDef.props;
17
+ export declare type PreviewEvents = typeof __propDef.events;
18
+ export declare type PreviewSlots = typeof __propDef.slots;
19
+ export default class Preview extends SvelteComponentTyped<PreviewProps, PreviewEvents, PreviewSlots> {
20
+ }
21
+ export {};
package/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { Svg, Html } from 'layercake';
2
+ export * from './components';
3
+ export * from './utils';
package/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { Svg, Html } from 'layercake';
2
+ export * from './components';
3
+ export * from './utils';
package/package.json CHANGED
@@ -1,27 +1,85 @@
1
1
  {
2
- "name": "layerchart",
3
- "author": "Sean Lynch <techniq35@gmail.com>",
4
- "license": "MIT",
5
- "repository": "techniq/layerchart",
6
- "version": "0.0.1",
7
- "scripts": {
8
- "dev": "svelte-kit dev",
9
- "build": "svelte-kit build",
10
- "preview": "svelte-kit preview",
11
- "check": "svelte-check --tsconfig ./tsconfig.json",
12
- "check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
13
- "lint": "prettier --ignore-path .gitignore --check --plugin-search-dir=. .",
14
- "format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. ."
15
- },
16
- "devDependencies": {
17
- "@sveltejs/kit": "next",
18
- "prettier": "~2.2.1",
19
- "prettier-plugin-svelte": "^2.2.0",
20
- "svelte": "^3.34.0",
21
- "svelte-check": "^2.0.0",
22
- "svelte-preprocess": "^4.0.0",
23
- "tslib": "^2.0.0",
24
- "typescript": "^4.0.0"
25
- },
26
- "type": "module"
27
- }
2
+ "name": "layerchart",
3
+ "author": "Sean Lynch <techniq35@gmail.com>",
4
+ "license": "MIT",
5
+ "repository": "techniq/layerchart",
6
+ "version": "0.0.5",
7
+ "devDependencies": {
8
+ "@sveltejs/adapter-static": "^1.0.0-next.21",
9
+ "@sveltejs/kit": "^1.0.0-next.201",
10
+ "@types/d3-array": "^3.0.2",
11
+ "@types/d3-scale": "^4.0.2",
12
+ "@types/d3-shape": "^3.0.2",
13
+ "@types/lodash-es": "^4.17.5",
14
+ "autoprefixer": "^10.4.0",
15
+ "mdsvex": "^0.9.8",
16
+ "prettier": "^2.5.0",
17
+ "prettier-plugin-svelte": "^2.5.0",
18
+ "prism-themes": "^1.9.0",
19
+ "svelte": "^3.44.2",
20
+ "svelte-check": "^2.2.10",
21
+ "svelte-preprocess": "^4.9.4",
22
+ "svelte2tsx": "^0.4.10",
23
+ "tailwindcss": "^2.2.19",
24
+ "tailwindcss-elevation": "^1.0.1",
25
+ "tslib": "^2.3.1",
26
+ "typescript": "^4.5.2",
27
+ "unist-util-visit": "^4.1.0"
28
+ },
29
+ "type": "module",
30
+ "dependencies": {
31
+ "@mdi/js": "^6.5.95",
32
+ "d3-array": "^3.1.1",
33
+ "d3-interpolate-path": "^2.2.3",
34
+ "d3-scale": "^4.0.2",
35
+ "d3-shape": "^3.0.1",
36
+ "date-fns": "^2.26.0",
37
+ "layercake": "^5.0.1",
38
+ "lodash-es": "^4.17.21",
39
+ "svelte-ux": "^0.0.105"
40
+ },
41
+ "exports": {
42
+ "./package.json": "./package.json",
43
+ "./components/Arc.svelte": "./components/Arc.svelte",
44
+ "./components/Area.svelte": "./components/Area.svelte",
45
+ "./components/AreaStack.svelte": "./components/AreaStack.svelte",
46
+ "./components/AxisX.svelte": "./components/AxisX.svelte",
47
+ "./components/AxisY.svelte": "./components/AxisY.svelte",
48
+ "./components/Bars.svelte": "./components/Bars.svelte",
49
+ "./components/Baseline.svelte": "./components/Baseline.svelte",
50
+ "./components/Chart.svelte": "./components/Chart.svelte",
51
+ "./components/Circle.svelte": "./components/Circle.svelte",
52
+ "./components/ConnectedPoints.svelte": "./components/ConnectedPoints.svelte",
53
+ "./components/Group.svelte": "./components/Group.svelte",
54
+ "./components/HighlightLine.svelte": "./components/HighlightLine.svelte",
55
+ "./components/HighlightRect.svelte": "./components/HighlightRect.svelte",
56
+ "./components/Labels.svelte": "./components/Labels.svelte",
57
+ "./components/Line.svelte": "./components/Line.svelte",
58
+ "./components/LinearGradient.svelte": "./components/LinearGradient.svelte",
59
+ "./components/Path.svelte": "./components/Path.svelte",
60
+ "./components/Points.svelte": "./components/Points.svelte",
61
+ "./components/Rect.svelte": "./components/Rect.svelte",
62
+ "./components/Text.svelte": "./components/Text.svelte",
63
+ "./components/Threshold.svelte": "./components/Threshold.svelte",
64
+ "./components/Tooltip.svelte": "./components/Tooltip.svelte",
65
+ "./components": "./components/index.js",
66
+ "./docs/Blockquote.svelte": "./docs/Blockquote.svelte",
67
+ "./docs/Code.svelte": "./docs/Code.svelte",
68
+ "./docs/Layout.svelte": "./docs/Layout.svelte",
69
+ "./docs/Link.svelte": "./docs/Link.svelte",
70
+ "./docs/Preview.svelte": "./docs/Preview.svelte",
71
+ ".": "./index.js",
72
+ "./stores/motionStore": "./stores/motionStore.js",
73
+ "./utils/event": "./utils/event.js",
74
+ "./utils/genData": "./utils/genData.js",
75
+ "./utils": "./utils/index.js",
76
+ "./utils/math": "./utils/math.js",
77
+ "./utils/path": "./utils/path.js",
78
+ "./utils/pivot": "./utils/pivot.js",
79
+ "./utils/scales": "./utils/scales.js",
80
+ "./utils/stack": "./utils/stack.js",
81
+ "./utils/string": "./utils/string.js",
82
+ "./utils/ticks": "./utils/ticks.js"
83
+ },
84
+ "svelte": "./index.js"
85
+ }
@@ -0,0 +1,8 @@
1
+ import { spring, tweened } from 'svelte/motion';
2
+ /**
3
+ * Convenient wrapper to get motion store based on properties, or fall back to basic writable() store
4
+ */
5
+ export declare function getMotionStore(value: any, options: {
6
+ spring?: boolean | Parameters<typeof spring>[1];
7
+ tweened?: boolean | Parameters<typeof tweened>[1];
8
+ }): import("svelte/motion").Tweened<unknown> | import("svelte/motion").Spring<any> | import("svelte/store").Writable<any>;
@@ -0,0 +1,16 @@
1
+ import { writable } from 'svelte/store';
2
+ import { spring, tweened } from 'svelte/motion';
3
+ /**
4
+ * Convenient wrapper to get motion store based on properties, or fall back to basic writable() store
5
+ */
6
+ export function getMotionStore(value, options) {
7
+ if (options.spring) {
8
+ return spring(value, options.spring === true ? undefined : options.spring);
9
+ }
10
+ else if (options.tweened) {
11
+ return tweened(value, options.tweened === true ? undefined : options.tweened);
12
+ }
13
+ else {
14
+ return writable(value);
15
+ }
16
+ }
@@ -0,0 +1,4 @@
1
+ export declare function localPoint(node: Element, event: MouseEvent | TouchEvent): {
2
+ x: number;
3
+ y: number;
4
+ };
package/utils/event.js ADDED
@@ -0,0 +1,42 @@
1
+ import { isSVGElement, isSVGGraphicsElement, isSVGSVGElement, isTouchEvent } from 'svelte-ux/types/typeGuards';
2
+ // See: https://github.com/airbnb/visx/blob/master/packages/visx-event/src/localPointGeneric.ts
3
+ export function localPoint(node, event) {
4
+ if (!node || !event)
5
+ return null;
6
+ const coords = getPointFromEvent(event);
7
+ // find top-most SVG
8
+ const svg = isSVGElement(node) ? node.ownerSVGElement : node;
9
+ const screenCTM = isSVGGraphicsElement(svg) ? svg.getScreenCTM() : null;
10
+ if (isSVGSVGElement(svg) && screenCTM) {
11
+ let point = svg.createSVGPoint();
12
+ point.x = coords.x;
13
+ point.y = coords.y;
14
+ point = point.matrixTransform(screenCTM.inverse());
15
+ return {
16
+ x: point.x,
17
+ y: point.y
18
+ };
19
+ }
20
+ // fall back to bounding box
21
+ const rect = node.getBoundingClientRect();
22
+ return {
23
+ x: coords.x - rect.left - node.clientLeft,
24
+ y: coords.y - rect.top - node.clientTop
25
+ };
26
+ }
27
+ function getPointFromEvent(event) {
28
+ if (!event)
29
+ return { x: 0, y: 0 };
30
+ if (isTouchEvent(event)) {
31
+ return event.changedTouches.length > 0
32
+ ? {
33
+ x: event.changedTouches[0].clientX,
34
+ y: event.changedTouches[0].clientY
35
+ }
36
+ : { x: 0, y: 0 };
37
+ }
38
+ return {
39
+ x: event.clientX,
40
+ y: event.clientY
41
+ };
42
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Get random number between min (inclusive) and max (exclusive)
3
+ * see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random#getting_a_random_number_between_0_inclusive_and_1_exclusive
4
+ */
5
+ export declare function getRandomNumber(min: number, max: number): number;
6
+ /**
7
+ * Get random integer between min (inclusive) and max (inclusive by default)
8
+ * see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random#getting_a_random_integer_between_two_values_inclusive
9
+ */
10
+ export declare function getRandomInteger(min: number, max: number, includeMax?: boolean): number;
11
+ export declare function createDateSeries(options: {
12
+ count?: number;
13
+ min: number;
14
+ max: number;
15
+ keys: Array<string>;
16
+ value: 'number' | 'integer';
17
+ }): {
18
+ date: Date;
19
+ }[];
20
+ export declare const wideData: {
21
+ year: string;
22
+ apples: number;
23
+ bananas: number;
24
+ cherries: number;
25
+ dates: number;
26
+ }[];
27
+ export declare const longData: {
28
+ year: string;
29
+ basket: number;
30
+ fruit: string;
31
+ value: number;
32
+ }[];
@@ -0,0 +1,59 @@
1
+ import { subDays } from 'date-fns';
2
+ /**
3
+ * Get random number between min (inclusive) and max (exclusive)
4
+ * see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random#getting_a_random_number_between_0_inclusive_and_1_exclusive
5
+ */
6
+ export function getRandomNumber(min, max) {
7
+ return Math.random() * (max - min) + min;
8
+ }
9
+ /**
10
+ * Get random integer between min (inclusive) and max (inclusive by default)
11
+ * see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random#getting_a_random_integer_between_two_values_inclusive
12
+ */
13
+ export function getRandomInteger(min, max, includeMax = true) {
14
+ min = Math.ceil(min);
15
+ max = Math.floor(max);
16
+ return Math.floor(Math.random() * (max - min + (includeMax ? 1 : 0)) + min);
17
+ }
18
+ export function createDateSeries(options) {
19
+ const now = new Date();
20
+ const count = options.count ?? 10;
21
+ const min = options.min;
22
+ const max = options.max;
23
+ const keys = options.keys ?? ['value'];
24
+ return Array.from({ length: count }).map((_, i) => {
25
+ return {
26
+ date: subDays(now, count - i - 1),
27
+ ...Object.fromEntries(keys.map((key) => {
28
+ return [
29
+ key,
30
+ options.value === 'integer' ? getRandomInteger(min, max) : getRandomNumber(min, max)
31
+ ];
32
+ }))
33
+ };
34
+ });
35
+ }
36
+ export const wideData = [
37
+ { year: '2019', apples: 3840, bananas: 1920, cherries: 960, dates: 400 },
38
+ { year: '2018', apples: 1600, bananas: 1440, cherries: 960, dates: 400 },
39
+ { year: '2017', apples: 820, bananas: 1000, cherries: 640, dates: 400 },
40
+ { year: '2016', apples: 820, bananas: 560, cherries: 720, dates: 400 }
41
+ ];
42
+ export const longData = [
43
+ { year: '2019', basket: 1, fruit: 'apples', value: 3840 },
44
+ { year: '2019', basket: 1, fruit: 'bananas', value: 1920 },
45
+ { year: '2019', basket: 2, fruit: 'cherries', value: 960 },
46
+ { year: '2019', basket: 2, fruit: 'dates', value: 400 },
47
+ { year: '2018', basket: 1, fruit: 'apples', value: 1600 },
48
+ { year: '2018', basket: 1, fruit: 'bananas', value: 1440 },
49
+ { year: '2018', basket: 2, fruit: 'cherries', value: 960 },
50
+ { year: '2018', basket: 2, fruit: 'dates', value: 400 },
51
+ { year: '2017', basket: 1, fruit: 'apples', value: 820 },
52
+ { year: '2017', basket: 1, fruit: 'bananas', value: 1000 },
53
+ { year: '2017', basket: 2, fruit: 'cherries', value: 640 },
54
+ { year: '2017', basket: 2, fruit: 'dates', value: 400 },
55
+ { year: '2016', basket: 1, fruit: 'apples', value: 820 },
56
+ { year: '2016', basket: 1, fruit: 'bananas', value: 560 },
57
+ { year: '2016', basket: 2, fruit: 'cherries', value: 720 },
58
+ { year: '2016', basket: 2, fruit: 'dates', value: 400 }
59
+ ];
@@ -0,0 +1 @@
1
+ export { getMajorTicks, getMinorTicks } from './ticks';
package/utils/index.js ADDED
@@ -0,0 +1 @@
1
+ export { getMajorTicks, getMinorTicks } from './ticks';
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Convert degrees to radians
3
+ */
4
+ export declare function degreesToRadians(degrees: number): number;
package/utils/math.js ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Convert degrees to radians
3
+ */
4
+ export function degreesToRadians(degrees) {
5
+ return (degrees * Math.PI) / 180;
6
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Convert easing into path data with number of points
3
+ * see: https://svelte.dev/examples#easing
4
+ */
5
+ export declare function getEasingPath(easing: (t: number) => number, count?: number): string;
package/utils/path.js ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Convert easing into path data with number of points
3
+ * see: https://svelte.dev/examples#easing
4
+ */
5
+ export function getEasingPath(easing, count = 1000) {
6
+ let pathData = `M0 ${count}`;
7
+ for (let i = 1; i <= count; i++) {
8
+ pathData += `
9
+ L${(i / count) * count}
10
+ ${count - easing(i / count) * count}
11
+ `;
12
+ }
13
+ return pathData;
14
+ }
@@ -0,0 +1,14 @@
1
+ export declare function getAccessor(key: any): any;
2
+ /**
3
+ * Pivot longer (columns to rows)
4
+ * - see: https://observablehq.com/d/3ea8d446f5ba96fe
5
+ * - see also: https://observablehq.com/d/ac2a320cf2b0adc4 as generator
6
+ */
7
+ export declare function pivotLonger(data: any, columns: any, name: any, value: any): any;
8
+ /**
9
+ * Pivot wider (rows to columns)
10
+ * - see: https://github.com/d3/d3-array/issues/142#issuecomment-761861983
11
+ */
12
+ export declare function pivotWider(data: any, column: any, name: any, value: any): any[];
13
+ export declare function first(items: any): any;
14
+ export declare function last(items: any): any;
package/utils/pivot.js ADDED
@@ -0,0 +1,36 @@
1
+ import { group } from 'd3-array';
2
+ export function getAccessor(key) {
3
+ if (typeof key === 'function') {
4
+ return key;
5
+ }
6
+ else {
7
+ return (d) => d[key];
8
+ }
9
+ }
10
+ /**
11
+ * Pivot longer (columns to rows)
12
+ * - see: https://observablehq.com/d/3ea8d446f5ba96fe
13
+ * - see also: https://observablehq.com/d/ac2a320cf2b0adc4 as generator
14
+ */
15
+ export function pivotLonger(data, columns, name, value) {
16
+ const keep = Object.keys(data[0]).filter((c) => !columns.includes(c));
17
+ return data.flatMap((d) => {
18
+ const base = keep.map((k) => [k, d[k]]);
19
+ return columns.map((column) => {
20
+ return Object.fromEntries([...base, [name, column], [value, d[column]]]);
21
+ });
22
+ });
23
+ }
24
+ /**
25
+ * Pivot wider (rows to columns)
26
+ * - see: https://github.com/d3/d3-array/issues/142#issuecomment-761861983
27
+ */
28
+ export function pivotWider(data, column, name, value) {
29
+ return Array.from(group(data, (d) => d[column]), ([columnVal, items]) => Object.fromEntries([[column, columnVal]].concat(items.map((d) => [d[name], d[value]]))));
30
+ }
31
+ export function first(items) {
32
+ return items[0];
33
+ }
34
+ export function last(items) {
35
+ return items[items.length - 1];
36
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Implemenation for missing `scaleBand().invert()`
3
+ *
4
+ * See: https://stackoverflow.com/a/50846323/191902
5
+ * https://github.com/d3/d3-scale/pull/64
6
+ * https://github.com/vega/vega-scale/blob/master/src/scaleBand.js#L118
7
+ * https://observablehq.com/@d3/ordinal-brushing
8
+ */
9
+ export declare function scaleBandInvert(scale: any): (value: any) => any;
10
+ export declare function isScaleBand(scale: any): boolean;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Implemenation for missing `scaleBand().invert()`
3
+ *
4
+ * See: https://stackoverflow.com/a/50846323/191902
5
+ * https://github.com/d3/d3-scale/pull/64
6
+ * https://github.com/vega/vega-scale/blob/master/src/scaleBand.js#L118
7
+ * https://observablehq.com/@d3/ordinal-brushing
8
+ */
9
+ export function scaleBandInvert(scale) {
10
+ const domain = scale.domain();
11
+ const paddingOuter = scale(domain[0]);
12
+ const eachBand = scale.step();
13
+ return function (value) {
14
+ // TODO: Should this use Math.round to better select? https://stackoverflow.com/questions/38633082/d3-getting-invert-value-of-band-scales/50846323#comment104743795_50846323
15
+ const index = Math.floor((value - paddingOuter) / eachBand);
16
+ return domain[Math.max(0, Math.min(index, domain.length - 1))];
17
+ };
18
+ }
19
+ export function isScaleBand(scale) {
20
+ return typeof scale.bandwidth === 'function';
21
+ }
@@ -0,0 +1,14 @@
1
+ import { stackOffsetNone } from 'd3-shape';
2
+ declare type OffsetType = typeof stackOffsetNone;
3
+ export declare function createStackData(data: any[], options: {
4
+ xKey: string;
5
+ groupBy?: string;
6
+ stackBy?: string;
7
+ offset?: OffsetType;
8
+ }): any[];
9
+ /**
10
+ * Function to offset each layer by the maximum of the previous layer
11
+ * - see: https://observablehq.com/@mkfreeman/separated-bar-chart
12
+ */
13
+ export declare function stackOffsetSeparated(series: any, order: any): void;
14
+ export {};
package/utils/stack.js ADDED
@@ -0,0 +1,69 @@
1
+ import { flatGroup, max, sum } from 'd3-array';
2
+ import { stack } from 'd3-shape';
3
+ import { pivotWider } from './pivot';
4
+ export function createStackData(data, options) {
5
+ if (options.groupBy) {
6
+ // Group then Stack (if needed)
7
+ const groupedData = flatGroup(data, (d) => d[options.xKey], (d) => d[options.groupBy]);
8
+ const result = groupedData.flatMap((d, i) => {
9
+ const groupKeys = d.slice(0, -1); // all but last item
10
+ const itemData = d.slice(-1)[0]; // last item
11
+ const pivotData = pivotWider(itemData, options.xKey, options.stackBy, 'value');
12
+ const stackKeys = [...new Set(itemData.map((d) => d[options.stackBy]))];
13
+ const stackData = stack().keys(stackKeys).offset(options.offset)(pivotData);
14
+ //console.log({ pivotData, stackData })
15
+ return stackData.flatMap((series) => {
16
+ //console.log({ series })
17
+ return series.flatMap((s) => {
18
+ return {
19
+ ...itemData[0],
20
+ keys: options.stackBy ? [...groupKeys, series.key] : groupKeys,
21
+ values: options.stackBy ? [s[0], s[1]] : [0, sum(itemData, (d) => d.value)]
22
+ };
23
+ });
24
+ });
25
+ });
26
+ return result;
27
+ }
28
+ else if (options.stackBy) {
29
+ // Stack only
30
+ const pivotData = pivotWider(data, options.xKey, options.stackBy, 'value');
31
+ const stackKeys = [...new Set(data.map((d) => d[options.stackBy]))];
32
+ const stackData = stack().keys(stackKeys).offset(options.offset)(pivotData);
33
+ const result = stackData.flatMap((series) => {
34
+ return series.flatMap((s) => {
35
+ return {
36
+ ...s.data,
37
+ keys: [s.data[options.xKey], series.key],
38
+ values: [s[0], s[1]]
39
+ };
40
+ });
41
+ });
42
+ return result;
43
+ }
44
+ else {
45
+ // TODO: Do anything if no group or stack? Convert to { ...d, keys: [...}, values: [...] ]}?
46
+ return data;
47
+ }
48
+ }
49
+ /**
50
+ * Function to offset each layer by the maximum of the previous layer
51
+ * - see: https://observablehq.com/@mkfreeman/separated-bar-chart
52
+ */
53
+ // TODO: Try to find way to support separated with createStackData() (which has isolated stacked per group)
54
+ export function stackOffsetSeparated(series, order) {
55
+ const gap = 200; // TODO: Determine way to pass in as option (curry?)
56
+ if (!((n = series.length) > 1))
57
+ return;
58
+ // Standard series
59
+ for (var i = 1, s0, s1 = series[order[0]], n, m = s1.length; i < n; ++i) {
60
+ (s0 = s1), (s1 = series[order[i]]);
61
+ let base = max(s0, (d) => d[1]) + gap; // here is where you calculate the maximum of the previous layer
62
+ for (var j = 0; j < m; ++j) {
63
+ // Set the height based on the data values, shifted up by the previous layer
64
+ let diff = s1[j][1] - s1[j][0];
65
+ s1[j][0] = base;
66
+ s1[j][1] = base + diff;
67
+ }
68
+ }
69
+ }
@@ -0,0 +1,4 @@
1
+ /// <reference types="lodash" />
2
+ declare function _getStringWidth(str: string, style?: CSSStyleDeclaration): number;
3
+ export declare const getStringWidth: typeof _getStringWidth & import("lodash").MemoizedFunction;
4
+ export {};
@@ -0,0 +1,27 @@
1
+ import { memoize } from 'lodash-es';
2
+ const MEASUREMENT_ELEMENT_ID = '__text_measurement_id';
3
+ function _getStringWidth(str, style) {
4
+ try {
5
+ // Calculate length of each word to be used to determine number of words per line
6
+ let textEl = document.getElementById(MEASUREMENT_ELEMENT_ID);
7
+ if (!textEl) {
8
+ const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
9
+ svg.style.width = '0';
10
+ svg.style.height = '0';
11
+ svg.style.position = 'absolute';
12
+ svg.style.top = '-100%';
13
+ svg.style.left = '-100%';
14
+ textEl = document.createElementNS('http://www.w3.org/2000/svg', 'text');
15
+ textEl.setAttribute('id', MEASUREMENT_ELEMENT_ID);
16
+ svg.appendChild(textEl);
17
+ document.body.appendChild(svg);
18
+ }
19
+ Object.assign(textEl.style, style);
20
+ textEl.textContent = str;
21
+ return textEl.getComputedTextLength();
22
+ }
23
+ catch (e) {
24
+ return null;
25
+ }
26
+ }
27
+ export const getStringWidth = memoize(_getStringWidth, (str, style) => `${str}_${JSON.stringify(style)}`);
@@ -0,0 +1,3 @@
1
+ export declare function getMajorTicks(start: Date, end: Date): import("d3-time").TimeInterval;
2
+ export declare function formatMajorTick(date: Date, rangeStart: Date, rangeEnd: Date): string;
3
+ export declare function getMinorTicks(start: Date, end: Date): import("d3-time").TimeInterval;