layerchart 0.54.0 → 0.54.1

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 (99) hide show
  1. package/dist/components/Arc.svelte +170 -144
  2. package/dist/components/Area.svelte +96 -67
  3. package/dist/components/Area.svelte.d.ts +1 -0
  4. package/dist/components/Axis.svelte +205 -155
  5. package/dist/components/Bar.svelte +72 -45
  6. package/dist/components/Bars.svelte +45 -34
  7. package/dist/components/Blur.svelte +5 -3
  8. package/dist/components/Bounds.svelte +37 -21
  9. package/dist/components/Brush.svelte +181 -110
  10. package/dist/components/Calendar.svelte +51 -38
  11. package/dist/components/Chart.svelte +295 -74
  12. package/dist/components/Chart.svelte.d.ts +17 -17
  13. package/dist/components/ChartClipPath.svelte +8 -5
  14. package/dist/components/ChartContext.svelte +243 -93
  15. package/dist/components/ChartContext.svelte.d.ts +15 -23
  16. package/dist/components/Circle.svelte +25 -16
  17. package/dist/components/CircleClipPath.svelte +16 -10
  18. package/dist/components/ClipPath.svelte +11 -7
  19. package/dist/components/ColorRamp.svelte +12 -10
  20. package/dist/components/ForceSimulation.svelte +185 -116
  21. package/dist/components/Frame.svelte +10 -6
  22. package/dist/components/GeoCircle.svelte +15 -9
  23. package/dist/components/GeoContext.svelte +109 -62
  24. package/dist/components/GeoEdgeFade.svelte +20 -14
  25. package/dist/components/GeoPath.svelte +107 -69
  26. package/dist/components/GeoPoint.svelte +32 -18
  27. package/dist/components/GeoSpline.svelte +30 -22
  28. package/dist/components/GeoTile.svelte +40 -30
  29. package/dist/components/GeoVisible.svelte +10 -7
  30. package/dist/components/Graticule.svelte +14 -8
  31. package/dist/components/Grid.svelte +75 -48
  32. package/dist/components/Group.svelte +43 -31
  33. package/dist/components/Highlight.svelte +284 -243
  34. package/dist/components/HitCanvas.svelte +75 -42
  35. package/dist/components/Hull.svelte +40 -20
  36. package/dist/components/Labels.svelte +81 -70
  37. package/dist/components/Legend.svelte +105 -74
  38. package/dist/components/Legend.svelte.d.ts +1 -1
  39. package/dist/components/Line.svelte +29 -19
  40. package/dist/components/LinearGradient.svelte +21 -15
  41. package/dist/components/Link.svelte +44 -22
  42. package/dist/components/MonthPath.svelte +23 -16
  43. package/dist/components/MotionPath.svelte +34 -25
  44. package/dist/components/Pack.svelte +21 -14
  45. package/dist/components/Partition.svelte +35 -20
  46. package/dist/components/Pattern.svelte +8 -6
  47. package/dist/components/Pie.svelte +76 -57
  48. package/dist/components/Point.svelte +11 -7
  49. package/dist/components/Points.svelte +178 -143
  50. package/dist/components/RadialGradient.svelte +25 -18
  51. package/dist/components/Rect.svelte +33 -19
  52. package/dist/components/RectClipPath.svelte +16 -11
  53. package/dist/components/Rule.svelte +50 -42
  54. package/dist/components/Sankey.svelte +55 -30
  55. package/dist/components/Spline.svelte +118 -96
  56. package/dist/components/Text.svelte +137 -104
  57. package/dist/components/Threshold.svelte +18 -7
  58. package/dist/components/TileImage.svelte +56 -50
  59. package/dist/components/TransformContext.svelte +235 -135
  60. package/dist/components/TransformControls.svelte +57 -29
  61. package/dist/components/TransformControls.svelte.d.ts +1 -1
  62. package/dist/components/Tree.svelte +33 -23
  63. package/dist/components/Treemap.svelte +69 -41
  64. package/dist/components/Voronoi.svelte +55 -28
  65. package/dist/components/charts/AreaChart.svelte +128 -77
  66. package/dist/components/charts/AreaChart.svelte.d.ts +1 -1
  67. package/dist/components/charts/BarChart.svelte +169 -104
  68. package/dist/components/charts/BarChart.svelte.d.ts +1 -1
  69. package/dist/components/charts/LineChart.svelte +87 -43
  70. package/dist/components/charts/LineChart.svelte.d.ts +1 -1
  71. package/dist/components/charts/PieChart.svelte +102 -52
  72. package/dist/components/charts/PieChart.svelte.d.ts +1 -1
  73. package/dist/components/charts/ScatterChart.svelte +73 -38
  74. package/dist/components/charts/ScatterChart.svelte.d.ts +1 -1
  75. package/dist/components/layout/Canvas.svelte +63 -43
  76. package/dist/components/layout/Html.svelte +28 -18
  77. package/dist/components/layout/Svg.svelte +47 -32
  78. package/dist/components/tooltip/Tooltip.svelte +137 -91
  79. package/dist/components/tooltip/Tooltip.svelte.d.ts +1 -1
  80. package/dist/components/tooltip/TooltipContext.svelte +315 -249
  81. package/dist/components/tooltip/TooltipHeader.svelte +9 -3
  82. package/dist/components/tooltip/TooltipItem.svelte +17 -9
  83. package/dist/components/tooltip/TooltipList.svelte +2 -1
  84. package/dist/components/tooltip/TooltipSeparator.svelte +3 -2
  85. package/dist/docs/Blockquote.svelte +4 -3
  86. package/dist/docs/Code.svelte +15 -8
  87. package/dist/docs/CurveMenuField.svelte +17 -12
  88. package/dist/docs/GeoDebug.svelte +13 -9
  89. package/dist/docs/Header1.svelte +2 -1
  90. package/dist/docs/Json.svelte +6 -4
  91. package/dist/docs/Layout.svelte +6 -6
  92. package/dist/docs/PathDataMenuField.svelte +52 -44
  93. package/dist/docs/Preview.svelte +39 -33
  94. package/dist/docs/TilesetField.svelte +80 -62
  95. package/dist/docs/TransformDebug.svelte +8 -5
  96. package/dist/docs/ViewSourceButton.svelte +13 -9
  97. package/dist/stores/motionStore.d.ts +1 -1
  98. package/dist/utils/scales.d.ts +3 -3
  99. package/package.json +29 -30
