layerchart 0.6.9 → 0.7.2

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.
@@ -15,6 +15,7 @@
15
15
  import { getContext } from 'svelte';
16
16
  import { arc as d3arc } from 'd3-shape';
17
17
  import { scaleLinear } from 'd3-scale';
18
+ import { min, max } from 'd3-array';
18
19
  import { motionStore } from '../stores/motionStore';
19
20
  import { degreesToRadians } from '../utils/math';
20
21
  export let spring = undefined;
@@ -53,7 +54,7 @@ export let padAngle = 0;
53
54
  export let track = false;
54
55
  const { yRange } = getContext('LayerCake');
55
56
  $: scale = scaleLinear().domain(domain).range(range);
56
- $: _outerRadius = outerRadius ?? $yRange[0] / 2;
57
+ $: _outerRadius = outerRadius ?? max($yRange) / 2;
57
58
  $: _innerRadius =
58
59
  (innerRadius > 1
59
60
  ? innerRadius
@@ -61,7 +62,7 @@ $: _innerRadius =
61
62
  ? _outerRadius * innerRadius
62
63
  : innerRadius < 0
63
64
  ? _outerRadius - innerRadius
64
- : innerRadius) ?? $yRange[1];
65
+ : innerRadius) ?? min($yRange);
65
66
  $: arc = d3arc()
66
67
  .innerRadius(_innerRadius)
67
68
  .outerRadius(_outerRadius)
@@ -1,5 +1,6 @@
1
1
  <script>import { getContext } from 'svelte';
2
2
  import { area as d3Area } from 'd3-shape';
3
+ import { max } from 'd3-array';
3
4
  import { interpolatePath } from 'd3-interpolate-path';
4
5
  import { motionStore } from '../stores/motionStore';
5
6
  import Path from './Path.svelte';
