svelteplot 0.10.3 → 0.11.0

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 (222) hide show
  1. package/dist/Mark.svelte +42 -25
  2. package/dist/Mark.svelte.d.ts +111 -32
  3. package/dist/Plot.svelte +21 -15
  4. package/dist/core/Facet.svelte +1 -1
  5. package/dist/core/FacetAxes.svelte +13 -8
  6. package/dist/core/FacetGrid.svelte +4 -4
  7. package/dist/core/Plot.svelte +41 -35
  8. package/dist/helpers/autoScales.d.ts +3 -3
  9. package/dist/helpers/autoScales.js +28 -18
  10. package/dist/helpers/autoTicks.js +2 -0
  11. package/dist/helpers/callWithProps.d.ts +1 -2
  12. package/dist/helpers/facets.js +0 -1
  13. package/dist/helpers/index.js +1 -1
  14. package/dist/helpers/mergeDeep.d.ts +1 -3
  15. package/dist/helpers/mergeDeep.js +15 -16
  16. package/dist/helpers/projection.d.ts +4 -3
  17. package/dist/helpers/projection.js +17 -5
  18. package/dist/helpers/reduce.d.ts +4 -4
  19. package/dist/helpers/reduce.js +6 -4
  20. package/dist/helpers/regressionLoess.js +2 -1
  21. package/dist/helpers/resolve.d.ts +6 -3
  22. package/dist/helpers/resolve.js +25 -16
  23. package/dist/helpers/scales.d.ts +10 -10
  24. package/dist/helpers/scales.js +43 -13
  25. package/dist/helpers/time.d.ts +10 -3
  26. package/dist/helpers/time.js +2 -1
  27. package/dist/hooks/index.d.ts +2 -0
  28. package/dist/hooks/index.js +2 -0
  29. package/dist/hooks/plotDefaults.d.ts +3 -1
  30. package/dist/hooks/plotDefaults.js +33 -1
  31. package/dist/hooks/usePlot.svelte.d.ts +10 -25
  32. package/dist/hooks/usePlot.svelte.js +8 -7
  33. package/dist/index.d.ts +1 -2
  34. package/dist/index.js +1 -3
  35. package/dist/marks/Area.svelte +24 -13
  36. package/dist/marks/Area.svelte.d.ts +118 -34
  37. package/dist/marks/AreaX.svelte +42 -8
  38. package/dist/marks/AreaX.svelte.d.ts +154 -71
  39. package/dist/marks/AreaY.svelte +42 -8
  40. package/dist/marks/AreaY.svelte.d.ts +154 -71
  41. package/dist/marks/Arrow.svelte +42 -23
  42. package/dist/marks/Arrow.svelte.d.ts +114 -35
  43. package/dist/marks/AxisX.svelte +43 -28
  44. package/dist/marks/AxisX.svelte.d.ts +125 -40
  45. package/dist/marks/AxisY.svelte +43 -26
  46. package/dist/marks/AxisY.svelte.d.ts +127 -40
  47. package/dist/marks/BarX.svelte +12 -10
  48. package/dist/marks/BarX.svelte.d.ts +104 -32
  49. package/dist/marks/BarY.svelte +11 -10
  50. package/dist/marks/BarY.svelte.d.ts +106 -34
  51. package/dist/marks/BollingerX.svelte +4 -7
  52. package/dist/marks/BollingerX.svelte.d.ts +105 -30
  53. package/dist/marks/BollingerY.svelte +3 -0
  54. package/dist/marks/BollingerY.svelte.d.ts +105 -30
  55. package/dist/marks/BoxX.svelte +3 -3
  56. package/dist/marks/BoxY.svelte +12 -9
  57. package/dist/marks/BoxY.svelte.d.ts +128 -53
  58. package/dist/marks/Brush.svelte +26 -21
  59. package/dist/marks/Brush.svelte.d.ts +119 -60
  60. package/dist/marks/Cell.svelte +13 -9
  61. package/dist/marks/Cell.svelte.d.ts +105 -30
  62. package/dist/marks/CellX.svelte +2 -1
  63. package/dist/marks/CellX.svelte.d.ts +105 -32
  64. package/dist/marks/CellY.svelte +2 -1
  65. package/dist/marks/CellY.svelte.d.ts +105 -32
  66. package/dist/marks/ColorLegend.svelte +24 -13
  67. package/dist/marks/ColorLegend.svelte.d.ts +1 -0
  68. package/dist/marks/CustomMark.svelte +16 -10
  69. package/dist/marks/CustomMark.svelte.d.ts +112 -31
  70. package/dist/marks/CustomMarkHTML.svelte +8 -2
  71. package/dist/marks/CustomMarkHTML.svelte.d.ts +8 -2
  72. package/dist/marks/DifferenceY.svelte +31 -20
  73. package/dist/marks/DifferenceY.svelte.d.ts +134 -55
  74. package/dist/marks/Dot.svelte +21 -11
  75. package/dist/marks/Dot.svelte.d.ts +117 -38
  76. package/dist/marks/DotX.svelte +2 -0
  77. package/dist/marks/DotX.svelte.d.ts +136 -62
  78. package/dist/marks/DotY.svelte +1 -0
  79. package/dist/marks/DotY.svelte.d.ts +135 -62
  80. package/dist/marks/Frame.svelte +47 -9
  81. package/dist/marks/Frame.svelte.d.ts +124 -41
  82. package/dist/marks/Geo.svelte +21 -12
  83. package/dist/marks/Geo.svelte.d.ts +105 -30
  84. package/dist/marks/Graticule.svelte +3 -0
  85. package/dist/marks/Graticule.svelte.d.ts +3 -0
  86. package/dist/marks/GridX.svelte +31 -16
  87. package/dist/marks/GridX.svelte.d.ts +108 -32
  88. package/dist/marks/GridY.svelte +30 -15
  89. package/dist/marks/GridY.svelte.d.ts +108 -32
  90. package/dist/marks/HTMLTooltip.svelte +14 -7
  91. package/dist/marks/HTMLTooltip.svelte.d.ts +7 -0
  92. package/dist/marks/Image.svelte +50 -25
  93. package/dist/marks/Image.svelte.d.ts +117 -35
  94. package/dist/marks/Line.svelte +67 -44
  95. package/dist/marks/Line.svelte.d.ts +119 -30
  96. package/dist/marks/LineX.svelte +2 -1
  97. package/dist/marks/LineX.svelte.d.ts +142 -69
  98. package/dist/marks/LineY.svelte +2 -1
  99. package/dist/marks/LineY.svelte.d.ts +142 -69
  100. package/dist/marks/Link.svelte +70 -46
  101. package/dist/marks/Link.svelte.d.ts +126 -41
  102. package/dist/marks/Pointer.svelte +24 -15
  103. package/dist/marks/Pointer.svelte.d.ts +7 -0
  104. package/dist/marks/Rect.svelte +13 -5
  105. package/dist/marks/Rect.svelte.d.ts +116 -35
  106. package/dist/marks/RectX.svelte +6 -3
  107. package/dist/marks/RectX.svelte.d.ts +158 -12
  108. package/dist/marks/RectY.svelte +6 -3
  109. package/dist/marks/RectY.svelte.d.ts +158 -12
  110. package/dist/marks/RegressionX.svelte +13 -6
  111. package/dist/marks/RegressionX.svelte.d.ts +8 -3
  112. package/dist/marks/RegressionY.svelte +13 -6
  113. package/dist/marks/RegressionY.svelte.d.ts +8 -3
  114. package/dist/marks/RuleX.svelte +18 -11
  115. package/dist/marks/RuleX.svelte.d.ts +112 -32
  116. package/dist/marks/RuleY.svelte +19 -12
  117. package/dist/marks/RuleY.svelte.d.ts +114 -34
  118. package/dist/marks/Spike.svelte +11 -5
  119. package/dist/marks/Spike.svelte.d.ts +146 -68
  120. package/dist/marks/Text.svelte +24 -7
  121. package/dist/marks/Text.svelte.d.ts +253 -75
  122. package/dist/marks/TickX.svelte +56 -48
  123. package/dist/marks/TickX.svelte.d.ts +114 -40
  124. package/dist/marks/TickY.svelte +59 -51
  125. package/dist/marks/TickY.svelte.d.ts +117 -43
  126. package/dist/marks/Trail.svelte +25 -13
  127. package/dist/marks/Trail.svelte.d.ts +116 -33
  128. package/dist/marks/Vector.svelte +20 -11
  129. package/dist/marks/Vector.svelte.d.ts +116 -35
  130. package/dist/marks/WaffleX.svelte +18 -16
  131. package/dist/marks/WaffleX.svelte.d.ts +131 -57
  132. package/dist/marks/WaffleY.svelte +16 -15
  133. package/dist/marks/WaffleY.svelte.d.ts +129 -56
  134. package/dist/marks/helpers/Anchor.svelte +17 -2
  135. package/dist/marks/helpers/Anchor.svelte.d.ts +16 -1
  136. package/dist/marks/helpers/AreaCanvas.svelte +8 -8
  137. package/dist/marks/helpers/BaseAxisX.svelte +38 -41
  138. package/dist/marks/helpers/BaseAxisX.svelte.d.ts +11 -17
  139. package/dist/marks/helpers/BaseAxisY.svelte +35 -35
  140. package/dist/marks/helpers/BaseAxisY.svelte.d.ts +12 -15
  141. package/dist/marks/helpers/Box.svelte +35 -28
  142. package/dist/marks/helpers/Box.svelte.d.ts +122 -50
  143. package/dist/marks/helpers/DotCanvas.svelte +11 -9
  144. package/dist/marks/helpers/GeoCanvas.svelte +7 -6
  145. package/dist/marks/helpers/LineCanvas.svelte +7 -7
  146. package/dist/marks/helpers/LinearGradientX.svelte +2 -2
  147. package/dist/marks/helpers/LinearGradientX.svelte.d.ts +1 -1
  148. package/dist/marks/helpers/LinearGradientY.svelte +2 -2
  149. package/dist/marks/helpers/LinearGradientY.svelte.d.ts +1 -1
  150. package/dist/marks/helpers/Marker.svelte +2 -2
  151. package/dist/marks/helpers/MarkerPath.svelte +15 -12
  152. package/dist/marks/helpers/MarkerPath.svelte.d.ts +105 -32
  153. package/dist/marks/helpers/MultilineText.svelte +24 -17
  154. package/dist/marks/helpers/MultilineText.svelte.d.ts +1 -1
  155. package/dist/marks/helpers/RectCanvas.svelte +31 -26
  156. package/dist/marks/helpers/RectPath.svelte +2 -2
  157. package/dist/marks/helpers/Regression.svelte +176 -86
  158. package/dist/marks/helpers/Regression.svelte.d.ts +20 -8
  159. package/dist/marks/helpers/RuleCanvas.svelte +9 -6
  160. package/dist/marks/helpers/TextCanvas.svelte +13 -9
  161. package/dist/marks/helpers/TextCanvas.svelte.d.ts +6 -6
  162. package/dist/marks/helpers/TickCanvas.svelte +6 -5
  163. package/dist/marks/helpers/TrailCanvas.svelte +16 -18
  164. package/dist/marks/helpers/TrailCanvas.svelte.d.ts +3 -5
  165. package/dist/marks/helpers/canvas.js +16 -9
  166. package/dist/marks/helpers/events.d.ts +2 -2
  167. package/dist/marks/helpers/events.js +14 -7
  168. package/dist/marks/helpers/waffle.d.ts +3 -3
  169. package/dist/marks/helpers/waffle.js +6 -4
  170. package/dist/regression/polynomial.d.ts +1 -1
  171. package/dist/regression/polynomial.js +5 -5
  172. package/dist/regression/utils/determination.d.ts +1 -1
  173. package/dist/regression/utils/determination.js +1 -1
  174. package/dist/regression/utils/geometry.d.ts +1 -1
  175. package/dist/regression/utils/interpose.d.ts +1 -1
  176. package/dist/regression/utils/interpose.js +1 -1
  177. package/dist/regression/utils/points.d.ts +1 -1
  178. package/dist/transforms/bin.d.ts +3 -3
  179. package/dist/transforms/bin.js +29 -20
  180. package/dist/transforms/bollinger.d.ts +8 -0
  181. package/dist/transforms/bollinger.js +9 -1
  182. package/dist/transforms/centroid.d.ts +4 -0
  183. package/dist/transforms/centroid.js +4 -0
  184. package/dist/transforms/density.d.ts +4 -4
  185. package/dist/transforms/density.js +20 -13
  186. package/dist/transforms/dodge.d.ts +12 -1
  187. package/dist/transforms/dodge.js +15 -6
  188. package/dist/transforms/group.d.ts +141 -4
  189. package/dist/transforms/group.js +4 -1
  190. package/dist/transforms/interval.d.ts +204 -60
  191. package/dist/transforms/jitter.d.ts +421 -4
  192. package/dist/transforms/jitter.js +10 -1
  193. package/dist/transforms/map.d.ts +412 -4
  194. package/dist/transforms/map.js +3 -3
  195. package/dist/transforms/normalize.d.ts +276 -5
  196. package/dist/transforms/normalize.js +5 -3
  197. package/dist/transforms/recordize.d.ts +17 -5
  198. package/dist/transforms/recordize.js +13 -9
  199. package/dist/transforms/rename.d.ts +11 -4
  200. package/dist/transforms/rename.js +7 -2
  201. package/dist/transforms/select.d.ts +722 -210
  202. package/dist/transforms/select.js +13 -1
  203. package/dist/transforms/shift.d.ts +8 -0
  204. package/dist/transforms/shift.js +20 -6
  205. package/dist/transforms/sort.d.ts +13 -258
  206. package/dist/transforms/sort.js +13 -10
  207. package/dist/transforms/stack.d.ts +58 -9
  208. package/dist/transforms/stack.js +27 -11
  209. package/dist/transforms/window.d.ts +221 -66
  210. package/dist/transforms/window.js +8 -2
  211. package/dist/types/axes.d.ts +43 -0
  212. package/dist/types/axes.js +1 -0
  213. package/dist/types/channel.d.ts +30 -2
  214. package/dist/types/data.d.ts +14 -1
  215. package/dist/types/facet.d.ts +5 -0
  216. package/dist/types/index.d.ts +33 -8
  217. package/dist/types/index.js +11 -7
  218. package/dist/types/mark.d.ts +124 -35
  219. package/dist/types/plot.d.ts +118 -16
  220. package/dist/types/scale.d.ts +125 -8
  221. package/package.json +178 -175
  222. package/dist/helpers/autoTicks.d.ts +0 -12