@@ -1,33 +1,55 @@
1
- <script>// https://github.com/d3/d3-sankey
2
- import { createEventDispatcher } from 'svelte';
3
- import { sankey as d3Sankey, sankeyLeft, sankeyCenter, sankeyRight, sankeyJustify, } from 'd3-sankey';
4
- import { chartContext } from './ChartContext.svelte';
5
- const dispatch = createEventDispatcher();
6
- const { data, width, height } = chartContext();
7
- export let nodes = (d) => d.nodes;
8
- export let nodeId = (d) => d.index;
9
- /**
10
- * see: https://github.com/d3/d3-sankey#alignments
11
- */
12
- export let nodeAlign = sankeyJustify;
13
- export let nodeWidth = 4;
14
- export let nodePadding = 10;
15
- export let nodeSort = undefined;
16
- export let links = (d) => d.links;
17
- export let linkSort = undefined;
18
- $: sankey = d3Sankey()
1
+ <script lang="ts">
2
+ // https://github.com/d3/d3-sankey
3
+ import { createEventDispatcher } from 'svelte';
4
+ import {
5
+ sankey as d3Sankey,
6
+ sankeyLeft,
7
+ sankeyCenter,
8
+ sankeyRight,
9
+ sankeyJustify,
10
+ type SankeyNode,
11
+ type SankeyLink,
12
+ } from 'd3-sankey';
13
+
14
+ import { chartContext } from './ChartContext.svelte';
15
+
16
+ const dispatch = createEventDispatcher();
17
+
18
+ const { data, width, height } = chartContext();
19
+
20
+ export let nodes = (d: any) => d.nodes;
21
+ export let nodeId = (d: any) => d.index;
22
+ /**
23
+ * see: https://github.com/d3/d3-sankey#alignments
24
+ */
25
+ export let nodeAlign:
26
+ | ((node: SankeyNode<any, any>, n: number) => number)
27
+ | 'left'
28
+ | 'right'
29
+ | 'center'
30
+ | 'justify' = sankeyJustify;
31
+ export let nodeWidth = 4;
32
+ export let nodePadding = 10;
33
+ export let nodeSort = undefined;
34
+
35
+ export let links = (d: any) => d.links;
36
+ export let linkSort = undefined;
37
+
38
+ $: sankey = d3Sankey()
19
39
  .size([$width, $height])
