@canonical/react-components 1.5.0 → 1.6.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.
- package/dist/components/DoughnutChart/DoughnutChart.d.ts +58 -0
- package/dist/components/DoughnutChart/DoughnutChart.js +163 -0
- package/dist/components/DoughnutChart/DoughnutChart.scss +30 -0
- package/dist/components/DoughnutChart/index.d.ts +3 -0
- package/dist/components/DoughnutChart/index.js +13 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -0
- package/package.json +1 -1
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { FC } from "react";
|
|
2
|
+
import "./DoughnutChart.scss";
|
|
3
|
+
export type Segment = {
|
|
4
|
+
/**
|
|
5
|
+
* The colour of the segment.
|
|
6
|
+
*/
|
|
7
|
+
color: string;
|
|
8
|
+
/**
|
|
9
|
+
* The segment tooltip.
|
|
10
|
+
*/
|
|
11
|
+
tooltip?: string;
|
|
12
|
+
/**
|
|
13
|
+
* The segment length.
|
|
14
|
+
*/
|
|
15
|
+
value: number;
|
|
16
|
+
};
|
|
17
|
+
export type Props = {
|
|
18
|
+
/**
|
|
19
|
+
* The label in the centre of the doughnut.
|
|
20
|
+
*/
|
|
21
|
+
label?: string;
|
|
22
|
+
/**
|
|
23
|
+
* An optional class name applied to the label.
|
|
24
|
+
*/
|
|
25
|
+
labelClassname?: string;
|
|
26
|
+
/**
|
|
27
|
+
* An optional class name applied to the wrapping element.
|
|
28
|
+
*/
|
|
29
|
+
className?: string;
|
|
30
|
+
/**
|
|
31
|
+
* The width of the segments when hovered.
|
|
32
|
+
*/
|
|
33
|
+
segmentHoverWidth: number;
|
|
34
|
+
/**
|
|
35
|
+
* The width of the segments.
|
|
36
|
+
*/
|
|
37
|
+
segmentThickness: number;
|
|
38
|
+
/**
|
|
39
|
+
* The doughnut segments.
|
|
40
|
+
*/
|
|
41
|
+
segments: Segment[];
|
|
42
|
+
/**
|
|
43
|
+
* The size of the doughnut.
|
|
44
|
+
*/
|
|
45
|
+
size: number;
|
|
46
|
+
/**
|
|
47
|
+
* ID associated to the specific instance of a Chart.
|
|
48
|
+
*/
|
|
49
|
+
chartID: string;
|
|
50
|
+
};
|
|
51
|
+
export declare enum TestIds {
|
|
52
|
+
Label = "label",
|
|
53
|
+
Segment = "segment",
|
|
54
|
+
Chart = "chart",
|
|
55
|
+
Section = "Section"
|
|
56
|
+
}
|
|
57
|
+
declare const DoughnutChart: FC<Props>;
|
|
58
|
+
export default DoughnutChart;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = exports.TestIds = void 0;
|
|
7
|
+
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
8
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
9
|
+
var _classnames = _interopRequireDefault(require("classnames"));
|
|
10
|
+
var _Tooltip = _interopRequireDefault(require("../Tooltip"));
|
|
11
|
+
require("./DoughnutChart.scss");
|
|
12
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
13
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
14
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
|
+
let TestIds = exports.TestIds = /*#__PURE__*/function (TestIds) {
|
|
16
|
+
TestIds["Label"] = "label";
|
|
17
|
+
TestIds["Segment"] = "segment";
|
|
18
|
+
TestIds["Chart"] = "chart";
|
|
19
|
+
TestIds["Section"] = "Section";
|
|
20
|
+
return TestIds;
|
|
21
|
+
}({});
|
|
22
|
+
const DoughnutChart = _ref => {
|
|
23
|
+
let {
|
|
24
|
+
className,
|
|
25
|
+
label,
|
|
26
|
+
labelClassname,
|
|
27
|
+
segmentHoverWidth,
|
|
28
|
+
segmentThickness,
|
|
29
|
+
segments,
|
|
30
|
+
size,
|
|
31
|
+
chartID
|
|
32
|
+
} = _ref;
|
|
33
|
+
const [tooltipMessage, setTooltipMessage] = (0, _react.useState)(null);
|
|
34
|
+
const id = (0, _react.useRef)("doughnut-chart-".concat(chartID));
|
|
35
|
+
const hoverIncrease = segmentHoverWidth - segmentThickness;
|
|
36
|
+
const adjustedHoverWidth = segmentHoverWidth + hoverIncrease;
|
|
37
|
+
// The canvas needs enough space so that the hover state does not get cut off.
|
|
38
|
+
const canvasSize = size + adjustedHoverWidth - segmentThickness;
|
|
39
|
+
const diameter = size - segmentThickness;
|
|
40
|
+
const radius = diameter / 2;
|
|
41
|
+
const circumference = Math.round(diameter * Math.PI);
|
|
42
|
+
// Calculate the total value of all segments.
|
|
43
|
+
const total = segments.reduce((totalValue, segment) => totalValue += segment.value, 0);
|
|
44
|
+
let accumulatedLength = 0;
|
|
45
|
+
const segmentNodes = segments.map((_ref2, i) => {
|
|
46
|
+
let {
|
|
47
|
+
color,
|
|
48
|
+
tooltip,
|
|
49
|
+
value
|
|
50
|
+
} = _ref2;
|
|
51
|
+
// The start position is the value of all previous segments.
|
|
52
|
+
const startPosition = accumulatedLength;
|
|
53
|
+
// The length of the segment (as a portion of the doughnut circumference)
|
|
54
|
+
const segmentLength = value / total * circumference;
|
|
55
|
+
// The space left until the end of the circle.
|
|
56
|
+
const remainingSpace = circumference - (segmentLength + startPosition);
|
|
57
|
+
// Add this segment length to the running tally.
|
|
58
|
+
accumulatedLength += segmentLength;
|
|
59
|
+
return /*#__PURE__*/_react.default.createElement("circle", {
|
|
60
|
+
className: "doughnut-chart__segment",
|
|
61
|
+
cx: radius - segmentThickness / 2 - hoverIncrease,
|
|
62
|
+
cy: radius + segmentThickness / 2 + hoverIncrease,
|
|
63
|
+
"data-testid": TestIds.Segment,
|
|
64
|
+
key: i,
|
|
65
|
+
tabIndex: 0,
|
|
66
|
+
"aria-label": tooltip ? "".concat(tooltip, ": ").concat(value) : "".concat(value),
|
|
67
|
+
onMouseOut: tooltip ? () => {
|
|
68
|
+
// Hide the tooltip.
|
|
69
|
+
setTooltipMessage(null);
|
|
70
|
+
} : undefined,
|
|
71
|
+
onMouseOver: tooltip ? () => {
|
|
72
|
+
setTooltipMessage(tooltip);
|
|
73
|
+
} : undefined,
|
|
74
|
+
r: radius,
|
|
75
|
+
style: {
|
|
76
|
+
stroke: color,
|
|
77
|
+
strokeWidth: segmentThickness,
|
|
78
|
+
// The dash array used is:
|
|
79
|
+
// 1 - We want there to be a space before the first visible dash so
|
|
80
|
+
// by setting this to 0 we can use the next dash for the space.
|
|
81
|
+
// 2 - This gap is the distance of all previous segments
|
|
82
|
+
// so that the segment starts in the correct spot.
|
|
83
|
+
// 3 - A dash that is the length of the segment.
|
|
84
|
+
// 4 - A gap from the end of the segment to the start of the circle
|
|
85
|
+
// so that the dash array doesn't repeat and be visible.
|
|
86
|
+
strokeDasharray: "0 ".concat(startPosition.toFixed(2), " ").concat(segmentLength.toFixed(2), " ").concat(remainingSpace.toFixed(2))
|
|
87
|
+
}
|
|
88
|
+
// Rotate the segment so that the segments start at the top of
|
|
89
|
+
// the chart.
|
|
90
|
+
,
|
|
91
|
+
transform: "rotate(-90 ".concat(radius, ",").concat(radius, ")")
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
return /*#__PURE__*/_react.default.createElement("div", {
|
|
95
|
+
className: (0, _classnames.default)("doughnut-chart", className),
|
|
96
|
+
style: {
|
|
97
|
+
maxWidth: "".concat(canvasSize, "px")
|
|
98
|
+
},
|
|
99
|
+
"data-testid": TestIds.Chart
|
|
100
|
+
}, /*#__PURE__*/_react.default.createElement(_Tooltip.default, {
|
|
101
|
+
className: "doughnut-chart__tooltip",
|
|
102
|
+
followMouse: true,
|
|
103
|
+
message: tooltipMessage,
|
|
104
|
+
position: "right"
|
|
105
|
+
}, /*#__PURE__*/_react.default.createElement("style", null, "#".concat(id.current, " .doughnut-chart__segment:hover {\n stroke-width: ").concat(adjustedHoverWidth, " !important;\n }")), /*#__PURE__*/_react.default.createElement("svg", {
|
|
106
|
+
className: "doughnut-chart__chart",
|
|
107
|
+
id: id.current,
|
|
108
|
+
viewBox: "0 0 ".concat(canvasSize, " ").concat(canvasSize),
|
|
109
|
+
"data-testid": TestIds.Section,
|
|
110
|
+
"aria-labelledby": "".concat(id.current, "-chart-title ").concat(id.current, "-chart-desc")
|
|
111
|
+
}, label && /*#__PURE__*/_react.default.createElement("title", {
|
|
112
|
+
id: "".concat(id.current, "-chart-title")
|
|
113
|
+
}, label), /*#__PURE__*/_react.default.createElement("desc", {
|
|
114
|
+
id: "".concat(id.current, "-chart-desc")
|
|
115
|
+
}, segments.map(segment => {
|
|
116
|
+
let description = "";
|
|
117
|
+
if (segment.tooltip) description += "".concat(segment.tooltip, ": ");
|
|
118
|
+
description += segment.value;
|
|
119
|
+
return description;
|
|
120
|
+
}).join(",")), /*#__PURE__*/_react.default.createElement("mask", {
|
|
121
|
+
id: "canvasMask"
|
|
122
|
+
}, /*#__PURE__*/_react.default.createElement("rect", {
|
|
123
|
+
fill: "white",
|
|
124
|
+
height: canvasSize,
|
|
125
|
+
width: canvasSize,
|
|
126
|
+
x: "0",
|
|
127
|
+
y: "0"
|
|
128
|
+
}), /*#__PURE__*/_react.default.createElement("circle", {
|
|
129
|
+
cx: canvasSize / 2,
|
|
130
|
+
cy: canvasSize / 2,
|
|
131
|
+
fill: "black",
|
|
132
|
+
r: radius - segmentThickness / 2
|
|
133
|
+
})), /*#__PURE__*/_react.default.createElement("g", {
|
|
134
|
+
mask: "url(#canvasMask)"
|
|
135
|
+
}, /*#__PURE__*/_react.default.createElement("rect", {
|
|
136
|
+
fill: "transparent",
|
|
137
|
+
height: canvasSize,
|
|
138
|
+
width: canvasSize,
|
|
139
|
+
x: "0",
|
|
140
|
+
y: "0"
|
|
141
|
+
}), /*#__PURE__*/_react.default.createElement("g", null, segmentNodes)), label ? /*#__PURE__*/_react.default.createElement("text", {
|
|
142
|
+
x: radius + adjustedHoverWidth / 2,
|
|
143
|
+
y: radius + adjustedHoverWidth / 2
|
|
144
|
+
}, /*#__PURE__*/_react.default.createElement("tspan", {
|
|
145
|
+
className: (0, _classnames.default)("doughnut-chart__label", labelClassname),
|
|
146
|
+
"data-testid": TestIds.Label
|
|
147
|
+
}, label)) : null)));
|
|
148
|
+
};
|
|
149
|
+
DoughnutChart.propTypes = {
|
|
150
|
+
label: _propTypes.default.string,
|
|
151
|
+
labelClassname: _propTypes.default.string,
|
|
152
|
+
className: _propTypes.default.string,
|
|
153
|
+
segmentHoverWidth: _propTypes.default.number.isRequired,
|
|
154
|
+
segmentThickness: _propTypes.default.number.isRequired,
|
|
155
|
+
segments: _propTypes.default.arrayOf(_propTypes.default.shape({
|
|
156
|
+
color: _propTypes.default.string.isRequired,
|
|
157
|
+
tooltip: _propTypes.default.string,
|
|
158
|
+
value: _propTypes.default.number.isRequired
|
|
159
|
+
})).isRequired,
|
|
160
|
+
size: _propTypes.default.number.isRequired,
|
|
161
|
+
chartID: _propTypes.default.string.isRequired
|
|
162
|
+
};
|
|
163
|
+
var _default = exports.default = DoughnutChart;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
@import "vanilla-framework";
|
|
2
|
+
|
|
3
|
+
.doughnut-chart {
|
|
4
|
+
width: 6.5rem;
|
|
5
|
+
|
|
6
|
+
.doughnut-chart__tooltip {
|
|
7
|
+
display: block;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.doughnut-chart__tooltip > :only-child {
|
|
11
|
+
// Override the tooltip wrapper.
|
|
12
|
+
display: block !important;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.doughnut-chart__chart {
|
|
16
|
+
// Restrict hover areas to the strokes.
|
|
17
|
+
pointer-events: stroke;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.doughnut-chart__segment {
|
|
21
|
+
fill: transparent;
|
|
22
|
+
|
|
23
|
+
// Animate stroke size changes on hover.
|
|
24
|
+
transition: stroke-width 0.3s ease;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.doughnut-chart__legend {
|
|
29
|
+
list-style-type: none;
|
|
30
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
Object.defineProperty(exports, "default", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _DoughnutChart.default;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
var _DoughnutChart = _interopRequireDefault(require("./DoughnutChart"));
|
|
13
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
package/dist/index.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export { default as Col } from "./components/Col";
|
|
|
19
19
|
export { default as ConfirmationButton } from "./components/ConfirmationButton";
|
|
20
20
|
export { default as ConfirmationModal } from "./components/ConfirmationModal";
|
|
21
21
|
export { default as ContextualMenu } from "./components/ContextualMenu";
|
|
22
|
+
export { default as DoughnutChart } from "./components/DoughnutChart";
|
|
22
23
|
export { default as EmptyState } from "./components/EmptyState";
|
|
23
24
|
export { default as Field } from "./components/Field";
|
|
24
25
|
export { default as Form } from "./components/Form";
|
|
@@ -87,6 +88,7 @@ export type { ColProps, ColSize } from "./components/Col";
|
|
|
87
88
|
export type { ConfirmationButtonProps } from "./components/ConfirmationButton";
|
|
88
89
|
export type { ConfirmationModalProps } from "./components/ConfirmationModal";
|
|
89
90
|
export type { ContextualMenuProps, ContextualMenuDropdownProps, MenuLink, Position, } from "./components/ContextualMenu";
|
|
91
|
+
export type { DoughnutChartProps, Segment } from "./components/DoughnutChart";
|
|
90
92
|
export type { EmptyStateProps } from "./components/EmptyState";
|
|
91
93
|
export type { FieldProps } from "./components/Field";
|
|
92
94
|
export type { FormProps } from "./components/Form";
|
package/dist/index.js
CHANGED
|
@@ -27,6 +27,7 @@ var _exportNames = {
|
|
|
27
27
|
ConfirmationButton: true,
|
|
28
28
|
ConfirmationModal: true,
|
|
29
29
|
ContextualMenu: true,
|
|
30
|
+
DoughnutChart: true,
|
|
30
31
|
EmptyState: true,
|
|
31
32
|
Field: true,
|
|
32
33
|
Form: true,
|
|
@@ -234,6 +235,12 @@ Object.defineProperty(exports, "ContextualMenu", {
|
|
|
234
235
|
return _ContextualMenu.default;
|
|
235
236
|
}
|
|
236
237
|
});
|
|
238
|
+
Object.defineProperty(exports, "DoughnutChart", {
|
|
239
|
+
enumerable: true,
|
|
240
|
+
get: function () {
|
|
241
|
+
return _DoughnutChart.default;
|
|
242
|
+
}
|
|
243
|
+
});
|
|
237
244
|
Object.defineProperty(exports, "EmptyState", {
|
|
238
245
|
enumerable: true,
|
|
239
246
|
get: function () {
|
|
@@ -663,6 +670,7 @@ var _Col = _interopRequireDefault(require("./components/Col"));
|
|
|
663
670
|
var _ConfirmationButton = _interopRequireDefault(require("./components/ConfirmationButton"));
|
|
664
671
|
var _ConfirmationModal = _interopRequireDefault(require("./components/ConfirmationModal"));
|
|
665
672
|
var _ContextualMenu = _interopRequireDefault(require("./components/ContextualMenu"));
|
|
673
|
+
var _DoughnutChart = _interopRequireDefault(require("./components/DoughnutChart"));
|
|
666
674
|
var _EmptyState = _interopRequireDefault(require("./components/EmptyState"));
|
|
667
675
|
var _Field = _interopRequireDefault(require("./components/Field"));
|
|
668
676
|
var _Form = _interopRequireDefault(require("./components/Form"));
|