@semcore/d3-chart 1.0.0 → 1.3.1-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 (55) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/lib/cjs/Area.js +2 -1
  3. package/lib/cjs/Area.js.map +1 -1
  4. package/lib/cjs/Bar.js +44 -12
  5. package/lib/cjs/Bar.js.map +1 -1
  6. package/lib/cjs/ClipPath.js +1 -1
  7. package/lib/cjs/ClipPath.js.map +1 -1
  8. package/lib/cjs/Donut.js +125 -57
  9. package/lib/cjs/Donut.js.map +1 -1
  10. package/lib/cjs/GroupBar.js +5 -5
  11. package/lib/cjs/HorizontalBar.js +45 -9
  12. package/lib/cjs/HorizontalBar.js.map +1 -1
  13. package/lib/cjs/Hover.js +24 -6
  14. package/lib/cjs/Hover.js.map +1 -1
  15. package/lib/cjs/Line.js +2 -1
  16. package/lib/cjs/Line.js.map +1 -1
  17. package/lib/cjs/StackBar.js +6 -6
  18. package/lib/cjs/StackBar.js.map +1 -1
  19. package/lib/cjs/StackedArea.js +6 -6
  20. package/lib/cjs/utils.js +67 -1
  21. package/lib/cjs/utils.js.map +1 -1
  22. package/lib/es6/Area.js +3 -2
  23. package/lib/es6/Area.js.map +1 -1
  24. package/lib/es6/Bar.js +42 -11
  25. package/lib/es6/Bar.js.map +1 -1
  26. package/lib/es6/ClipPath.js +1 -1
  27. package/lib/es6/ClipPath.js.map +1 -1
  28. package/lib/es6/Donut.js +124 -56
  29. package/lib/es6/Donut.js.map +1 -1
  30. package/lib/es6/GroupBar.js +5 -5
  31. package/lib/es6/HorizontalBar.js +44 -9
  32. package/lib/es6/HorizontalBar.js.map +1 -1
  33. package/lib/es6/Hover.js +24 -7
  34. package/lib/es6/Hover.js.map +1 -1
  35. package/lib/es6/Line.js +3 -2
  36. package/lib/es6/Line.js.map +1 -1
  37. package/lib/es6/StackBar.js +6 -6
  38. package/lib/es6/StackBar.js.map +1 -1
  39. package/lib/es6/StackedArea.js +6 -6
  40. package/lib/es6/utils.js +61 -1
  41. package/lib/es6/utils.js.map +1 -1
  42. package/lib/types/Bar.d.ts +4 -0
  43. package/lib/types/HorizontalBar.d.ts +4 -0
  44. package/package.json +5 -4
  45. package/src/Area.js +3 -3
  46. package/src/Bar.js +58 -20
  47. package/src/ClipPath.js +1 -1
  48. package/src/Donut.js +61 -4
  49. package/src/HorizontalBar.js +46 -7
  50. package/src/Hover.js +17 -23
  51. package/src/Line.js +3 -3
  52. package/src/StackBar.js +1 -1
  53. package/src/types/Bar.d.ts +4 -0
  54. package/src/types/HorizontalBar.d.ts +4 -0
  55. package/src/utils.js +52 -1
package/src/Area.js CHANGED
@@ -3,7 +3,7 @@ import { area, curveLinear, line } from 'd3-shape';
3
3
  import Dots from './Dots';
4
4
  import { Component, sstyled } from '@semcore/core';
5
5
  import createElement from './createElement';
6
- import { definedData, scaleOfBandwidth, getNullData } from './utils';
6
+ import { definedData, scaleOfBandwidth, getNullData, definedNullData } from './utils';
7
7
  import ClipPath from './ClipPath';
8
8
  import uniqueIDEnhancement from '@semcore/utils/lib/uniqueID';
9
9
 
@@ -48,9 +48,9 @@ class AreaRoot extends Component {
48
48
  }
49
49
 