20
40
  .nodes(nodes)
21
41
  .nodeId(nodeId)
22
- .nodeAlign(nodeAlign === 'left'
23
- ? sankeyLeft
24
- : nodeAlign === 'center'
25
- ? sankeyCenter
26
- : nodeAlign === 'right'
42
+ .nodeAlign(
43
+ nodeAlign === 'left'
44
+ ? sankeyLeft
45
+ : nodeAlign === 'center'
46
+ ? sankeyCenter
47
+ : nodeAlign === 'right'
27
48
  ? sankeyRight
28
49
  : nodeAlign === 'justify'
29
- ? sankeyJustify
30
- : nodeAlign)
50
+ ? sankeyJustify
51
+ : nodeAlign
52
+ )
31
53
  .nodeWidth(nodeWidth)
32
54
  .nodePadding(nodePadding)
33
55
  // @ts-expect-error
@@ -35,11 +57,14 @@ $: sankey = d3Sankey()
35
57
  .links(links)
36
58
  // @ts-expect-error
37
59
  .linkSort(linkSort);
38
- // @ts-expect-error
39
- $: sankeyData = sankey($data);
40
- $: _nodes = sankeyData.nodes;
41
- $: _links = sankeyData.links;
42
- $: dispatch('update', sankeyData);
60
+
61
+ // @ts-expect-error
62
+ $: sankeyData = sankey($data);
63
+ type NodeExtraProperties = Record<string, any>;
64
+ $: _nodes = sankeyData.nodes as SankeyNode<NodeExtraProperties, any>[];
65
+ $: _links = sankeyData.links as SankeyLink<NodeExtraProperties, any>[];
66
+
67
+ $: dispatch('update', sankeyData);
43
68
  </script>
44
69
 
45
70
  <slot nodes={_nodes} links={_links} />