@@ -22,7 +23,7 @@ $: tweened_d = motionStore('', { tweened: tweenedOptions });
22
23
  $: {
23
24
  const path = d3Area()
24
25
  .x(x ?? $xGet)
25
- .y0(y0 ?? $yRange[0])
26
+ .y0(y0 ?? max($yRange))
26
27
  .y1(y1 ?? $yGet);
27
28
  if (curve)
28
29
  path.curve(curve);
@@ -1,5 +1,6 @@
1
1
  <script>import { getContext } from 'svelte';
2
2
  import { format } from 'svelte-ux/utils/format';
3
+ import { max } from 'd3-array';
3
4
  import Text from './Text.svelte';
4
5
  import { isScaleBand } from '../utils/scales';
5
6
  const { height, xScale, yRange } = getContext('LayerCake');
@@ -20,7 +21,7 @@ $: tickVals = Array.isArray(ticks)
20
21
 
21
22
  <g class="axis x-axis">
22
23
  {#each tickVals as tick, i}
23
- <g class="tick tick-{tick}" transform="translate({$xScale(tick)},{$yRange[0]})">
24
+ <g class="tick tick-{tick}" transform="translate({$xScale(tick)},{max($yRange)})">
24
25
  {#if gridlines !== false}
25
26
  <line y1={$height * -1} y2="0" x1="0" x2="0" {...gridlines} />
26
27
  {/if}
@@ -1,5 +1,6 @@
1
1
  <script>import { getContext } from 'svelte';
2
2
  import { format } from 'svelte-ux/utils/format';
3
+ import { min } from 'd3-array';
3
4
  import Text from './Text.svelte';
4
5
  import { isScaleBand } from '../utils/scales';
5
6
  const { padding, xRange, yScale, width } = getContext('LayerCake');
@@ -20,7 +21,7 @@ $: tickVals = Array.isArray(ticks)
20
21
 
21
22
  <g class="axis y-axis" transform="translate({-$padding.left}, 0)">
22
23
  {#each tickVals as tick, i}
23
- <g class="tick tick-{tick}" transform="translate({$xRange[0]}, {$yScale(tick)})">
24
+ <g class="tick tick-{tick}" transform="translate({min($xRange)}, {$yScale(tick)})">
24
25
  {#if gridlines !== false}
25
26
  <line
26
27
  x1={$padding.left}
@@ -58,11 +58,11 @@ $: getDimensions = (item) => {
58
58
  else if (yValue > 0) {
59
59
  // Positive value
60
60
  yTop = yValue;
61
- yBottom = $yRange[1]; // or `0`?
61
+ yBottom = min($yRange); // or `0`?
62
62
  }
63
63
  else {
64
64
  // Negative value
65
- yTop = $yRange[1]; // or `0`?
65
+ yTop = min($yRange); // or `0`?
66
66
  yBottom = yValue;
67
67
  }
68
68
  return {
@@ -1,4 +1,5 @@
1
1
  <script>import { getContext } from 'svelte';
2
+ import { min, max } from 'd3-array';
2
3
  const { xRange, yScale, yRange } = getContext('LayerCake');
3
4
  export let x = false;
4
5
  export let y = false;
@@ -6,11 +7,11 @@ export let y = false;
6
7
 
7
8
  <g class="baseline">
8
9
  {#if x}
9
- <line x1={0} x2={$xRange[1] || 0} y1={$yScale(0) || 0} y2={$yScale(0) || 0} class="baseline" />
10
+ <line x1={0} x2={max($xRange) || 0} y1={max($yRange)} y2={max($yRange)} class="baseline" />
10
11
  {/if}
11
12
 
12
13
  {#if y}
13
- <line x1={0} x2={0} y1={$yRange[0] || 0} y2={$yRange[1] || 0} class="baseline" />
14
+ <line x1={0} x2={0} y1={min($yRange) || 0} y2={max($yRange) || 0} class="baseline" />
14
15
  {/if}
15
16
  </g>
16
17
 
@@ -4,6 +4,7 @@ export { Svg, Html };
4
4
 
5
5
  <script>import { max, min } from 'd3-array';
6
6
  import { get } from 'lodash-es';
7
+ import { isScaleBand } from '../utils/scales';
7
8
  /**
8
9
  * Resolve a value from data based on the accessor type
9
10
  */
@@ -25,6 +26,7 @@ function getValue(accessor, d) {
25
26
  export let data = [];
26
27
  export let x;
27
28
  export let y;
29
+ export let yScale;
28
30
  /**
29
31
  * xBaseline guaranteed to be visible in xDomain
30
32
  */
@@ -43,8 +45,14 @@ $: if (yBaseline != null) {
43
45
  const yValues = data.flatMap((d) => getValue(y, d));
44
46
  yDomain = [min([yBaseline, ...yValues]), max([yBaseline, ...yValues])];
45
47
  }
48
+ /**
49
+ * Reverse the default y range ([0, height] becomes [height, 0]). By default this is `true` unless using scaleBand y scale.
50
+ * see: https://layercake.graphics/guide#yreverse
51
+ * see: https://github.com/mhkeller/layercake/issues/83
52
+ */
53
+ $: yReverse = yScale ? !isScaleBand(yScale) : true;
46
54
  </script>
47
55
 
48
- <LayerCake {data} {y} {yDomain} {x} {xDomain} {...$$restProps}>
56
+ <LayerCake {data} {x} {xDomain} {y} {yScale} {yDomain} {yReverse} {...$$restProps}>
49
57
  <slot />
50
58
  </LayerCake>
@@ -7,6 +7,7 @@ declare const __propDef: {
7
7
  data?: any[];
8
8
  x: (string | ((d: any) => number)) | (string | ((d: any) => number))[];
9
9
  y: (string | ((d: any) => number)) | (string | ((d: any) => number))[];
10
+ yScale: Function;
10
11
  xBaseline?: number | null;
11
12
  yBaseline?: number | null;
12
13
  };
@@ -33,7 +33,7 @@ $: {
33
33
  }
34
34
  dimensions.x = xCoord - (isScaleBand($xScale) ? ($xScale.padding() * $xScale.step()) / 2 : 0);
35
35
  if (axis === 'x') {
36
- dimensions.height = $yRange[0];
36
+ dimensions.height = max($yRange);
37
37
  }
38
38
  }
39
39
  if (axis === 'y' || axis === 'both') {
@@ -55,7 +55,7 @@ $: {
55
55
  }
56
56
  dimensions.y = yCoord - (isScaleBand($yScale) ? ($yScale.padding() * $yScale.step()) / 2 : 0);
57
57
  if (axis === 'y') {
58
- dimensions.width = $xRange[1];
58
+ dimensions.width = max($xRange);
59
59
  }
60
60
  }
61
61
  }
@@ -46,11 +46,11 @@ $: getDimensions = (item) => {
46
46
  else if (yValue > 0) {
47
47
  // Positive value
48
48
  yTop = yValue;
49
- yBottom = $yRange[1]; // or `0`?
49
+ yBottom = min($yRange); // or `0`?
50
50
  }
51
51
  else {
52
52
  // Negative value
53
- yTop = $yRange[1]; // or `0`?
53
+ yTop = min($yRange); // or `0`?
54
54
  yBottom = yValue;
55
55
  }
56
56
  if (yBottom < 0) {
@@ -1,5 +1,6 @@
1
1
  <script>import { getContext } from 'svelte';
2
2
  import { pie as d3pie } from 'd3-shape';
3
+ import { min, max } from 'd3-array';
3
4
  import Arc from './Arc.svelte';
4
5
  import Group from './Group.svelte';
5
6
  import { degreesToRadians } from '../utils/math';
@@ -47,11 +48,11 @@ export let tweened = undefined;
47
48
  */
48
49
  export let offset = 0;
49
50
  const { data: contextData, x, y, xRange, rGet, config } = getContext('LayerCake');
50
- $: resolved_endAngle = endAngle ?? degreesToRadians($config.xRange ? $xRange[1] : range[1]);
51
+ $: resolved_endAngle = endAngle ?? degreesToRadians($config.xRange ? max($xRange) : max(range));
51
52
  let tweened_endAngle = motionStore(0, { spring, tweened });
52
53
  $: tweened_endAngle.set(resolved_endAngle);
53
54
  $: pie = d3pie()
54
- .startAngle(startAngle ?? degreesToRadians($config.xRange ? $xRange[0] : range[0]))
55
+ .startAngle(startAngle ?? degreesToRadians($config.xRange ? min($xRange) : min(range)))
55
56
  .endAngle($tweened_endAngle)
56
57
  .padAngle(padAngle)
57
58
  .value($x);
@@ -5,6 +5,7 @@ See also:
5
5
  */
6
6
  import { getContext } from 'svelte';
7
7
  import { area as d3Area, line as d3Line } from 'd3-shape';
8
+ import { max } from 'd3-array';
8
9
  const { data: contextData, xGet, yGet, yRange } = getContext('LayerCake');
9
10
  // Properties to override what is used from context
10
11
  export let data = undefined; // TODO: Update Type
@@ -25,14 +26,14 @@ $: if (defined)
25
26
  $: clipPathBelow = d3Area()
26
27
  .x(x ?? $xGet)
27
28
  .y0(y0 ?? ((d) => $yGet(d)[0]))
28
- .y1(y1 ?? ((d) => $yRange[0]));
29
+ .y1(y1 ?? ((d) => max($yRange)));
29
30
  $: if (curve)
30
31
  clipPathBelow.curve(curve);
31
32
  $: if (defined)
32
33
  clipPathBelow.defined(defined);
33
34
  $: clipPathAbove = d3Area()
34
35
  .x(x ?? $xGet)
35
- .y0(y0 ?? ((d) => $yRange[0]))
36
+ .y0(y0 ?? ((d) => max($yRange)))
36
37
  .y1(y1 ?? ((d) => $yGet(d)[1]));
37
38
  $: if (curve)
38
39
  clipPathAbove.curve(curve);
@@ -10,8 +10,9 @@ import ChartClipPath from './ChartClipPath.svelte';
10
10
  import { localPoint } from '../utils/event';
11
11
  import { isScaleBand, scaleInvert } from '../utils/scales';
12
12
  import { quadtreeRects } from '../utils/quadtree';
13
+ import { createPropertySortFunc, createSortFunc } from 'svelte-ux/utils/sort';
13
14
  const dispatch = createEventDispatcher();
14
- const { flatData, x, xScale, xGet, xRange, yScale, yGet, yRange, width, height, padding } = getContext('LayerCake');
15
+ const { flatData, x, xScale, xGet, xRange, y, yScale, yGet, yRange, width, height, padding } = getContext('LayerCake');
15
16
  /*
16
17
  TODO: Defaults to consider (if possible to detect scale type, which might not be possible)
17
18
  - scaleTime / scaleLinear: bisect
@@ -22,7 +23,7 @@ const { flatData, x, xScale, xGet, xRange, yScale, yGet, yRange, width, height,
22
23
  - scaleBand, scaleLinear: band (or bounds) - multiple (overlapping) bars
23
24
  - scaleLinear, scaleLinear: voronoi (or quadtree)
24
25
  */
25
- export let mode = 'bisect';
26
+ export let mode = 'bisect-x';
26
27
  export let snapToDataX = false;
27
28
  export let snapToDataY = false;
28
29
  export let findTooltipData = 'closest';
@@ -57,64 +58,108 @@ $: if (tooltip) {
57
58
  $left = tooltip.left + leftOffset;
58
59
  }
59
60
  }
61
+ $: bisectX = bisector((d) => {
62
+ const value = $x(d);
63
+ if (Array.isArray(value)) {
64
+ // `x` accessor with multiple properties (ex. `x={['start', 'end']})`)
65
+ // Using first value. Consider using average, max, etc
66
+ // const midpoint = new Date((value[1].valueOf() + value[0].getTime()) / 2);
67
+ // return midpoint;
68
+ return value[0];
69
+ }
70
+ else {
71
+ return value;
72
+ }
73
+ }).left;
74
+ $: bisectY = bisector((d) => {
75
+ const value = $y(d);
76
+ if (Array.isArray(value)) {
77
+ // `x` accessor with multiple properties (ex. `x={['start', 'end']})`)
78
+ // Using first value. Consider using average, max, etc
79
+ // const midpoint = new Date((value[1].valueOf() + value[0].getTime()) / 2);
80
+ // return midpoint;
81
+ return value[0];
82
+ }
83
+ else {
84
+ return value;
85
+ }
86
+ }).left;
87
+ function findData(previousValue, currentValue, valueAtPoint, accessor) {
88
+ switch (findTooltipData) {
89
+ case 'closest':
90
+ if (currentValue === undefined) {
91
+ return previousValue;
92
+ }
93
+ else if (previousValue === undefined) {
94
+ return currentValue;
95
+ }
96
+ else {
97
+ return Number(valueAtPoint) - Number(accessor(previousValue)) >
98
+ Number(accessor(currentValue)) - Number(valueAtPoint)
99
+ ? currentValue
100
+ : previousValue;
101
+ }
102
+ case 'left':
103
+ return previousValue;
104
+ case 'right':
105
+ default:
106
+ return currentValue;
107
+ }
108
+ }
60
109
  function handleTooltip(event, tooltipData) {
61
110
  const point = localPoint(event.target, event);
62
111
  const localX = point?.x ?? 0;
63
112
  const localY = point?.y ?? 0;
64
113
  // If tooltipData not provided already (voronoi, etc), attempt to find it
114
+ // TODO: When using bisect-x/y/band, values should be sorted. Tyipcally are for `x`, but not `y` (and band depends on if x or y scale)
65
115
  if (tooltipData == null) {
66
116
  if (mode === 'quadtree') {
67
117
  tooltipData = quadtree.find(localX, localY, radius);
68
118
  }
69
- else if (mode === 'bisect') {
70
- // `x` value at mouse/touch coordinate
71
- const valueAtPoint = scaleInvert($xScale, localX);
119
+ else if (mode === 'bisect-band') {
120
+ // `x` and `y` values at mouse/touch coordinate
121
+ const xValueAtPoint = scaleInvert($xScale, localX);
122
+ const yValueAtPoint = scaleInvert($yScale, localY);
72
123
  if (isScaleBand($xScale)) {
73
- tooltipData = $flatData.find((d) => $x(d) === valueAtPoint);
124
+ // Find point closest to pointer within the x band
125
+ const bandData = $flatData
126
+ .filter((d) => $x(d) === xValueAtPoint)
127
+ .sort(createSortFunc($y)); // sort for bisect
128
+ const index = bisectY(bandData, yValueAtPoint, 1);
129
+ const previousValue = bandData[index - 1];
130
+ const currentValue = bandData[index];
131
+ tooltipData = findData(previousValue, currentValue, yValueAtPoint, $y);
132
+ }
133
+ else if (isScaleBand($yScale)) {
134
+ // Find point closest to pointer within the y band
135
+ const bandData = $flatData
136
+ .filter((d) => $y(d) === yValueAtPoint)
137
+ .sort(createSortFunc($x)); // sort for bisect
138
+ const index = bisectX(bandData, xValueAtPoint, 1);
139
+ const previousValue = bandData[index - 1];
140
+ const currentValue = bandData[index];
141
+ tooltipData = findData(previousValue, currentValue, xValueAtPoint, $x);
74
142
  }
75
143
  else {
76
- // continuous scale (linear, time, etc). Use bisector to find closest data to mouse location
77
- const bisectX = bisector((d) => {
78
- const value = $x(d);
79
- if (Array.isArray(value)) {
80
- // `x` accessor with multiple properties (ex. `x={['start', 'end']})`)
81
- // Using first value. Consider using average, max, etc
82
- // const midpoint = new Date((value[1].valueOf() + value[0].getTime()) / 2);
83
- // return midpoint;
84
- return value[0];
85
- }
86
- else {
87
- return value;
88
- }
89
- }).left;
90
- const index = bisectX($flatData, valueAtPoint, 1);
91
- const data0 = $flatData[index - 1];
92
- const data1 = $flatData[index];
93
- switch (findTooltipData) {
94
- case 'closest':
95
- if (data1 === undefined) {
96
- tooltipData = data0;
97
- }
98
- else if (data0 === undefined) {
99
- tooltipData = data1;
100
- }
101
- else {
102
- tooltipData =
103
- Number(valueAtPoint) - Number($x(data0)) >
104
- Number($x(data1)) - Number(valueAtPoint)
105
- ? data1
106
- : data0;
107
- }
108
- break;
109
- case 'left':
110
- tooltipData = data0;
111
- break;
112
- case 'right':
113
- default:
114
- tooltipData = data1;
115
- }
144
+ // TODO: Support `bisect-band` without band? Fallback to bisect?
116
145
  }
117
146
  }
147
+ else if (mode === 'bisect-x') {
148
+ // `x` value at mouse/touch coordinate
149
+ const xValueAtPoint = scaleInvert($xScale, localX);
150
+ const index = bisectX($flatData, xValueAtPoint, 1);
151
+ const previousValue = $flatData[index - 1];
152
+ const currentValue = $flatData[index];
153
+ tooltipData = findData(previousValue, currentValue, xValueAtPoint, $x);
154
+ }
155
+ else if (mode === 'bisect-y') {
156
+ // `y` value at mouse/touch coordinate
157
+ const yValueAtPoint = scaleInvert($yScale, localY);
158
+ const index = bisectY($flatData, yValueAtPoint, 1);
159
+ const previousValue = $flatData[index - 1];
160
+ const currentValue = $flatData[index];
161
+ tooltipData = findData(previousValue, currentValue, yValueAtPoint, $y);
162
+ }
118
163
  }
119
164
  if (tooltipData) {
120
165
  tooltip = {
@@ -182,7 +227,8 @@ $: if (mode === 'quadtree') {
182
227
  }
183
228
  let rects = [];
184
229
  $: if (mode === 'bounds' || mode === 'band') {
185
- rects = $flatData.map((d) => {
230
+ rects = $flatData
231
+ .map((d) => {
186
232
  const xValue = $xGet(d);
187
233
  const yValue = $yGet(d);
188
234
  const x = Array.isArray(xValue) ? min(xValue) : xValue;
@@ -219,7 +265,8 @@ $: if (mode === 'bounds' || mode === 'band') {
219
265
  data: d
220
266
  };
221
267
  }
222
- });
268
+ })
269
+ .sort(createPropertySortFunc('x'));
223
270
  // console.log({ rects });
224
271
  }
225
272
  </script>
@@ -246,7 +293,7 @@ $: if (mode === 'bounds' || mode === 'band') {
246
293
  </Svg>
247
294
  {/if}
248
295
 
249
- {#if mode === 'bisect' || mode === 'quadtree'}
296
+ {#if ['bisect-x', 'bisect-y', 'bisect-band', 'quadtree'].indexOf(mode) != -1}
250
297
  <Html>
251
298
  <div
252
299
  class="absolute"
@@ -266,10 +313,14 @@ $: if (mode === 'bounds' || mode === 'band') {
266
313
  <g class="tooltip-voronoi">
267
314
  <path
268
315
  d={voronoi.renderCell(i)}
269
- style:fill="transparent"
316
+ style:fill={debug ? 'red' : 'transparent'}
317
+ style:fill-opacity={debug ? 0.1 : 0}
270
318
  style:stroke={debug ? 'red' : 'transparent'}
271
319
  on:mousemove={(e) => handleTooltip(e, point.data)}
272
320
  on:mouseleave={hideTooltip}
321
+ on:click={(e) => {
322
+ dispatch('click', { data: point.data });
323
+ }}
273
324
  />
274
325
  </g>
275
326
  {/each}
@@ -283,10 +334,14 @@ $: if (mode === 'bounds' || mode === 'band') {
283
334
  y={rect.y}
284
335
  width={rect.width}
285
336
  height={rect.height}
286
- style:fill="transparent"
337
+ style:fill={debug ? 'red' : 'transparent'}
338
+ style:fill-opacity={debug ? 0.1 : 0}
287
339
  style:stroke={debug ? 'red' : 'transparent'}
288
340
  on:mousemove={(e) => handleTooltip(e, rect.data)}
289
341
  on:mouseleave={hideTooltip}
342
+ on:click={(e) => {
343
+ dispatch('click', { data: rect.data });
344
+ }}
290
345
  />
291
346
  {/each}
292
347
  </g>
@@ -303,8 +358,9 @@ $: if (mode === 'bounds' || mode === 'band') {
303
358
  y={rect.y}
304
359
  width={rect.width}
305
360
  height={rect.height}
361
+ style:fill={debug ? 'red' : 'transparent'}
362
+ style:fill-opacity={debug ? 0.1 : 0}
306
363
  stroke="red"
307
- fill="none"
308
364
  />
309
365
  {/each}
310
366
  </g>
@@ -1,7 +1,7 @@
1
1
  import { SvelteComponentTyped } from "svelte";
2
2
  declare const __propDef: {
3
3
  props: {
4
- mode?: 'bisect' | 'voronoi' | 'quadtree' | 'bounds' | 'band';
4
+ mode?: 'bisect-x' | 'bisect-y' | 'band' | 'bisect-band' | 'bounds' | 'voronoi' | 'quadtree';
5
5
  snapToDataX?: boolean;
6
6
  snapToDataY?: boolean;
7
7
  findTooltipData?: 'closest' | 'left' | 'right';
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "author": "Sean Lynch <techniq35@gmail.com>",
4
4
  "license": "MIT",
5
5
  "repository": "techniq/layerchart",
6
- "version": "0.6.9",
6
+ "version": "0.7.2",
7
7
  "devDependencies": {
8
8
  "@rollup/plugin-dsv": "^2.0.3",
9
9
  "@sveltejs/adapter-vercel": "^1.0.0-next.59",
package/utils/scales.d.ts CHANGED
@@ -3,10 +3,12 @@ import { MotionOptions } from '../stores/motionStore';
3
3
  /**
4
4
  * Implemenation for missing `scaleBand().invert()`
5
5
  *
6
- * See: https://stackoverflow.com/a/50846323/191902
6
+ * See: https://stackoverflow.com/questions/38633082/d3-getting-invert-value-of-band-scales
7
7
  * https://github.com/d3/d3-scale/pull/64
8
8
  * https://github.com/vega/vega-scale/blob/master/src/scaleBand.js#L118
9
9
  * https://observablehq.com/@d3/ordinal-brushing
10
+ * https://github.com/d3/d3-scale/blob/11777dac7d4b0b3e229d658aee3257ea67bd5ffa/src/band.js#L32
11
+ * https://gist.github.com/LuisSevillano/d53a1dc529eef518780c6df99613e2fd
10
12
  */
11
13
  export declare function scaleBandInvert(scale: any): (value: any) => any;
12
14
  export declare function isScaleBand(scale: any): boolean;
package/utils/scales.js CHANGED
@@ -1,17 +1,20 @@
1
1
  import { derived } from 'svelte/store';
2
2
  import { tweened, spring } from 'svelte/motion';
3
+ import { min } from 'd3-array';
3
4
  import { motionStore } from '../stores/motionStore';
4
5
  /**
5
6
  * Implemenation for missing `scaleBand().invert()`
6
7
  *
7
- * See: https://stackoverflow.com/a/50846323/191902
8
+ * See: https://stackoverflow.com/questions/38633082/d3-getting-invert-value-of-band-scales
8
9
  * https://github.com/d3/d3-scale/pull/64
9
10
  * https://github.com/vega/vega-scale/blob/master/src/scaleBand.js#L118
10
11
  * https://observablehq.com/@d3/ordinal-brushing
12
+ * https://github.com/d3/d3-scale/blob/11777dac7d4b0b3e229d658aee3257ea67bd5ffa/src/band.js#L32
13
+ * https://gist.github.com/LuisSevillano/d53a1dc529eef518780c6df99613e2fd
11
14
  */
12
15
  export function scaleBandInvert(scale) {
13
16
  const domain = scale.domain();
14
- const paddingOuter = scale(domain[0]);
17
+ const paddingOuter = scale(min(domain));
15
18
  const eachBand = scale.step();
16
19
  return function (value) {
17
20
  // TODO: Should this use Math.round to better select? https://stackoverflow.com/questions/38633082/d3-getting-invert-value-of-band-scales/50846323#comment104743795_50846323