@@ -1,4 +1,4 @@
1
- import { CSS_URL, CSS_VAR } from '../../constants';
1
+ import { CSS_URL, CSS_VAR } from '../../constants.js';
2
2
  export function resolveColor(color, canvas) {
3
3
  if (`${color}`.toLowerCase() === 'currentcolor') {
4
4
  color = getComputedStyle(canvas?.parentElement?.parentElement).getPropertyValue('color');
@@ -9,20 +9,27 @@ export function resolveColor(color, canvas) {
9
9
  if (CSS_URL.test(color)) {
10
10
  // might be a gradient we can parse!
11
11
  const m = color.match(/^url\((#[^)]+)\)/);
12
+ if (!m)
13
+ return color;
12
14
  const gradientId = m[1];
13
15
  const gradient = canvas.ownerDocument.querySelector(gradientId);
14
16
  if (gradient) {
15
17
  // parse gradient
16
18
  if (gradient.nodeName.toLowerCase() === 'lineargradient') {
17
- const x0 = +gradient.getAttribute('x1');
18
- const x1 = +gradient.getAttribute('x2');
19
- const y0 = +gradient.getAttribute('y1');
20
- const y1 = +gradient.getAttribute('y2');
21
- const ctxGradient = canvas.getContext('2d').createLinearGradient(x0, y0, x1, y1);
19
+ const x0 = +(gradient.getAttribute('x1') ?? 0);
20
+ const x1 = +(gradient.getAttribute('x2') ?? 0);
21
+ const y0 = +(gradient.getAttribute('y1') ?? 0);
22
+ const y1 = +(gradient.getAttribute('y2') ?? 0);
23
+ const ctx = canvas.getContext('2d');
24
+ // If we cannot obtain a 2D context, fall back to a safe color instead of returning
25
+ // the original gradient URL (e.g., "url(#gradient1)"), which is not a valid canvas color.
26
+ if (!ctx)
27
+ return 'transparent';
28
+ const ctxGradient = ctx.createLinearGradient(x0, y0, x1, y1);
22
29
  for (const stop of gradient.querySelectorAll('stop')) {
23
- const offset = +stop.getAttribute('offset');
24
- const color = resolveColor(stop.getAttribute('stop-color'), canvas);
25
- ctxGradient.addColorStop(Math.min(1, Math.max(0, offset)), color);
30
+ const offset = +(stop.getAttribute('offset') ?? 0);
31
+ const stopColor = resolveColor(stop.getAttribute('stop-color') ?? '', canvas);
32
+ ctxGradient.addColorStop(Math.min(1, Math.max(0, offset)), stopColor);
26
33
  }
27
34
  return ctxGradient;
28
35
  }
@@ -2,8 +2,8 @@ import type { BaseMarkProps, DataRecord, DataRow, PlotState } from '../../types/
2
2
  import type { Attachment } from 'svelte/attachments';
3
3
  declare global {
4
4
  interface MouseEvent {
5
- layerX?: number;
6
- layerY?: number;
5
+ readonly layerX: number;
6
+ readonly layerY: number;
7
7
  dataX?: number | string | Date;
8
8
  dataY?: number | string | Date;
9
9
  }
@@ -72,7 +72,10 @@ export function addEventHandlers({ options, datum, plot }) {
72
72
  const relativeX = origEvent.clientX - facetRect.left + (plotOptions.marginLeft ?? 0);
73
73
  const relativeY = origEvent.clientY - facetRect.top + (plotOptions.marginTop ?? 0);
74
74
  if (scales.projection) {
75
- const [x, y] = scales.projection.invert([relativeX, relativeY]);
75
+ const [x, y] = scales.projection.invert([
76
+ relativeX,
77
+ relativeY
78
+ ]);
76
79
  origEvent.dataX = x;
77
80
  origEvent.dataY = y;
78
81
  }
@@ -100,13 +103,17 @@ export function addEventHandlers({ options, datum, plot }) {
100
103
  }
101
104
  function invertScale(scale, position) {
102
105
  if (scale.type === 'band') {
103
- const range = scale.fn.range();
104
- const domain = scale.fn.domain();
105
- const eachBand = scale.fn.step();
106
- const extent = range[1] - range[0];
107
- const posInRange = (position - range[0]) * Math.sign(extent);
106
+ const bandScale = scale.fn;
107
+ const range = bandScale.range();
108
+ const domain = bandScale.domain();
109
+ const eachBand = bandScale.step();
110
+ const start = range[0] ?? 0;
111
+ const end = range[1] ?? start;
112
+ const extent = end - start;
113
+ const posInRange = (position - start) * Math.sign(extent);
108
114
  const index = Math.floor(posInRange / eachBand);
109
115
  return domain[index];
110
116
  }
111
- return scale.fn.invert ? scale.fn.invert(position) : undefined;
117
+ const maybeInvert = scale.fn;
118
+ return maybeInvert.invert ? maybeInvert.invert(position) : undefined;
112
119
  }
@@ -5,8 +5,8 @@
5
5
  * Kept the comments from the original implementation for clarity.
6
6
  */
7
7
  import type { Snippet } from 'svelte';
8
- import type { StackOptions } from '../../transforms/stack';
9
- import type { BorderRadius, ConstantAccessor, PlotScales, ScaledDataRecord } from '../../types';
8
+ import type { StackOptions } from '../../transforms/stack.js';
9
+ import type { BorderRadius, ConstantAccessor, PlotScales, ScaledDataRecord } from '../../types/index.js';
10
10
  type Point = [number, number];
11
11
  export type WaffleOptions<T> = {
12
12
  /**
@@ -58,7 +58,7 @@ type WaffleProps = {
58
58
  d: string;
59
59
  };
60
60
  };
61
- export declare function wafflePolygon(y: 'x' | 'y', options: WaffleOptions, scales: PlotScales): (d: ScaledDataRecord) => WaffleProps;
61
+ export declare function wafflePolygon(y: 'x' | 'y', options: WaffleOptions<any>, scales: PlotScales): (d: ScaledDataRecord) => WaffleProps;
62
62
  export declare function wafflePoints(i1: number, i2: number, columns: number): Point[];
63
63
  export declare function maybeRound(round: boolean | ((x: number) => number) | undefined): (x: number) => number;
64
64
  export {};
@@ -4,18 +4,20 @@
4
4
  *
5
5
  * Kept the comments from the original implementation for clarity.
6
6
  */
7
- import { getPatternId } from '../../helpers/getBaseStyles';
7
+ import { getPatternId } from '../../helpers/getBaseStyles.js';
8
8
  export function wafflePolygon(y, options, scales) {
9
9
  const x = y === 'y' ? 'x' : 'y';
10
10
  const y1 = `${y}1`;
11
11
  const y2 = `${y}2`;
12
12
  const xScale = scales[x];
13
13
  const yScale = scales[y];
14
- const barwidth = xScale.fn.bandwidth();
14
+ const xBandScale = xScale.fn;
15
+ const mapY = (value) => yScale.fn(value) ?? 0;
16
+ const barwidth = xBandScale.bandwidth();
15
17
  const { unit = 1, gap = 1 } = options;
16
18
  const round = maybeRound(options.round);
17
19
  // The length of a unit along y in pixels.
18
- const scale = Math.abs(yScale.fn(unit) - yScale.fn(0));
20
+ const scale = Math.abs(mapY(unit) - mapY(0));
19
21
  // The number of cells on each row (or column) of the waffle.
20
22
  const multiple = options.multiple ?? Math.max(1, Math.floor(Math.sqrt(barwidth / scale)));
21
23
  // The outer size of each square cell, in pixels, including the gap.
@@ -26,7 +28,7 @@ export function wafflePolygon(y, options, scales) {
26
28
  const transform = y === 'y' ? ([x, y]) => [x * cx, -y * cy] : ([x, y]) => [y * cy, x * cx];
27
29
  // const mx = typeof x0 === 'function' ? (i) => x0(i) - barwidth / 2 : () => x0;
28
30
  const [ix, iy] = y === 'y' ? [0, 1] : [1, 0];
29
- const y0 = yScale.fn(0);
31
+ const y0 = mapY(0);
30
32
  const mx = -barwidth / 2;
31
33
  return (d) => {
32
34
  const y1val = d.resolved[y1];
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Adapted from https://github.com/HarryStevens/d3-regression
3
3
  */
4
- import type { PredictFunction, Accessor, DataPoint, Domain } from './types';
4
+ import type { PredictFunction, Accessor, DataPoint, Domain } from './types.js';
5
5
  export type PolynomialOutput = [DataPoint, DataPoint] & {
6
6
  coefficients: number[];
7
7
  predict: PredictFunction;
@@ -7,11 +7,11 @@
7
7
  // ...with ideas from vega-statistics by Jeffrey Heer
8
8
  // Source: https://github.com/vega/vega/blob/f21cb8792b4e0cbe2b1a3fd44b0f5db370dbaadb/packages/vega-statistics/src/regression/poly.js
9
9
  // License: https://github.com/vega/vega/blob/f058b099decad9db78301405dd0d2e9d8ba3d51a/LICENSE
10
- import { determination } from './utils/determination';
11
- import { interpose } from './utils/interpose';
12
- import { points, visitPoints } from './utils/points';
13
- import linear from './linear';
14
- import quad from './quadratic';
10
+ import { determination } from './utils/determination.js';
11
+ import { interpose } from './utils/interpose.js';
12
+ import { points, visitPoints } from './utils/points.js';
13
+ import linear from './linear.js';
14
+ import quad from './quadratic.js';
15
15
  export default function polynomial() {
16
16
  let x = (d) => d[0], y = (d) => d[1], order = 3, domain;
17
17
  const polynomialRegression = function polynomialRegression(data) {
@@ -1,4 +1,4 @@
1
- import type { Accessor, PredictFunction } from '../types';
1
+ import type { Accessor, PredictFunction } from '../types.js';
2
2
  /**
3
3
  * Given a dataset, x- and y-accessors, the mean center of the y-values (uY),
4
4
  * and a prediction function, return the coefficient of determination, R^2.
@@ -1,4 +1,4 @@
1
- import { visitPoints } from './points';
1
+ import { visitPoints } from './points.js';
2
2
  /**
3
3
  * Given a dataset, x- and y-accessors, the mean center of the y-values (uY),
4
4
  * and a prediction function, return the coefficient of determination, R^2.
@@ -1,4 +1,4 @@
1
- import type { DataPoint } from '../types';
1
+ import type { DataPoint } from '../types.js';
2
2
  /**
3
3
  * Returns the angle of a line in degrees.
4
4
  */
@@ -1,4 +1,4 @@
1
- import type { PredictFunction, DataPoint } from '../types';
1
+ import type { PredictFunction, DataPoint } from '../types.js';
2
2
  /**
3
3
  * Given a start point (xmin), an end point (xmax),
4
4
  * and a prediction function, returns a smooth line.
@@ -1,4 +1,4 @@
1
- import { angle, midpoint } from './geometry';
1
+ import { angle, midpoint } from './geometry.js';
2
2
  /**
3
3
  * Given a start point (xmin), an end point (xmax),
4
4
  * and a prediction function, returns a smooth line.
@@ -1,4 +1,4 @@
1
- import type { Accessor } from '../types';
1
+ import type { Accessor } from '../types.js';
2
2
  /**
3
3
  * Adapted from vega-statistics by Jeffrey Heer
4
4
  * License: https://github.com/vega/vega/blob/f058b099decad9db78301405dd0d2e9d8ba3d51a/LICENSE
@@ -36,16 +36,16 @@ type BinOptions = BinBaseOptions & AdditionalOutputChannels;
36
36
  * @param param0
37
37
  * @param options
38
38
  */
39
- export declare function binX<T>({ data, ...channels }: TransformArg<T, DataRecord>, options?: BinXOptions): TransformArg<T, DataRecord>;
39
+ export declare function binX({ data, ...channels }: TransformArg<DataRecord>, options?: BinXOptions): TransformArg<DataRecord>;
40
40
  /**
41
41
  * Bins on y. Also groups on y and the first channel of z, fill, or stroke, if any.
42
42
  *
43
43
  * @param param0
44
44
  * @param options
45
45
  */
46
- export declare function binY<T>({ data, ...channels }: TransformArg<T, DataRecord>, options?: BinYOptions): TransformArg<T, DataRecord>;
46
+ export declare function binY({ data, ...channels }: TransformArg<DataRecord>, options?: BinYOptions): TransformArg<DataRecord>;
47
47
  /**
48
48
  * for binning in x and y dimension simultaneously
49
49
  */
50
- export declare function bin<T>({ data, ...channels }: TransformArg<T, DataRecord>, options?: BinOptions): TransformArg<T, DataRecord>;
50
+ export declare function bin({ data, ...channels }: TransformArg<DataRecord>, options?: BinOptions): TransformArg<DataRecord>;
51
51
  export {};
@@ -4,7 +4,7 @@ import { bin as d3Bin, extent, thresholdFreedmanDiaconis, thresholdScott, thresh
4
4
  import { reduceOutputs } from '../helpers/reduce.js';
5
5
  import { groupFacetsAndZ } from '../helpers/group.js';
6
6
  import { isDate } from '../helpers/typeChecks.js';
7
- import { ORIGINAL_NAME_KEYS } from '../constants';
7
+ import { ORIGINAL_NAME_KEYS } from '../constants.js';
8
8
  const CHANNELS = {
9
9
  x: Symbol('x'),
10
10
  x1: Symbol('x1'),
@@ -26,7 +26,9 @@ function binBy(byDim, { data, ...channels }, options) {
26
26
  bin.domain(domain);
27
27
  if (interval) {
28
28
  const [lo, hi] = extent(data.map((d) => resolveChannel(byDim, d, channels)));
29
- bin.thresholds(maybeInterval(interval).range(lo, hi));
29
+ const iv = maybeInterval(interval);
30
+ if (iv)
31
+ bin.thresholds(iv.range(lo, hi));
30
32
  }
31
33
  else if (thresholds)
32
34
  bin.thresholds(
@@ -58,13 +60,15 @@ function binBy(byDim, { data, ...channels }, options) {
58
60
  const newData = [];
59
61
  let passedGroups = [];
60
62
  const bins = bin(data);
61
- (options.cumulative < 0 ? bins.toReversed() : bins).forEach((group) => {
63
+ ((options.cumulative ?? 0) < 0 ? bins.toReversed() : bins).forEach((group) => {
64
+ const x0 = group.x0;
65
+ const x1 = group.x1;
62
66
  const itemBinProps = {
63
- [CHANNELS[`${byDim}1`]]: group.x0,
64
- [CHANNELS[`${byDim}2`]]: group.x1,
65
- [CHANNELS[`${byDim}`]]: isDate(group.x0)
66
- ? new Date(Math.round((group.x0.getTime() + group.x1.getTime()) * 0.5))
67
- : (group.x0 + group.x1) * 0.5
67
+ [CHANNELS[`${byDim}1`]]: x0,
68
+ [CHANNELS[`${byDim}2`]]: x1,
69
+ [CHANNELS[`${byDim}`]]: isDate(x0)
70
+ ? new Date(Math.round((x0.getTime() + x1.getTime()) * 0.5))
71
+ : ((x0 ?? 0) + (x1 ?? 0)) * 0.5
68
72
  };
69
73
  if (options.cumulative)
70
74
  passedGroups = [...passedGroups, ...group];
@@ -99,7 +103,7 @@ export function binY({ data, ...channels }, options = { thresholds: 'auto', cumu
99
103
  * for binning in x and y dimension simultaneously
100
104
  */
101
105
  export function bin({ data, ...channels }, options = { thresholds: 'auto', cumulative: false }) {
102
- const { domain, thresholds = 'auto', interval, cumulative = false } = options;
106
+ const { domain, thresholds = 'auto', interval } = options;
103
107
  const binX = d3Bin();
104
108
  const binY = d3Bin();
105
109
  if (domain) {
@@ -114,8 +118,11 @@ export function bin({ data, ...channels }, options = { thresholds: 'auto', cumul
114
118
  if (interval) {
115
119
  const [xlo, xhi] = extent(data.map((d) => resolveChannel('x', d, channels)));
116
120
  const [ylo, yhi] = extent(data.map((d) => resolveChannel('y', d, channels)));
117
- binX.thresholds(maybeInterval(interval).range(xlo, xhi));
118
- binY.thresholds((yThresholds = maybeInterval(interval).range(ylo, yhi)));
121
+ const ivl = maybeInterval(interval);
122
+ if (ivl) {
123
+ binX.thresholds(ivl.range(xlo, xhi));
124
+ binY.thresholds((yThresholds = ivl.range(ylo, yhi)));
125
+ }
119
126
  }
120
127
  else if (thresholds) {
121
128
  // when binning in x and y, we need to ensure we are using consistent thresholds
@@ -150,17 +157,19 @@ export function bin({ data, ...channels }, options = { thresholds: 'auto', cumul
150
157
  // consistent intervals
151
158
  const newData = [];
152
159
  binX(data).forEach((groupX) => {
160
+ const gx0 = groupX.x0;
161
+ const gx1 = groupX.x1;
153
162
  const newRecordBaseX = {
154
- [CHANNELS.x1]: groupX.x0,
155
- [CHANNELS.x2]: groupX.x1,
156
- [CHANNELS.x]: isDate(groupX.x0)
157
- ? new Date(Math.round((groupX.x0.getTime() + groupX.x1.getTime()) * 0.5))
158
- : (groupX.x0 + groupX.x1) * 0.5
163
+ [CHANNELS.x1]: gx0,
164
+ [CHANNELS.x2]: gx1,
165
+ [CHANNELS.x]: isDate(gx0)
166
+ ? new Date(Math.round((gx0.getTime() + gx1.getTime()) * 0.5))
167
+ : ((gx0 ?? 0) + (gx1 ?? 0)) * 0.5
159
168
  };
160
169
  const [ylo, yhi] = extent(groupX.map((d) => resolveChannel('y', d, channels)));
161
- const tExtentLo = yThresholds.filter((d) => d < ylo).at(-1);
162
- const tExtentHi = yThresholds.filter((d) => d > yhi).at(0);
163
- binY(groupX).forEach((groupY, i) => {
170
+ const tExtentLo = yThresholds.filter((d) => d < ylo).at(-1) ?? ylo;
171
+ const tExtentHi = yThresholds.filter((d) => d > yhi).at(0) ?? yhi;
172
+ binY(groupX).forEach((groupY) => {
164
173
  if (groupY.length === 0)
165
174
  return;
166
175
  // The first bin.x0 is always equal to the minimum domain value,
@@ -174,7 +183,7 @@ export function bin({ data, ...channels }, options = { thresholds: 'auto', cumul
174
183
  [CHANNELS.y2]: y2,
175
184
  [CHANNELS.y]: isDate(y1)
176
185
  ? new Date(Math.round((y1.getTime() + y2.getTime()) * 0.5))
177
- : (y1 + y2) * 0.5
186
+ : ((y1 ?? 0) + (y2 ?? 0)) * 0.5
178
187
  };
179
188
  const newGroupChannels = groupFacetsAndZ(groupY, channels, (items, itemGroupProps) => {
180
189
  const newRecord = {
@@ -9,6 +9,14 @@ export type BollingerOptions = {
9
9
  */
10
10
  k?: number;
11
11
  };
12
+ /**
13
+ * computes Bollinger bands for the x channel, producing x1 (lower), x (mean),
14
+ * and x2 (upper) channels
15
+ */
12
16
  export declare function bollingerX<T>(args: TransformArg<T>, options?: BollingerOptions): TransformArg<T>;
17
+ /**
18
+ * computes Bollinger bands for the y channel, producing y1 (lower), y (mean),
19
+ * and y2 (upper) channels
20
+ */
13
21
  export declare function bollingerY<T>(args: TransformArg<T>, options?: BollingerOptions): TransformArg<T>;
14
22
  export declare function bollingerDim<T>(dim: 'x' | 'y', { data, ...channels }: TransformArg<T>, options?: BollingerOptions): TransformArg<T>;
@@ -1,7 +1,15 @@
1
1
  import { resolveChannel } from '../helpers/resolve.js';
2
+ /**
3
+ * computes Bollinger bands for the x channel, producing x1 (lower), x (mean),
4
+ * and x2 (upper) channels
5
+ */
2
6
  export function bollingerX(args, options = {}) {
3
7
  return bollingerDim('x', args, options);
4
8
  }
9
+ /**
10
+ * computes Bollinger bands for the y channel, producing y1 (lower), y (mean),
11
+ * and y2 (upper) channels
12
+ */
5
13
  export function bollingerY(args, options = {}) {
6
14
  return bollingerDim('y', args, options);
7
15
  }
@@ -38,7 +46,7 @@ function bollinger(values, N, K) {
38
46
  const value = values[i];
39
47
  ((sum += value), (sumSquared += value ** 2));
40
48
  }
41
- for (let n = values.length, m = bands.length; i < n; ++i) {
49
+ for (let n = values.length; i < n; ++i) {
42
50
  const value = values[i];
43
51
  ((sum += value), (sumSquared += value ** 2));
44
52
  const mean = sum / N;
@@ -3,6 +3,10 @@ declare const CENTROID: unique symbol;
3
3
  type WithCentroid<T> = T & {
4
4
  [CENTROID]: [number, number];
5
5
  };
6
+ /**
7
+ * computes the geographic centroid of each geometry feature, producing
8
+ * x (longitude) and y (latitude) channels
9
+ */
6
10
  export declare function geoCentroid<Datum extends DataRecord>({ data, ...options }: {
7
11
  data: Datum[];
8
12
  } & TransformArg<Datum>): TransformArg<WithCentroid<Datum>>;
@@ -1,6 +1,10 @@
1
1
  import { resolveProp } from '../helpers/resolve.js';
2
2
  import { geoCentroid as d3GeoCentroid } from 'd3-geo';
3
3
  const CENTROID = Symbol('centroid');
4
+ /**
5
+ * computes the geographic centroid of each geometry feature, producing
6
+ * x (longitude) and y (latitude) channels
7
+ */
4
8
  export function geoCentroid({ data, ...options }) {
5
9
  const transformedData = data.map((d) => ({
6
10
  ...d,
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * implementation based on science.js by Jason Davies
3
3
  */
4
- import type { TransformArg } from '../types';
5
- type Kernel = 'uniform' | 'triangular' | 'epanechnikov' | 'quartic' | 'triweight' | 'gaussian' | 'cosine' | ((u: number) => number);
6
- type DensityOptions<T> = {
4
+ import type { TransformArg } from '../types/index.js';
5
+ export type KernelName = 'uniform' | 'triangular' | 'epanechnikov' | 'quartic' | 'triweight' | 'gaussian' | 'cosine';
6
+ export type Kernel = KernelName | ((u: number) => number);
7
+ export type DensityOptions<_T> = {
7
8
  /**
8
9
  * The kernel function to use for smoothing.
9
10
  */
@@ -37,4 +38,3 @@ export declare function densityX<T>(args: TransformArg<T>, options: DensityOptio
37
38
  export declare function densityY<T>(args: TransformArg<T>, options: DensityOptions<T> & {
38
39
  channel?: 'x' | 'x1' | 'x2';
39
40
  }): TransformArg<T>;
40
- export {};
@@ -9,11 +9,11 @@
9
9
  *
10
10
  */
11
11
  import { extent, quantileSorted, variance } from 'd3-array';
12
- import { isValid } from '../helpers';
13
- import { maybeInterval } from '../helpers/autoTicks';
14
- import { groupFacetsAndZ } from '../helpers/group';
15
- import isDataRecord from '../helpers/isDataRecord';
16
- import { resolveChannel } from '../helpers/resolve';
12
+ import { isValid } from '../helpers/index.js';
13
+ import { maybeInterval } from '../helpers/autoTicks.js';
14
+ import { groupFacetsAndZ } from '../helpers/group.js';
15
+ import isDataRecord from '../helpers/isDataRecord.js';
16
+ import { resolveChannel } from '../helpers/resolve.js';
17
17
  import { ORIGINAL_NAME_KEYS } from '../constants.js';
18
18
  // see https://github.com/jasondavies/science.js/blob/master/src/stats/kernel.js
19
19
  const KERNEL = {
@@ -80,6 +80,9 @@ const BANDWIDTH_FACTOR = {
80
80
  triweight: 3.15,
81
81
  cosine: 1.06
82
82
  };
83
+ function isKernelName(value) {
84
+ return value in KERNEL;
85
+ }
83
86
  function bandwidthSilverman(x) {
84
87
  const iqr = quantileSorted(x, 0.75) - quantileSorted(x, 0.25);
85
88
  const xvar = variance(x);
@@ -128,17 +131,21 @@ function density1d(independent, { data, weight, ...channels }, options = {}) {
128
131
  }))).filter((d) => isValid(d[VALUE]) && isValid(d[WEIGHT]) && d[WEIGHT] >= 0);
129
132
  const values = resolvedData.map((d) => d[VALUE]);
130
133
  // compute bandwidth from full data
134
+ const kernelName = typeof kernel === 'string' && isKernelName(kernel) ? kernel : null;
131
135
  const bw = typeof bandwidth === 'function'
132
- ? (BANDWIDTH_FACTOR[kernel] ?? 1) * bandwidth(values.toSorted((a, b) => a - b))
136
+ ? ((kernelName ? BANDWIDTH_FACTOR[kernelName] : null) ?? 1) *
137
+ bandwidth(values.toSorted((a, b) => a - b))
133
138
  : bandwidth;
134
139
  const I = maybeInterval(interval ?? roundToTerminating(bw / 5));
135
140
  let [min, max] = extent(values);
136
141
  if (!trim) {
137
- const r = max - min;
138
- min = I.floor(min - r * 0.2);
139
- max = I.floor(max + r * 0.2);
142
+ const r = (max ?? 0) - (min ?? 0);
143
+ min = I.floor((min ?? 0) - r * 0.2);
144
+ max = I.floor((max ?? 0) + r * 0.2);
140
145
  }
141
- const atValues = I.range(I.floor(min), I.offset(max)).map((d) => +d.toFixed(5));
146
+ const atValues = I
147
+ .range(I.floor(min), I.offset(max))
148
+ .map((d) => +d.toFixed(5));
142
149
  // let minX = Infinity;
143
150
  // let maxX = -Infinity;
144
151
  const res = groupFacetsAndZ(resolvedData, channels, (items, groupProps) => {
@@ -149,9 +156,9 @@ function density1d(independent, { data, weight, ...channels }, options = {}) {
149
156
  .sort((a, b) => a[0] - b[0]);
150
157
  if (!trim) {
151
158
  // trim zero values at begin and end except first and last
152
- const firstNonZero = kdeValues.findIndex(([x, v]) => v > 0);
159
+ const firstNonZero = kdeValues.findIndex(([_x, v]) => v > 0);
153
160
  // if (firstNonZero > 0) minX = Math.min(minX, kdeValues[firstNonZero - 1][0]);
154
- const lastNonZero = kdeValues.length - 1 - [...kdeValues].reverse().findIndex(([x, v]) => v > 0);
161
+ const lastNonZero = kdeValues.length - 1 - [...kdeValues].reverse().findIndex(([_x, v]) => v > 0);
155
162
  // if (lastNonZero > -1 && lastNonZero < kdeValues.length - 1)
156
163
  // maxX = Math.max(maxX, kdeValues[lastNonZero + 1][0]);
157
164
  kdeValues = kdeValues.slice(firstNonZero < 1 ? 0 : firstNonZero - 1, lastNonZero < 0 ? kdeValues.length : lastNonZero + 1);
@@ -219,7 +226,7 @@ function kde1d(values, weights, atValues, kernel, bw, cumulative) {
219
226
  function maybeKernel(kernel) {
220
227
  if (typeof kernel === 'function')
221
228
  return kernel;
222
- return KERNEL[kernel] || KERNEL.epanechnikov;
229
+ return isKernelName(kernel) ? KERNEL[kernel] : KERNEL.epanechnikov;
223
230
  }
224
231
  // See <http://en.wikipedia.org/wiki/Kernel_(statistics)>.
225
232
  // science.stats.kernel = {
@@ -1,17 +1,28 @@
1
- import type { ScaledDataRecord, TransformArg, PlotState } from '../types';
1
+ import type { ScaledDataRecord, TransformArg, PlotState } from '../types/index.js';
2
2
  type BaseDodgeOptions = {
3
+ /** the anchor side for placing dodged marks */
3
4
  anchor?: string;
5
+ /** the padding between dodged marks, in pixels */
4
6
  padding?: number;
7
+ /** the radius of dodged marks, in pixels */
5
8
  r?: number;
6
9
  };
7
10
  type AnchorX = 'left' | 'right' | 'middle';
8
11
  type AnchorY = 'top' | 'bottom' | 'middle';
12
+ /** options for horizontal dodge positioning; can be an anchor string or a full options object */
9
13
  export type DodgeXOptions = AnchorX | (BaseDodgeOptions & {
10
14
  anchor?: 'left' | 'right' | 'middle';
11
15
  });
16
+ /** options for vertical dodge positioning; can be an anchor string or a full options object */
12
17
  export type DodgeYOptions = AnchorY | (BaseDodgeOptions & {
13
18
  anchor?: 'top' | 'bottom' | 'middle';
14
19
  });
20
+ /**
21
+ * offsets marks horizontally to avoid overlap, using circle-packing
22
+ */
15
23
  export declare function dodgeX(args: TransformArg<ScaledDataRecord>, plotState: PlotState): ScaledDataRecord[];
24
+ /**
25
+ * offsets marks vertically to avoid overlap, using circle-packing
26
+ */
16
27
  export declare function dodgeY(args: TransformArg<ScaledDataRecord>, plotState: PlotState): ScaledDataRecord[];
17
28
  export {};
@@ -1,9 +1,12 @@
1
1
  import IntervalTree from 'interval-tree-1d';
2
- import { groupFacetsAndZ } from '../helpers/group';
2
+ import { groupFacetsAndZ } from '../helpers/group.js';
3
+ /**
4
+ * offsets marks horizontally to avoid overlap, using circle-packing
5
+ */
3
6
  export function dodgeX(args, plotState) {
4
7
  if (!args.dodgeX)
5
8
  return args.data;
6
- let { anchor = 'left', padding = 1, r = args.dodgeX.r } = maybeAnchor(args.dodgeX);
9
+ let { anchor = 'left', padding = 1, r = args.dodgeX?.r } = maybeAnchor(args.dodgeX);
7
10
  let anchorFunction;
8
11
  switch (`${anchor}`.toLowerCase()) {
9
12
  case 'left':
@@ -20,10 +23,13 @@ export function dodgeX(args, plotState) {
20
23
  }
21
24
  return dodge('x', 'y', anchorFunction, Number(padding), r, args, plotState);
22
25
  }
26
+ /**
27
+ * offsets marks vertically to avoid overlap, using circle-packing
28
+ */
23
29
  export function dodgeY(args, plotState) {
24
30
  if (!args.dodgeY)
25
31
  return args.data;
26
- let { anchor = 'bottom', padding = 1, r = args.dodgeY.r } = maybeAnchor(args.dodgeY);
32
+ let { anchor = 'bottom', padding = 1, r = args.dodgeY?.r } = maybeAnchor(args.dodgeY);
27
33
  let anchorFunction;
28
34
  switch (`${anchor}`.toLowerCase()) {
29
35
  case 'top':
@@ -57,7 +63,9 @@ function dodge(y, x, anchor, padding, r, { data, ...channels }, plotState) {
57
63
  groupFacetsAndZ(data, { fx, fy }, (items) => {
58
64
  // apply dodge within each facet
59
65
  const tree = IntervalTree();
60
- const data = items.filter((d) => (typeof d.r !== 'number' || d.r >= 0) && isFinite(d[x]) && isFinite(d[y]));
66
+ const data = items.filter((d) => (typeof d.r !== 'number' || d.r >= 0) &&
67
+ isFinite(d[x]) &&
68
+ isFinite(d[y]));
61
69
  const intervals = new Float64Array(2 * data.length + 2);
62
70
  data.forEach((d, i) => {
63
71
  const ri = d.r ?? r ?? 3;
@@ -69,7 +77,8 @@ function dodge(y, x, anchor, padding, r, { data, ...channels }, plotState) {
69
77
  // For any previously placed circles that may overlap this circle, compute
70
78
  // the y-positions that place this circle tangent to these other circles.
71
79
  // https://observablehq.com/@mbostock/circle-offset-along-line
72
- tree.queryInterval(l - padding, h + padding, ([, , j]) => {
80
+ tree.queryInterval(l - padding, h + padding, (interval) => {
81
+ const j = interval[2];
73
82
  const yj = data[j][y] - y0;
74
83
  const dx = d[x] - data[j][x];
75
84
  const dr = padding + (channels.r ? d.r + data[j].r : 2 * cr);
@@ -117,7 +126,7 @@ function anchorYTop({ options: { marginTop } }) {
117
126
  function anchorYBottom({ facetHeight: height }) {
118
127
  return [-1, height];
119
128
  }
120
- function anchorYMiddle({ facetHeight: height, options: { marginTop, marginBottom } }) {
129
+ function anchorYMiddle({ facetHeight: height, options: { marginTop, marginBottom: _marginBottom } }) {
121
130
  return [0, (marginTop + height) / 2];
122
131
  }
123
132
  function compareSymmetric(a, b) {