@@ -1,115 +1,137 @@
1
- <script>import { tick } from 'svelte';
2
- import { writable } from 'svelte/store';
3
- import { tweened as tweenedStore } from 'svelte/motion';
4
- import { draw as _drawTransition } from 'svelte/transition';
5
- import { cubicInOut } from 'svelte/easing';
6
- import { line as d3Line, lineRadial } from 'd3-shape';
7
- // import { interpolateString } from 'd3-interpolate';
8
- import { interpolatePath } from 'd3-interpolate-path';
9
- import { max } from 'd3-array';
10
- import { cls } from '@layerstack/tailwind';
11
- import { chartContext } from './ChartContext.svelte';
12
- import Group from './Group.svelte';
13
- import { motionStore } from '../stores/motionStore.js';
14
- import { accessor } from '../utils/common.js';
15
- import { isScaleBand } from '../utils/scales.js';
16
- const { data: contextData, xScale, yScale, x: contextX, y: contextY, radial } = chartContext();
17
- /** Override data instead of using context */
18
- export let data = undefined;
19
- /** Pass `<path d={...} />` explicitly instead of calculating from data / context */
20
- export let pathData = undefined;
21
- /** Override `x` accessor from Chart context */
22
- export let x = undefined;
23
- /** Override `y` accessor from Chart context */
24
- export let y = undefined;
25
- /** Interpolate path data using d3-interpolate-path. Works best without `draw` enabled */
26
- export let tweened = undefined;
27
- /** Draw path over time. Works best without `tweened` enabled */
28
- export let draw = undefined;
29
- /**
30
- * Curve of spline drawn. Imported via d3-shape.
31
- *
32
- * @example
33
- * import { curveNatural } from 'd3-shape';
34
- * <Spline curve={curveNatrual} />
35
- *
36
- * @type {CurveFactory | CurveFactoryLineOnly | undefined}
37
- */
38
- export let curve = undefined;
39
- export let defined = undefined;
40
- function getScaleValue(data, scale, accessor) {
1
+ <script lang="ts">
2
+ import { tick } from 'svelte';
3
+ import { writable } from 'svelte/store';
4
+ import { tweened as tweenedStore } from 'svelte/motion';
5
+ import { draw as _drawTransition } from 'svelte/transition';
6
+ import { cubicInOut } from 'svelte/easing';
7
+
8
+ import { line as d3Line, lineRadial } from 'd3-shape';
9
+ import type { CurveFactory, CurveFactoryLineOnly, Line } from 'd3-shape';
10
+ // import { interpolateString } from 'd3-interpolate';
11
+ import { interpolatePath } from 'd3-interpolate-path';
12
+ import { max } from 'd3-array';
13
+ import { cls } from '@layerstack/tailwind';
14
+
15
+ import { chartContext } from './ChartContext.svelte';
16
+ import Group from './Group.svelte';
17
+ import { motionStore } from '../stores/motionStore.js';
18
+ import { accessor, type Accessor } from '../utils/common.js';
19
+ import { isScaleBand } from '../utils/scales.js';
20
+
21
+ const { data: contextData, xScale, yScale, x: contextX, y: contextY, radial } = chartContext();
22
+
23
+ /** Override data instead of using context */
24
+ export let data: any = undefined;
25
+
26
+ /** Pass `<path d={...} />` explicitly instead of calculating from data / context */
27
+ export let pathData: string | undefined | null = undefined;
28
+
29
+ /** Override `x` accessor from Chart context */
30
+ export let x: Accessor = undefined;
31
+ /** Override `y` accessor from Chart context */
32
+ export let y: Accessor = undefined;
33
+
34
+ /** Interpolate path data using d3-interpolate-path. Works best without `draw` enabled */
35
+ export let tweened: boolean | Parameters<typeof tweenedStore>[1] = undefined;
36
+ /** Draw path over time. Works best without `tweened` enabled */
37
+ export let draw: boolean | Parameters<typeof _drawTransition>[1] = undefined;
38
+
39
+ /**
40
+ * Curve of spline drawn. Imported via d3-shape.
41
+ *
42
+ * @example
43
+ * import { curveNatural } from 'd3-shape';
44
+ * <Spline curve={curveNatrual} />
45
+ *
46
+ * @type {CurveFactory | CurveFactoryLineOnly | undefined}
47
+ */
48
+ export let curve: CurveFactory | CurveFactoryLineOnly | undefined = undefined;
49
+ export let defined: Parameters<Line<any>['defined']>[0] | undefined = undefined;
50
+
51
+ function getScaleValue(data: any, scale: typeof $xScale | typeof $yScale, accessor: Function) {
41
52
  let value = accessor(data);
53
+
42
54
  if (Array.isArray(value)) {
43
- value = max(value);
55
+ value = max(value);
44
56
  }
57
+
45
58
  if (scale.domain().length) {
46
- // If scale is defined with domain, map value
47
- return scale(value);
48
- }
49
- else {
50
- // Use raw value
51
- return value;
59
+ // If scale is defined with domain, map value
60
+ return scale(value);
61
+ } else {
62
+ // Use raw value
63
+ return value;
52
64
  }
53
- }
54
- const xAccessor = x ? accessor(x) : $contextX;
55
- const yAccessor = y ? accessor(y) : $contextY;
56
- $: xOffset = isScaleBand($xScale) ? $xScale.bandwidth() / 2 : 0;
57
- $: yOffset = isScaleBand($yScale) ? $yScale.bandwidth() / 2 : 0;
58
- let d = '';
59
- // @ts-expect-error
60
- $: tweenedOptions = tweened ? { interpolate: interpolatePath, ...tweened } : false;
61
- $: tweened_d = motionStore('', { tweened: tweenedOptions });
62
- $: {
65
+ }
66
+
67
+ const xAccessor = x ? accessor(x) : $contextX;
68
+ const yAccessor = y ? accessor(y) : $contextY;
69
+
70
+ $: xOffset = isScaleBand($xScale) ? $xScale.bandwidth() / 2 : 0;
71
+ $: yOffset = isScaleBand($yScale) ? $yScale.bandwidth() / 2 : 0;
72
+
73
+ let d: string | null = '';
74
+ // @ts-expect-error
75
+ $: tweenedOptions = tweened ? { interpolate: interpolatePath, ...tweened } : false;
76
+ $: tweened_d = motionStore('', { tweened: tweenedOptions });
77
+ $: {
63
78
  const path = $radial
64
- ? lineRadial()
65
- .angle((d) => getScaleValue(d, $xScale, xAccessor))
66
- .radius((d) => getScaleValue(d, $yScale, yAccessor))
67
- : d3Line()
68
- .x((d) => getScaleValue(d, $xScale, xAccessor) + xOffset)
69
- .y((d) => getScaleValue(d, $yScale, yAccessor) + yOffset);
79
+ ? lineRadial()
80
+ .angle((d) => getScaleValue(d, $xScale, xAccessor))
81
+ .radius((d) => getScaleValue(d, $yScale, yAccessor))
82
+ : d3Line()
83
+ .x((d) => getScaleValue(d, $xScale, xAccessor) + xOffset)
84
+ .y((d) => getScaleValue(d, $yScale, yAccessor) + yOffset);
85
+
70
86
  path.defined(defined ?? ((d) => xAccessor(d) != null && yAccessor(d) != null));
71
- if (curve)
72
- path.curve(curve);
87
+
88
+ if (curve) path.curve(curve);
89
+
73
90
  d = pathData ?? path(data ?? $contextData) ?? '';
74
91
  tweened_d.set(d);
75
- }
76
- $: drawTransition = draw ? _drawTransition : () => ({});
77
- let key = Symbol();
78
- $: if (draw) {
92
+ }
93
+
94
+ $: drawTransition = draw ? _drawTransition : () => ({});
95
+
96
+ let key = Symbol();
97
+ $: if (draw) {
79
98
  // Anytime the path data changes, redraw
80
99
  $tweened_d;
81
100
  key = Symbol();
82
- }
83
- let pathEl = undefined;
84
- const startPoint = writable(undefined);
85
- $: endPoint = motionStore(undefined, {
101
+ }
102
+
103
+ let pathEl: SVGPathElement | undefined = undefined;
104
+ const startPoint = writable<DOMPoint | undefined>(undefined);
105
+ $: endPoint = motionStore<DOMPoint | undefined>(undefined, {
86
106
  tweened: draw
87
- ? {
88
- duration: (typeof draw === 'object' && draw.duration) || 800,
89
- easing: (typeof draw === 'object' && draw.easing) || cubicInOut,
90
- interpolate(a, b) {
91
- return (t) => {
92
- const totalLength = pathEl?.getTotalLength() ?? 0;
93
- const point = pathEl?.getPointAtLength(totalLength * t);
94
- return point;
95
- };
96
- },
107
+ ? {
108
+ duration: (typeof draw === 'object' && draw.duration) || 800,
109
+ easing: (typeof draw === 'object' && draw.easing) || cubicInOut,
110
+ interpolate(a, b) {
111
+ return (t: number) => {
112
+ const totalLength = pathEl?.getTotalLength() ?? 0;
113
+ const point = pathEl?.getPointAtLength(totalLength * t);
114
+ return point;
115
+ };
116
+ },
97
117
  }
98
- : false,
99
- });
100
- $: {
118
+ : false,
119
+ });
120
+
121
+ $: {
101
122
  if ($$slots.start || $$slots.end) {
102
- // Wait for path data to update DOM, then update
103
- d;
104
- tick().then(() => {
105
- if (pathEl) {
106
- startPoint.set(pathEl.getPointAtLength(0));
107
- const totalLength = pathEl.getTotalLength();
108
- endPoint.set(pathEl.getPointAtLength(totalLength));
109
- }
110
- });
123
+ // Wait for path data to update DOM, then update
124
+ d;
125
+ tick().then(() => {
126
+ if (pathEl) {
127
+ startPoint.set(pathEl.getPointAtLength(0));
128
+
129
+ const totalLength = pathEl.getTotalLength();
130
+ endPoint.set(pathEl.getPointAtLength(totalLength));
131
+ }
132
+ });
111
133
  }
112
- }
134
+ }
113
135
  </script>
114
136
 
115
137
  {#key key}
@@ -1,134 +1,167 @@
1
- <script>import { tick } from 'svelte';
2
- import { cls } from '@layerstack/tailwind';
3
- import { getStringWidth } from '../utils/string.js';
4
- import { motionStore } from '../stores/motionStore.js';
5
- /*
6
- TODO:
7
- - [ ] Handle styled text (use <slot /> to measure?)
8
- - [ ] Simplify by using `alignment-baseline` / `dominant-baseline`, rework multiline or drop support, etc
9
- - https://svelte.dev/repl/f12d3003313a43ba8a0be53e5786f1c7?version=3.44.3
10
- - https://observablehq.com/@neocartocnrs/cheat-sheet-on-texts-in-svg
11
-
12
- Reference:
13
- - https://bl.ocks.org/mbostock/7555321
14
- - https://github.com/airbnb/visx/blob/master/packages/visx-text/src/Text.tsx
15
- - https://airbnb.io/visx/text
16
- - https://github.com/airbnb/visx/blob/master/packages/visx-demo/src/pages/text.tsx
17
- */
18
- /** text value */
19
- export let value = 0;
20
- /** Maximum width to occupy (approximate as words are not split) */
21
- export let width = undefined;
22
- /** x position of the text */
23
- export let x = 0;
24
- export let initialX = x;
25
- /** y position of the text */
26
- export let y = 0;
27
- export let initialY = y;
28
- /** dx offset of the text */
29
- export let dx = 0;
30
- /** dy offset of the text */
31
- export let dy = 0;
32
- /** Desired "line height" of the text, implemented as y offsets */
33
- export let lineHeight = '1em';
34
- /** Cap height of the text */
35
- export let capHeight = '0.71em'; // Magic number from d3
36
- /** Whether to scale the fontSize to accommodate the specified width */
37
- export let scaleToFit = false;
38
- /** Horizontal text anchor */
39
- export let textAnchor = 'start';
40
- /** Vertical text anchor */
41
- export let verticalAnchor = 'end'; // default SVG behavior
42
- /** Rotational angle of the text */
43
- export let rotate = undefined;
44
- let wordsByLines = [];
45
- let wordsWithWidth = [];
46
- let spaceWidth = 0;
47
- let style = undefined; // TODO: read from DOM?
48
- $: words = value != null ? value.toString().split(/(?:(?!\u00A0+)\s+)/) : [];
49
- $: wordsWithWidth = words.map((word) => ({
1
+ <script lang="ts">
2
+ import { tick } from 'svelte';
3
+ import type { spring as springStore, tweened as tweenedStore } from 'svelte/motion';
4
+ import { cls } from '@layerstack/tailwind';
5
+
6
+ import { getStringWidth } from '../utils/string.js';
7
+ import { motionStore } from '../stores/motionStore.js';
8
+
9
+ /*
10
+ TODO:
11
+ - [ ] Handle styled text (use <slot /> to measure?)
12
+ - [ ] Simplify by using `alignment-baseline` / `dominant-baseline`, rework multiline or drop support, etc
13
+ - https://svelte.dev/repl/f12d3003313a43ba8a0be53e5786f1c7?version=3.44.3
14
+ - https://observablehq.com/@neocartocnrs/cheat-sheet-on-texts-in-svg
15
+
16
+ Reference:
17
+ - https://bl.ocks.org/mbostock/7555321
18
+ - https://github.com/airbnb/visx/blob/master/packages/visx-text/src/Text.tsx
19
+ - https://airbnb.io/visx/text
20
+ - https://github.com/airbnb/visx/blob/master/packages/visx-demo/src/pages/text.tsx
21
+ */
22
+
23
+ /** text value */
24
+ export let value: string | number = 0;
25
+
26
+ /** Maximum width to occupy (approximate as words are not split) */
27
+ export let width: number | undefined = undefined;
28
+
29
+ /** x position of the text */
30
+ export let x: string | number = 0;
31
+ export let initialX = x;
32
+
33
+ /** y position of the text */
34
+ export let y: string | number = 0;
35
+ export let initialY = y;
36
+
37
+ /** dx offset of the text */
38
+ export let dx: string | number = 0;
39
+
40
+ /** dy offset of the text */
41
+ export let dy: string | number = 0;
42
+
43
+ /** Desired "line height" of the text, implemented as y offsets */
44
+ export let lineHeight = '1em';
45
+
46
+ /** Cap height of the text */
47
+ export let capHeight = '0.71em'; // Magic number from d3
48
+
49
+ /** Whether to scale the fontSize to accommodate the specified width */
50
+ export let scaleToFit: boolean = false;
51
+
52
+ /** Horizontal text anchor */
53
+ export let textAnchor: 'start' | 'middle' | 'end' | 'inherit' = 'start';
54
+
55
+ /** Vertical text anchor */
56
+ export let verticalAnchor: 'start' | 'middle' | 'end' | 'inherit' = 'end'; // default SVG behavior
57
+
58
+ /** Rotational angle of the text */
59
+ export let rotate: number | undefined = undefined;
60
+
61
+ let wordsByLines: { words: string[]; width?: number }[] = [];
62
+ let wordsWithWidth: { word: string; width: number }[] = [];
63
+ let spaceWidth: number = 0;
64
+
65
+ let style: CSSStyleDeclaration | undefined = undefined; // TODO: read from DOM?
66
+
67
+ $: words = value != null ? value.toString().split(/(?:(?!\u00A0+)\s+)/) : [];
68
+
69
+ $: wordsWithWidth = words.map((word) => ({
50
70
  word,
51
71
  width: getStringWidth(word, style) || 0,
52
- }));
53
- $: spaceWidth = getStringWidth('\u00A0', style) || 0;
54
- $: wordsByLines = wordsWithWidth.reduce((result, item) => {
72
+ }));
73
+
74
+ $: spaceWidth = getStringWidth('\u00A0', style) || 0;
75
+
76
+ $: wordsByLines = wordsWithWidth.reduce((result: typeof wordsByLines, item) => {
55
77
  const currentLine = result[result.length - 1];
56
- if (currentLine &&
57
- (width == null || scaleToFit || (currentLine.width || 0) + item.width + spaceWidth < width)) {
58
- // Word can be added to an existing line
59
- currentLine.words.push(item.word);
60
- currentLine.width = currentLine.width || 0;
61
- currentLine.width += item.width + spaceWidth;
62
- }
63
- else {
64
- // Add first word to line or word is too long to scaleToFit on existing line
65
- const newLine = { words: [item.word], width: item.width };
66
- result.push(newLine);
78
+
79
+ if (
80
+ currentLine &&
81
+ (width == null || scaleToFit || (currentLine.width || 0) + item.width + spaceWidth < width)
82
+ ) {
83
+ // Word can be added to an existing line
84
+ currentLine.words.push(item.word);
85
+ currentLine.width = currentLine.width || 0;
86
+ currentLine.width += item.width + spaceWidth;
87
+ } else {
88
+ // Add first word to line or word is too long to scaleToFit on existing line
89
+ const newLine = { words: [item.word], width: item.width };
90
+ result.push(newLine);
67
91
  }
92
+
68
93
  return result;
69
- }, []);
70
- $: lines = wordsByLines.length;
71
- /**
72
- * Convert css value to pixel value (ex. 0.71em => 11.36)
73
- */
74
- function getPixelValue(cssValue) {
94
+ }, []);
95
+ $: lines = wordsByLines.length;
96
+
97
+ /**
98
+ * Convert css value to pixel value (ex. 0.71em => 11.36)
99
+ */
100
+ function getPixelValue(cssValue: string) {
75
101
  // TODO: Properly measure pixel values using DOM (handle inherited font size, zoom, etc)
76
102
  // @ts-expect-error
77
103
  const [match, value, units] = cssValue.match(/([\d.]+)(\D+)/);
78
104
  const number = Number(value);
79
105
  switch (units) {
80
- case 'px':
81
- return number;
82
- case 'em':
83
- case 'rem':
84
- return number * 16;
85
- default:
86
- return 0;
106
+ case 'px':
107
+ return number;
108
+ case 'em':
109
+ case 'rem':
110
+ return number * 16;
111
+ default:
112
+ return 0;
87
113
  }
88
- }
89
- let startDy = 0;
90
- $: if (verticalAnchor === 'start') {
114
+ }
115
+
116
+ let startDy = 0;
117
+ $: if (verticalAnchor === 'start') {
91
118
  startDy = getPixelValue(capHeight);
92
- }
93
- else if (verticalAnchor === 'middle') {
119
+ } else if (verticalAnchor === 'middle') {
94
120
  startDy = ((lines - 1) / 2) * -getPixelValue(lineHeight) + getPixelValue(capHeight) / 2;
95
- }
96
- else {
121
+ } else {
97
122
  startDy = (lines - 1) * -getPixelValue(lineHeight);
98
- }
99
- let scaleTransform = '';
100
- $: if (scaleToFit &&
123
+ }
124
+
125
+ let scaleTransform = '';
126
+ $: if (
127
+ scaleToFit &&
101
128
  lines > 0 &&
102
129
  typeof x == 'number' &&
103
130
  typeof y == 'number' &&
104
- typeof width == 'number') {
131
+ typeof width == 'number'
132
+ ) {
105
133
  const lineWidth = wordsByLines[0].width || 1;
106
134
  const sx = width / lineWidth;
107
135
  const sy = sx;
108
136
  const originX = x - sx * x;
109
137
  const originY = y - sy * y;
110
138
  scaleTransform = `matrix(${sx}, 0, 0, ${sy}, ${originX}, ${originY})`;
111
- }
112
- else {
139
+ } else {
113
140
  scaleTransform = '';
114
- }
115
- $: rotateTransform = rotate ? `rotate(${rotate}, ${x}, ${y})` : '';
116
- $: transform = `${scaleTransform} ${rotateTransform}`;
117
- function isValidXOrY(xOrY) {
141
+ }
142
+ $: rotateTransform = rotate ? `rotate(${rotate}, ${x}, ${y})` : '';
143
+
144
+ $: transform = `${scaleTransform} ${rotateTransform}`;
145
+
146
+ function isValidXOrY(xOrY: string | number | undefined) {
118
147
  return (
119
- // number that is not NaN or Infinity
120
- (typeof xOrY === 'number' && Number.isFinite(xOrY)) ||
121
- // for percentage
122
- typeof xOrY === 'string');
123
- }
124
- export let spring = undefined;
125
- export let tweened = undefined;
126
- let tweened_x = motionStore(initialX, { spring, tweened });
127
- let tweened_y = motionStore(initialY, { spring, tweened });
128
- $: tick().then(() => {
148
+ // number that is not NaN or Infinity
149
+ (typeof xOrY === 'number' && Number.isFinite(xOrY)) ||
150
+ // for percentage
151
+ typeof xOrY === 'string'
152
+ );
153
+ }
154
+
155
+ export let spring: boolean | Parameters<typeof springStore>[1] = undefined;
156
+ export let tweened: boolean | Parameters<typeof tweenedStore>[1] = undefined;
157
+
158
+ let tweened_x = motionStore(initialX, { spring, tweened });
159
+ let tweened_y = motionStore(initialY, { spring, tweened });
160
+
161
+ $: tick().then(() => {
129
162
  tweened_x.set(x);
130
163
  tweened_y.set(y);
131
- });
164
+ });
132
165
  </script>
133
166
 
134
167
  <!-- `overflow: visible` allow contents to be shown outside element -->
@@ -1,10 +1,21 @@
1
- <script>import { min } from 'd3-array';
2
- import { chartContext } from './ChartContext.svelte';
3
- import Area from './Area.svelte';
4
- import ClipPath from './ClipPath.svelte';
5
- const { y, yDomain } = chartContext();
6
- export let curve = undefined;
7
- export let defined = undefined;
1
+ <script lang="ts">
2
+ /*
3
+ See also:
4
+ - https://observablehq.com/@d3/difference-chart
5
+ - https://github.com/airbnb/visx/issues/245
6
+ */
7
+ import type { ComponentProps } from 'svelte';
8
+ import type { CurveFactory } from 'd3-shape';
9
+ import { min } from 'd3-array';
10
+
11
+ import { chartContext } from './ChartContext.svelte';
12
+ import Area from './Area.svelte';
13
+ import ClipPath from './ClipPath.svelte';
14
+
15
+ const { y, yDomain } = chartContext();
16
+
17
+ export let curve: CurveFactory | undefined = undefined;
18
+ export let defined: ComponentProps<Area>['defined'] | undefined = undefined;
8
19
  </script>
9
20
 
10
21
  <!-- Recreate on curve change as otherwise is 1 state change behind for some reason -->