50
50
  getNullProps() {
51
- const { y, color, data, d3Line } = this.asProps;
51
+ const { x, y, color, data, d3Line } = this.asProps;
52
52
  return {
53
- data: getNullData(data, d3Line.defined(), y),
53
+ data: getNullData(data, definedNullData(x, y), y),
54
54
  d3: d3Line,
55
55
  color,
56
56
  };
package/src/Bar.js CHANGED
@@ -1,9 +1,10 @@
1
1
  import React from 'react';
2
+ import { transition } from 'd3-transition';
2
3
  import { Component, sstyled } from '@semcore/core';
4
+ import uniqueIDEnhancement from '@semcore/utils/lib/uniqueID';
3
5
  import createElement from './createElement';
4
6
  import ClipPath from './ClipPath';
5
- import uniqueIDEnhancement from '@semcore/utils/lib/uniqueID';
6
- import { transition } from 'd3-transition';
7
+ import { getBandwidth, roundedPath } from './utils';
7
8
 
8
9
  import style from './style/bar.shadow.css';
9
10
 
@@ -16,6 +17,7 @@ class BarRoot extends Component {
16
17
  color: '#50aef4',
17
18
  offset: [0, 0],
18
19
  duration: 500,
20
+ r: 2,
19
21
  };
20
22
 
21
23
  getBackgroundProps(props, index) {
@@ -50,28 +52,54 @@ class BarRoot extends Component {
50
52
 
51
53
  renderBar(d, i) {
52
54
  const SBar = this.Element;
53
- const { styles, color, x, y, y0, scale, hide, offset, duration, uid } = this.asProps;
55
+ const {
56
+ styles,
57
+ color,
58
+ x,
59
+ y,
60
+ y0,
61
+ scale,
62
+ hide,
63
+ offset,
64
+ duration,
65
+ uid,
66
+ r,
67
+ $index,
68
+ width: widthProps,
69
+ } = this.asProps;
54
70
 
55
71
  const [xScale, yScale] = scale;
72
+ const barY = yScale(Math.max(d[y0] ?? 0, d[y])) + offset[1];
73
+ const barX = xScale(d[x]) + offset[0];
74
+ const height = Math.abs(
75
+ yScale(d[y]) - Math.min(yScale(yScale.domain()[0]), yScale(d[y0] ?? 0)),
76
+ );
77
+ const width = widthProps || getBandwidth(xScale);
78
+ const isRounded = r !== 0;
56
79
 
57
80
  return sstyled(styles)(
58
- <SBar
59
- key={`bar-${i}`}
60
- render="rect"
61
- clipPath={`url(#${uid})`}
62
- __excludeProps={['data', 'scale', 'value']}
63
- childrenPosition="above"
64
- value={d}
65
- index={i}
66
- hide={hide}
67
- color={color}
68
- // TODO: https://github.com/airbnb/visx/blob/2fa674e7d7fdc9cffea13e8bf644d46dd6f0db5b/packages/visx-shape/src/util/getBandwidth.ts#L3
69
- width={xScale.bandwidth()}
70
- height={Math.abs(yScale(d[y]) - Math.min(yScale(yScale.domain()[0]), yScale(d[y0] ?? 0)))}
71
- x={xScale(d[x]) + offset[0]}
72
- y={yScale(Math.max(d[y0] ?? 0, d[y])) + offset[1]}
73
- use:duration={`${duration}ms`}
74
- />,
81
+ <>
82
+ <SBar
83
+ key={`bar-${i}`}
84
+ render="path"
85
+ clipPath={`url(#${uid})`}
86
+ __excludeProps={['data', 'scale', 'value']}
87
+ childrenPosition="above"
88
+ value={d}
89
+ index={i}
90
+ hide={hide}
91
+ color={color}
92
+ d={getRect({
93
+ x: barX,
94
+ y: isRounded ? (d[y] > 0 ? barY - r : barY) : barY,
95
+ width,
96
+ height: isRounded ? height + r : height,
97
+ radius: r,
98
+ position: d[y] > 0 ? 'top' : 'bottom',
99
+ })}
100
+ use:duration={`${duration}ms`}
101
+ />
102
+ </>,
75
103
  );
76
104
  }
77
105
  render() {
@@ -112,4 +140,14 @@ function Background(props) {
112
140
  );
113
141
  }
114
142
 
143
+ function getRect({ x, y, width, height, radius, position }) {
144
+ if (height <= radius) return '';
145
+ if (radius) {
146
+ if (position === 'top')
147
+ return roundedPath(x, y, width, height, radius, true, true, false, false);
148
+ return roundedPath(x, y, width, height, radius, false, false, true, true);
149
+ }
150
+ return roundedPath(x, y, width, height, radius);
151
+ }
152
+
115
153
  export default createElement(BarRoot, { Background });
package/src/ClipPath.js CHANGED
@@ -14,7 +14,7 @@ class ClipPath extends Component {
14
14
 
15
15
  componentDidMount() {
16
16
  const { id, tag, setAttributeTag } = this.asProps;
17
- if (!document || !document.querySelector(`#${id}`)) return;
17
+ if (!document || !id || !document.querySelector(`#${id}`)) return;
18
18
  const svg = document.querySelector(`#${id}`).closest('svg');
19
19
  Array.from(svg.querySelectorAll(`[clip-path="url(#${id})"]`)).forEach((node) => {
20
20
  node && node.getTotalLength && node.getTotalLength();
package/src/Donut.js CHANGED
@@ -43,6 +43,29 @@ function animationUpdatePie({ halfsize, arcs, d3Arc }) {
43
43
  };
44
44
  }
45
45
 
46
+ function animationHoverPie({ d, selector, duration, innerRadius, outerRadius }) {
47
+ if (duration > 0) {
48
+ transition()
49
+ .selection()
50
+ .select(selector)
51
+ .transition()
52
+ .duration(duration)
53
+ .attrTween('d', function() {
54
+ if (!d) return () => '';
55
+ const [min, max] = outerRadius;
56
+ const i = interpolate(min, max);
57
+ return function(t) {
58
+ const d3ArcOut = arc()
59
+ .innerRadius(innerRadius)
60
+ .outerRadius(i(t));
61
+ return d3ArcOut(d);
62
+ };
63
+ });
64
+ }
65
+ }
66
+
67
+ const increaseFactor = 8;
68
+
46
69
  class DonutRoot extends Component {
47
70
  static displayName = 'Donut';
48
71
  static style = style;
@@ -52,8 +75,8 @@ class DonutRoot extends Component {
52
75
  const [width, height] = size;
53
76
  const minORmax = halfsize ? Math.max : Math.min;
54
77
  const d3Arc = arc()
55
- .outerRadius(minORmax(width, height) / 2)
56
- .innerRadius(innerRadius);
78
+ .outerRadius(minORmax(width - increaseFactor * 2, height - increaseFactor * 2) / 2)
79
+ .innerRadius(innerRadius > increaseFactor ? innerRadius - increaseFactor : innerRadius);
57
80
  let d3Pie = pie()
58
81
  .sort(null)
59
82
  .value(([, value]) => value);
@@ -73,6 +96,7 @@ class DonutRoot extends Component {
73
96
  const { uid, id } = this.asProps;
74
97
  return id || uid;
75
98
  }
99
+
76
100
  virtualElement = canUseDOM() ? document.createElement('div') : {};
77
101
 
78
102
  generateGetBoundingClientRect(x = 0, y = 0) {
@@ -94,6 +118,15 @@ class DonutRoot extends Component {
94
118
  .filter(([key]) => keys.includes(key))
95
119
  .sort(([a], [b]) => (keys.indexOf(a) > keys.indexOf(b) ? 1 : -1));
96
120
  }
121
+ const minValue =
122
+ pieData.reduce((acc, cur) => {
123
+ if (cur[1]) acc += cur[1];
124
+ return acc;
125
+ }, 0) / 100;
126
+ pieData = pieData.map((d) => {
127
+ if (d[1] && d[1] < minValue) d[1] = minValue;
128
+ return d;
129
+ });
97
130
  return d3Pie(pieData);
98
131
  }
99
132
 
@@ -105,12 +138,36 @@ class DonutRoot extends Component {
105
138
  };
106
139
 
107
140
  getPieProps(props) {
108
- const { d3Arc } = this.asProps;
141
+ let { d3Arc, halfsize, size, duration, innerRadius } = this.asProps;
142
+ const [width, height] = size;
143
+ const minORmax = halfsize ? Math.max : Math.min;
144
+ innerRadius = innerRadius > increaseFactor ? innerRadius - increaseFactor : innerRadius;
145
+ const outerRadius = minORmax(width - increaseFactor * 2, height - increaseFactor * 2) / 2;
146
+ const data = this.arcs.find((arc) => arc.data[0] === props.dataKey);
147
+
109
148
  return {
110
- data: this.arcs.find((arc) => arc.data[0] === props.dataKey),
149
+ data,
111
150
  d3Arc,
112
151
  onMouseMove: this.bindHandlerTooltip(true, props),
113
152
  onMouseLeave: this.bindHandlerTooltip(false, props),
153
+ onMouseOver: (e) => {
154
+ animationHoverPie({
155
+ d: data,
156
+ selector: `[d="${e.target.getAttribute('d')}"]`,
157
+ duration: duration === 0 ? 0 : 300,
158
+ innerRadius,
159
+ outerRadius: [outerRadius, outerRadius + increaseFactor],
160
+ });
161
+ },
162
+ onMouseOut: (e) => {
163
+ animationHoverPie({
164
+ d: data,
165
+ selector: `[d="${e.target.getAttribute('d')}"]`,
166
+ duration: duration === 0 ? 0 : 300,
167
+ innerRadius,
168
+ outerRadius: [outerRadius + increaseFactor, outerRadius],
169
+ });
170
+ },
114
171
  };
115
172
  }
116
173
 
@@ -1,8 +1,9 @@
1
1
  import React from 'react';
2
2
  import { Component, sstyled } from '@semcore/core';
3
+ import uniqueIDEnhancement from '@semcore/utils/lib/uniqueID';
3
4
  import createElement from './createElement';
4
5
  import ClipPath from './ClipPath';
5
- import uniqueIDEnhancement from '@semcore/utils/lib/uniqueID';
6
+ import { getBandwidth, roundedPath } from './utils';
6
7
 
7
8
  import style from './style/bar.shadow.css';
8
9
 
@@ -15,6 +16,7 @@ class HorizontalBarRoot extends Component {
15
16
  color: '#50aef4',
16
17
  offset: [0, 0],
17
18
  duration: 500,
19
+ r: 2,
18
20
  };
19
21
 
20
22
  getBackgroundProps(props, index) {
@@ -26,13 +28,34 @@ class HorizontalBarRoot extends Component {
26
28
 
27
29
  renderBar(d, i) {
28
30
  const SBar = this.Element;
29
- const { styles, color, x, x0, y, scale, hide, offset, uid, duration } = this.asProps;
31
+ const {
32
+ styles,
33
+ color,
34
+ x,
35
+ x0,
36
+ y,
37
+ scale,
38
+ hide,
39
+ offset,
40
+ uid,
41
+ duration,
42
+ r,
43
+ $index,
44
+ height: heightProps,
45
+ onMouseMove,
46
+ onMouseLeave,
47
+ } = this.asProps;
30
48
  const [xScale, yScale] = scale;
49
+ const barY = yScale(d[y]) + offset[1];
50
+ const barX = xScale(Math.min(d[x0] ?? 0, d[x])) + offset[0];
51
+ const height = heightProps || getBandwidth(yScale);
52
+ const width = Math.abs(xScale(d[x]) - Math.max(xScale(xScale.domain()[0]), xScale(d[x0] ?? 0)));
53
+ const isRounded = r !== 0;
31
54
 
32
55
  return sstyled(styles)(
33
56
  <SBar
34
57
  key={`horizontal-bar-${i}`}
35
- render="rect"
58
+ render="path"
36
59
  clipPath={`url(#${uid})`}
37
60
  __excludeProps={['data', 'scale', 'value']}
38
61
  childrenPosition="above"
@@ -40,11 +63,17 @@ class HorizontalBarRoot extends Component {
40
63
  index={i}
41
64
  hide={hide}
42
65
  color={color}
43
- width={Math.abs(xScale(d[x]) - Math.max(xScale(xScale.domain()[0]), xScale(d[x0] ?? 0)))}
44
- height={yScale.bandwidth()}
45
- x={xScale(Math.min(d[x0] ?? 0, d[x])) + offset[0]}
46
- y={yScale(d[y]) + offset[1]}
66
+ d={getHorizontalRect({
67
+ x: isRounded ? (d[x] > 0 ? barX : barX - r) : barX,
68
+ y: barY,
69
+ width: isRounded ? width + r : width,
70
+ height,
71
+ radius: r,
72
+ position: d[x] > 0 ? 'right' : 'left',
73
+ })}
47
74
  use:duration={`${duration}ms`}
75
+ onMouseMove={onMouseMove}
76
+ onMouseLeave={onMouseLeave}
48
77
  />,
49
78
  );
50
79
  }
@@ -90,4 +119,14 @@ function Background(props) {
90
119
  );
91
120
  }
92
121
 
122
+ function getHorizontalRect({ x, y, width, height, radius, position }) {
123
+ if (width <= radius) return '';
124
+ if (radius) {
125
+ if (position === 'right')
126
+ return roundedPath(x, y, width, height, radius, false, true, false, true);
127
+ return roundedPath(x, y, width, height, radius, true, false, true, false);
128
+ }
129
+ return roundedPath(x, y, width, height, radius);
130
+ }
131
+
93
132
  export default createElement(HorizontalBarRoot, { Background });
package/src/Hover.js CHANGED
@@ -2,7 +2,8 @@ import React from 'react';
2
2
  import { Component, sstyled } from '@semcore/core';
3
3
  import createElement from './createElement';
4
4
  import trottle from '@semcore/utils/lib/rafTrottle';
5
- import { scaleOfBandwidth, getIndexFromData, eventToPoint, invert } from './utils';
5
+ import canUseDOM from '@semcore/utils/lib/canUseDOM';
6
+ import { scaleOfBandwidth, getIndexFromData, eventToPoint, invert, CONSTANT } from './utils';
6
7
 
7
8
  import style from './style/hover.shadow.css';
8
9
 
@@ -14,10 +15,15 @@ class Hover extends Component {
14
15
  yIndex: null,
15
16
  };
16
17
 
17
- hoverRef = React.createRef();
18
+ virtualElement = canUseDOM() ? document.createElement('div') : {};
19
+
20
+ generateGetBoundingClientRect(x = 0, y = 0) {
21
+ return () => ({ width: 0, height: 0, top: y, right: x, bottom: y, left: x });
22
+ }
18
23
 
19
24
  handlerMouseMoveRoot = trottle((e) => {
20
25
  const { eventEmitter, data, scale, x, y, rootRef } = this.asProps;
26
+ const { clientX, clientY } = e;
21
27
  const [xScale, yScale] = scale;
22
28
  const [pX, pY] = eventToPoint(e, rootRef.current);
23
29
  const vX = invert(xScale, pX);
@@ -27,12 +33,18 @@ class Hover extends Component {
27
33
  const yIndex =
28
34
  y === undefined || vY === undefined ? null : getIndexFromData(data, yScale, y, vY);
29
35
  const state = { xIndex, yIndex };
36
+ this.virtualElement.getBoundingClientRect = this.generateGetBoundingClientRect(
37
+ clientX,
38
+ clientY,
39
+ );
40
+ this.virtualElement[CONSTANT.VIRTUAL_ELEMENT] = true;
41
+
30
42
  this.setState(state, () => {
31
43
  eventEmitter.emit(
32
44
  'onTooltipVisible',
33
45
  xIndex !== null || yIndex !== null,
34
46
  state,
35
- this.hoverRef.current,
47
+ this.virtualElement,
36
48
  );
37
49
  });
38
50
  });
@@ -86,26 +98,10 @@ class HoverLineRoot extends Hover {
86
98
  return sstyled(styles)(
87
99
  <>
88
100
  {xIndex !== null ? (
89
- <SHoverLine
90
- render="line"
91
- ref={this.hoverRef}
92
- index={xIndex}
93
- x1={x1}
94
- y1={yRange[0]}
95
- x2={x1}
96
- y2={yRange[1]}
97
- />
101
+ <SHoverLine render="line" index={xIndex} x1={x1} y1={yRange[0]} x2={x1} y2={yRange[1]} />
98
102
  ) : null}
99
103
  {yIndex !== null ? (
100
- <SHoverLine
101
- render="line"
102
- ref={this.hoverRef}
103
- index={yIndex}
104
- x1={xRange[0]}
105
- y1={y1}
106
- x2={xRange[1]}
107
- y2={y1}
108
- />
104
+ <SHoverLine render="line" index={yIndex} x1={xRange[0]} y1={y1} x2={xRange[1]} y2={y1} />
109
105
  ) : null}
110
106
  </>,
111
107
  );
@@ -129,7 +125,6 @@ class HoverRectRoot extends Hover {
129
125
  {xIndex !== null ? (
130
126
  <SHoverRect
131
127
  render="rect"
132
- ref={this.hoverRef}
133
128
  index={xIndex}
134
129
  width={xScale.step() - xScale.paddingInner() / 2}
135
130
  height={yRange[0] - yRange[1]}
@@ -140,7 +135,6 @@ class HoverRectRoot extends Hover {
140
135
  {yIndex !== null ? (
141
136
  <SHoverRect
142
137
  render="rect"
143
- ref={this.hoverRef}
144
138
  index={yIndex}
145
139
  width={xRange[1] - xRange[0]}
146
140
  height={yScale.step() - yScale.paddingInner() / 2}
package/src/Line.js CHANGED
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import { curveLinear, line as d3Line } from 'd3-shape';
3
3
  import { Component, sstyled } from '@semcore/core';
4
4
  import createElement from './createElement';
5
- import { definedData, scaleOfBandwidth, getNullData } from './utils';
5
+ import { definedData, definedNullData, scaleOfBandwidth, getNullData } from './utils';
6
6
  import Dots from './Dots';
7
7
  import ClipPath from './ClipPath';
8
8
  import uniqueIDEnhancement from '@semcore/utils/lib/uniqueID';
@@ -39,11 +39,11 @@ class LineRoot extends Component {
39
39
  }
40
40
 
41
41
  getNullProps() {
42
- const { y, d3, color, data } = this.asProps;
42
+ const { x, y, d3, color, data } = this.asProps;
43
43
  return {
44
44
  d3,
45
45
  // TODO: vertical
46
- data: getNullData(data, d3.defined(), y),
46
+ data: getNullData(data, definedNullData(x, y), y),
47
47
  color,
48
48
  };
49
49
  }
package/src/StackBar.js CHANGED
@@ -35,7 +35,7 @@ class StackBarRoot extends Component {
35
35
  }
36
36
  return acc;
37
37
  }, []);
38
- stack.keys(keys.reverse());
38
+ stack.keys(keys);
39
39
  }
40
40
 
41
41
  return stack(data);
@@ -15,6 +15,10 @@ export interface IBarProps extends IContext {
15
15
  * @default 500
16
16
  */
17
17
  duration?: number;
18
+ /** Radius of curvature
19
+ * @default 2
20
+ */
21
+ r?: number;
18
22
  }
19
23
 
20
24
  export interface IBarContext {
@@ -14,6 +14,10 @@ export interface IHorizontalBarProps extends IContext {
14
14
  * @default 500
15
15
  */
16
16
  duration?: number;
17
+ /** Radius of curvature
18
+ * @default 2
19
+ */
20
+ r?: number;
17
21
  }
18
22
 
19
23
  declare const HorizontalBar: (<T>(
package/src/utils.js CHANGED
@@ -24,10 +24,16 @@ export function invert(scale, value) {
24
24
  .range(range[0] <= range[1] ? domain : domain.slice().reverse())(value);
25
25
  }
26
26
 
27
- export function definedData(x, y) {
27
+ export function definedNullData(x, y) {
28
28
  return (p) => p[x] !== null && p[y] !== null;
29
29
  }
30
30
 
31
+ export function definedData(x, y) {
32
+ return (p) => {
33
+ return p[x] !== null && p[x] !== undefined && (p[y] !== null && p[y] !== undefined);
34
+ };
35
+ }
36
+
31
37
  export function scaleOfBandwidth(scale, value) {
32
38
  return scale.bandwidth ? scale(value) + scale.bandwidth() / 2 : scale(value);
33
39
  }
@@ -97,3 +103,48 @@ export function getIndexFromData(data, scale, key, value) {
97
103
  return null;
98
104
  }
99
105
  }
106
+
107
+ export function roundedPath(x, y, w, h, r, tl = false, tr = false, bl = false, br = false) {
108
+ let retval;
109
+ retval = 'M' + (x + r) + ',' + y;
110
+ retval += 'h' + (w - 2 * r);
111
+ if (tr) {
112
+ retval += 'a' + r + ',' + r + ' 0 0 1 ' + r + ',' + r;
113
+ } else {
114
+ retval += 'h' + r;
115
+ retval += 'v' + r;
116
+ }
117
+ retval += 'v' + (h - 2 * r);
118
+ if (br) {
119
+ retval += 'a' + r + ',' + r + ' 0 0 1 ' + -r + ',' + r;
120
+ } else {
121
+ retval += 'v' + r;
122
+ retval += 'h' + -r;
123
+ }
124
+ retval += 'h' + (2 * r - w);
125
+ if (bl) {
126
+ retval += 'a' + r + ',' + r + ' 0 0 1 ' + -r + ',' + -r;
127
+ } else {
128
+ retval += 'h' + -r;
129
+ retval += 'v' + -r;
130
+ }
131
+ retval += 'v' + (2 * r - h);
132
+ if (tl) {
133
+ retval += 'a' + r + ',' + r + ' 0 0 1 ' + r + ',' + -r;
134
+ } else {
135
+ retval += 'v' + -r;
136
+ retval += 'h' + r;
137
+ }
138
+ retval += 'z';
139
+ return retval;
140
+ }
141
+
142
+ export function getBandwidth(scale) {
143
+ if ('bandwidth' in scale) {
144
+ return scale.bandwidth();
145
+ }
146
+
147
+ const range = scale.range();
148
+ const domain = scale.domain();
149
+ return Math.abs(range[range.length - 1] - range[0]) / domain.length;
150
+ }