@aclymatepackages/modules 1.0.25 → 1.0.26
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/EmissionsChart.js +182 -33
- package/package.json +1 -1
- package/src/components/EmissionsChart.js +191 -19
|
@@ -2,27 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
require("core-js/modules/es.symbol.description.js");
|
|
4
4
|
require("core-js/modules/es.object.assign.js");
|
|
5
|
+
require("core-js/modules/es.weak-map.js");
|
|
5
6
|
Object.defineProperty(exports, "__esModule", {
|
|
6
7
|
value: true
|
|
7
8
|
});
|
|
8
9
|
exports.default = void 0;
|
|
9
10
|
require("core-js/modules/es.array.reduce.js");
|
|
10
|
-
require("core-js/modules/es.object.from-entries.js");
|
|
11
11
|
require("core-js/modules/web.dom-collections.iterator.js");
|
|
12
|
-
|
|
12
|
+
require("core-js/modules/es.object.from-entries.js");
|
|
13
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
13
14
|
var _dayjs = _interopRequireDefault(require("dayjs"));
|
|
14
|
-
var _dayOfYear = _interopRequireDefault(require("dayjs/plugin/dayOfYear"));
|
|
15
15
|
var _recharts = require("recharts");
|
|
16
16
|
var _material = require("@mui/material");
|
|
17
17
|
var _formatters = require("@aclymatepackages/formatters");
|
|
18
18
|
var _otherHelpers = require("@aclymatepackages/other-helpers");
|
|
19
|
-
var
|
|
20
|
-
var _useChartWarningLabels = _interopRequireDefault(require("./useChartWarningLabels"));
|
|
19
|
+
var _converters = require("@aclymatepackages/converters");
|
|
21
20
|
var _chartHelpers = require("@aclymatepackages/chart-helpers");
|
|
22
21
|
var _subcategories = require("@aclymatepackages/subcategories");
|
|
22
|
+
var _EmissionsCustomTooltip = _interopRequireDefault(require("./EmissionsCustomTooltip"));
|
|
23
|
+
var _useChartWarningLabels = _interopRequireDefault(require("./useChartWarningLabels"));
|
|
23
24
|
const _excluded = ["totalEmissionsSumTons"],
|
|
24
|
-
_excluded2 = ["
|
|
25
|
+
_excluded2 = ["viewBox", "labels", "setLabels", "label"],
|
|
26
|
+
_excluded3 = ["subcategory", "color"];
|
|
25
27
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
28
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
|
|
29
|
+
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 && Object.prototype.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; }
|
|
26
30
|
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
27
31
|
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
|
28
32
|
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
@@ -31,7 +35,6 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
31
35
|
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
32
36
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
|
|
33
37
|
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
34
|
-
_dayjs.default.extend(_dayOfYear.default);
|
|
35
38
|
const findObjectValuesSum = object => Object.values(object).reduce((sum, value) => sum + value, 0);
|
|
36
39
|
const buildGroupedChartData = _ref => {
|
|
37
40
|
let {
|
|
@@ -40,7 +43,7 @@ const buildGroupedChartData = _ref => {
|
|
|
40
43
|
viewMode
|
|
41
44
|
} = _ref;
|
|
42
45
|
return groupedEmissions.map((emissionsArray, idx) => {
|
|
43
|
-
const subcategoriesObj = buildRealDataObj(emissionsArray, _subcategories.
|
|
46
|
+
const subcategoriesObj = buildRealDataObj(emissionsArray, [..._subcategories.subcategories, _subcategories.otherSubcategoryObj]);
|
|
44
47
|
const buildWarningObj = () => {
|
|
45
48
|
const singleEmissionWarning = emissionsArray.find(_ref2 => {
|
|
46
49
|
let {
|
|
@@ -111,17 +114,72 @@ const addTrendlineToChartData = chartData => {
|
|
|
111
114
|
});
|
|
112
115
|
});
|
|
113
116
|
};
|
|
114
|
-
const
|
|
117
|
+
const ReferenceLineLabelChips = _ref8 => {
|
|
118
|
+
let {
|
|
119
|
+
x,
|
|
120
|
+
y,
|
|
121
|
+
label
|
|
122
|
+
} = _ref8;
|
|
123
|
+
const CHIP_HEIGHT_PX = 24;
|
|
124
|
+
const {
|
|
125
|
+
palette
|
|
126
|
+
} = (0, _material.useTheme)();
|
|
127
|
+
const [chipWidth, setChipWidth] = (0, _react.useState)(0);
|
|
128
|
+
const chipRef = (0, _react.useRef)();
|
|
129
|
+
(0, _react.useEffect)(() => {
|
|
130
|
+
if (chipRef.current) {
|
|
131
|
+
setChipWidth(chipRef.current.offsetWidth);
|
|
132
|
+
}
|
|
133
|
+
}, [chipRef]);
|
|
134
|
+
return /*#__PURE__*/_react.default.createElement(_material.Chip, {
|
|
135
|
+
elevation: 3,
|
|
136
|
+
ref: chipRef,
|
|
137
|
+
label: label,
|
|
138
|
+
style: {
|
|
139
|
+
position: "absolute",
|
|
140
|
+
top: y - CHIP_HEIGHT_PX / 2,
|
|
141
|
+
left: x - chipWidth / 2,
|
|
142
|
+
background: (0, _converters.hexToRgba)(palette.backgroundGray.main, 0.85)
|
|
143
|
+
// boxShadow:
|
|
144
|
+
// "0px 2px 1px -1px rgba(0,0,0,0.2), 0px 1px 1px 0px rgba(0,0,0,0.14), 0px 1px 3px 0px rgba(0,0,0,0.12)",
|
|
145
|
+
},
|
|
146
|
+
size: "small"
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
const ChartLineLabelSetter = _ref9 => {
|
|
150
|
+
let {
|
|
151
|
+
viewBox,
|
|
152
|
+
labels,
|
|
153
|
+
setLabels,
|
|
154
|
+
label
|
|
155
|
+
} = _ref9,
|
|
156
|
+
otherProps = _objectWithoutProperties(_ref9, _excluded2);
|
|
157
|
+
(0, _react.useEffect)(() => {
|
|
158
|
+
const {
|
|
159
|
+
y
|
|
160
|
+
} = viewBox;
|
|
161
|
+
if (!labels.find(existingLabel => existingLabel.label === label)) {
|
|
162
|
+
setLabels(existingLabels => [...existingLabels, _objectSpread({
|
|
163
|
+
y,
|
|
164
|
+
label
|
|
165
|
+
}, otherProps)]);
|
|
166
|
+
}
|
|
167
|
+
}, [viewBox, label, labels, setLabels, otherProps]);
|
|
168
|
+
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null);
|
|
169
|
+
};
|
|
170
|
+
const LabeledEmissionsChart = _ref10 => {
|
|
115
171
|
var _data$, _data;
|
|
116
172
|
let {
|
|
173
|
+
graphPeriod,
|
|
117
174
|
data,
|
|
118
|
-
chartRef,
|
|
119
175
|
type,
|
|
120
176
|
displayUnitLabel,
|
|
121
177
|
chartArray,
|
|
122
178
|
aspect = 3,
|
|
123
|
-
showTooltip
|
|
124
|
-
|
|
179
|
+
showTooltip,
|
|
180
|
+
netZeroPercentage,
|
|
181
|
+
baseline
|
|
182
|
+
} = _ref10;
|
|
125
183
|
const {
|
|
126
184
|
palette
|
|
127
185
|
} = (0, _material.useTheme)();
|
|
@@ -133,17 +191,66 @@ const LabeledEmissionsChart = _ref8 => {
|
|
|
133
191
|
warningField: "warning",
|
|
134
192
|
barSumField: "totalEmissionsSumTons"
|
|
135
193
|
});
|
|
194
|
+
const [chartWidth, setChartWidth] = (0, _react.useState)(0);
|
|
195
|
+
const [referenceLineLabels, setReferenceLineLabels] = (0, _react.useState)([]);
|
|
196
|
+
const chartContainerRef = (0, _react.useRef)();
|
|
197
|
+
(0, _react.useEffect)(() => {
|
|
198
|
+
if (chartContainerRef.current) {
|
|
199
|
+
setChartWidth(chartContainerRef.current.offsetWidth);
|
|
200
|
+
}
|
|
201
|
+
}, [chartContainerRef]);
|
|
136
202
|
const isTrendLineGood = ((_data$ = data[0]) === null || _data$ === void 0 ? void 0 : _data$.trendLine) > ((_data = data[data.length - 1]) === null || _data === void 0 ? void 0 : _data.trendLine);
|
|
137
203
|
const ChartElement = type === "bar" ? _recharts.Bar : _recharts.Area;
|
|
204
|
+
const buildReferenceLinesArray = () => {
|
|
205
|
+
if (!baseline) {
|
|
206
|
+
return [];
|
|
207
|
+
}
|
|
208
|
+
const baselineReferenceLine = {
|
|
209
|
+
value: baseline,
|
|
210
|
+
label: "".concat((0, _formatters.ucFirstLetters)(graphPeriod), "ly Baseline"),
|
|
211
|
+
labelPosition: "top"
|
|
212
|
+
};
|
|
213
|
+
const ghgReductionLine = {
|
|
214
|
+
value: baseline * 0.5,
|
|
215
|
+
label: "GHG Reduction Mandate (50% by 2030)",
|
|
216
|
+
labelPosition: "top"
|
|
217
|
+
};
|
|
218
|
+
const companyPledgeLabel = "Company pledge to reduce emissions by ".concat(netZeroPercentage, "% by 2030");
|
|
219
|
+
const pledgeReductionValue = baseline * (1 - netZeroPercentage / 100);
|
|
220
|
+
if (!netZeroPercentage) {
|
|
221
|
+
return [baselineReferenceLine, ghgReductionLine];
|
|
222
|
+
}
|
|
223
|
+
if (netZeroPercentage >= 55) {
|
|
224
|
+
return [baselineReferenceLine, ghgReductionLine, {
|
|
225
|
+
value: pledgeReductionValue,
|
|
226
|
+
label: companyPledgeLabel,
|
|
227
|
+
labelPosition: "bottom"
|
|
228
|
+
}];
|
|
229
|
+
}
|
|
230
|
+
return [baselineReferenceLine, {
|
|
231
|
+
label: companyPledgeLabel,
|
|
232
|
+
value: pledgeReductionValue,
|
|
233
|
+
labelPosition: "bottom"
|
|
234
|
+
}];
|
|
235
|
+
};
|
|
236
|
+
const referenceLines = buildReferenceLinesArray();
|
|
237
|
+
|
|
238
|
+
//This weird hack is needed because for some reason every label shows up in referenceLineLabels twice.
|
|
239
|
+
const uniqueReferenceLineLabels = [...new Set(referenceLineLabels.map(_ref11 => {
|
|
240
|
+
let {
|
|
241
|
+
label
|
|
242
|
+
} = _ref11;
|
|
243
|
+
return label;
|
|
244
|
+
}))].map(label => referenceLineLabels.find(lineLabel => lineLabel.label === label));
|
|
138
245
|
return /*#__PURE__*/_react.default.createElement(_material.Box, {
|
|
139
|
-
position: "relative"
|
|
246
|
+
position: "relative",
|
|
247
|
+
ref: chartContainerRef
|
|
140
248
|
}, /*#__PURE__*/_react.default.createElement(_recharts.ResponsiveContainer, {
|
|
141
249
|
aspect: aspect
|
|
142
250
|
}, /*#__PURE__*/_react.default.createElement(_recharts.ComposedChart, {
|
|
143
251
|
width: 500,
|
|
144
252
|
height: 300,
|
|
145
|
-
data: data
|
|
146
|
-
ref: chartRef
|
|
253
|
+
data: data
|
|
147
254
|
}, /*#__PURE__*/_react.default.createElement(_recharts.XAxis, {
|
|
148
255
|
dataKey: "label",
|
|
149
256
|
interval: "preserveStartEnd",
|
|
@@ -161,12 +268,12 @@ const LabeledEmissionsChart = _ref8 => {
|
|
|
161
268
|
categoriesArray: chartArray,
|
|
162
269
|
displayUnitLabel: displayUnitLabel
|
|
163
270
|
})
|
|
164
|
-
}), chartArray.map(
|
|
271
|
+
}), chartArray.map(_ref12 => {
|
|
165
272
|
let {
|
|
166
273
|
subcategory,
|
|
167
274
|
color
|
|
168
|
-
} =
|
|
169
|
-
otherProps = _objectWithoutProperties(
|
|
275
|
+
} = _ref12,
|
|
276
|
+
otherProps = _objectWithoutProperties(_ref12, _excluded3);
|
|
170
277
|
return /*#__PURE__*/_react.default.createElement(ChartElement, _extends({
|
|
171
278
|
key: "emissions-chart-element-".concat(subcategory),
|
|
172
279
|
type: "monotone",
|
|
@@ -184,15 +291,34 @@ const LabeledEmissionsChart = _ref8 => {
|
|
|
184
291
|
strokeWidth: 4,
|
|
185
292
|
dot: false,
|
|
186
293
|
strokeDasharray: "5 5"
|
|
187
|
-
})
|
|
294
|
+
}), !!referenceLines.length && referenceLines.map((_ref13, idx) => {
|
|
295
|
+
let {
|
|
296
|
+
label,
|
|
297
|
+
value,
|
|
298
|
+
labelPosition
|
|
299
|
+
} = _ref13;
|
|
300
|
+
return /*#__PURE__*/_react.default.createElement(_recharts.ReferenceLine, {
|
|
301
|
+
key: "chart-reference-line-".concat(idx),
|
|
302
|
+
y: value,
|
|
303
|
+
strokeWidth: 2,
|
|
304
|
+
stroke: palette.backgroundGray.dark,
|
|
305
|
+
label: /*#__PURE__*/_react.default.createElement(ChartLineLabelSetter, {
|
|
306
|
+
label: label,
|
|
307
|
+
labels: referenceLineLabels,
|
|
308
|
+
setLabels: setReferenceLineLabels
|
|
309
|
+
})
|
|
310
|
+
});
|
|
311
|
+
}))), warningLabels, !!uniqueReferenceLineLabels.length && uniqueReferenceLineLabels.map((label, idx) => /*#__PURE__*/_react.default.createElement(ReferenceLineLabelChips, _extends({
|
|
312
|
+
key: "reference-line-label-chip-".concat(idx),
|
|
313
|
+
x: chartWidth / 2
|
|
314
|
+
}, label))));
|
|
188
315
|
};
|
|
189
|
-
const EmissionsChart =
|
|
316
|
+
const EmissionsChart = _ref14 => {
|
|
190
317
|
let {
|
|
191
318
|
dataArray: emissions,
|
|
192
319
|
type,
|
|
193
320
|
viewMode = "subcategories",
|
|
194
321
|
graphPeriod,
|
|
195
|
-
chartRef,
|
|
196
322
|
showTrendline,
|
|
197
323
|
displayUnit,
|
|
198
324
|
unitConverter,
|
|
@@ -202,8 +328,9 @@ const EmissionsChart = _ref10 => {
|
|
|
202
328
|
startDate,
|
|
203
329
|
convertCarbonUnits,
|
|
204
330
|
displayUnitLabel,
|
|
205
|
-
branding
|
|
206
|
-
|
|
331
|
+
branding,
|
|
332
|
+
netZeroPercentage
|
|
333
|
+
} = _ref14;
|
|
207
334
|
const {
|
|
208
335
|
chartLabelsArray,
|
|
209
336
|
scopesArray,
|
|
@@ -217,8 +344,8 @@ const EmissionsChart = _ref10 => {
|
|
|
217
344
|
buildRealDataObj,
|
|
218
345
|
viewMode
|
|
219
346
|
});
|
|
220
|
-
const convertChartDataObject = (chartDataObj, converter) => Object.fromEntries(Object.entries(chartDataObj).map(
|
|
221
|
-
let [key, value] =
|
|
347
|
+
const convertChartDataObject = (chartDataObj, converter) => Object.fromEntries(Object.entries(chartDataObj).map(_ref15 => {
|
|
348
|
+
let [key, value] = _ref15;
|
|
222
349
|
if (typeof value !== "number") {
|
|
223
350
|
return [key, value];
|
|
224
351
|
}
|
|
@@ -229,28 +356,28 @@ const EmissionsChart = _ref10 => {
|
|
|
229
356
|
}));
|
|
230
357
|
if (isPercentageChart) {
|
|
231
358
|
const percentageConvertedChartData = preliminaryChartData.map(chartDataObj => {
|
|
232
|
-
const objectSubcategoryProperties = Object.keys(chartDataObj).filter(key => _subcategories.subcategories.find(
|
|
359
|
+
const objectSubcategoryProperties = Object.keys(chartDataObj).filter(key => _subcategories.subcategories.find(_ref16 => {
|
|
233
360
|
let {
|
|
234
361
|
subcategory
|
|
235
|
-
} =
|
|
362
|
+
} = _ref16;
|
|
236
363
|
return subcategory === key;
|
|
237
364
|
}));
|
|
238
365
|
const objectSubcategoryValues = objectSubcategoryProperties.map(subcategory => ({
|
|
239
366
|
key: subcategory,
|
|
240
367
|
value: chartDataObj[subcategory]
|
|
241
368
|
}));
|
|
242
|
-
const periodEmissionsSum = objectSubcategoryValues.reduce((sum,
|
|
369
|
+
const periodEmissionsSum = objectSubcategoryValues.reduce((sum, _ref17) => {
|
|
243
370
|
let {
|
|
244
371
|
value
|
|
245
|
-
} =
|
|
372
|
+
} = _ref17;
|
|
246
373
|
return value + sum;
|
|
247
374
|
}, 0);
|
|
248
375
|
const percentageConverter = value => value / periodEmissionsSum * 100;
|
|
249
|
-
const newObject = Object.fromEntries(objectSubcategoryValues.map(
|
|
376
|
+
const newObject = Object.fromEntries(objectSubcategoryValues.map(_ref18 => {
|
|
250
377
|
let {
|
|
251
378
|
key,
|
|
252
379
|
value
|
|
253
|
-
} =
|
|
380
|
+
} = _ref18;
|
|
254
381
|
return [key, value];
|
|
255
382
|
}));
|
|
256
383
|
return convertChartDataObject(newObject, percentageConverter);
|
|
@@ -265,6 +392,26 @@ const EmissionsChart = _ref10 => {
|
|
|
265
392
|
return labeledChartData;
|
|
266
393
|
};
|
|
267
394
|
const chartData = buildChartData();
|
|
395
|
+
const findFirstYearBaseline = () => {
|
|
396
|
+
if ((0, _dayjs.default)().diff((0, _dayjs.default)(startDate), "month") < 12) {
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
const {
|
|
400
|
+
groupedEmissions: firstYearBaselineMonthlyEmissions
|
|
401
|
+
} = (0, _chartHelpers.buildEmissionGroupData)(emissions, "month", startDate);
|
|
402
|
+
const firstYearMonthlyGroupedEmissions = [...new Array(11)].map((_, idx) => firstYearBaselineMonthlyEmissions[idx]);
|
|
403
|
+
const firstYearMonthlyEmissionsSum = firstYearMonthlyGroupedEmissions.map(emissions => ({
|
|
404
|
+
tonsCo2e: (0, _otherHelpers.sumTonsCo2e)(emissions)
|
|
405
|
+
}));
|
|
406
|
+
const monthlyBaseline = (0, _otherHelpers.sumTonsCo2e)(firstYearMonthlyEmissionsSum) / 12;
|
|
407
|
+
if (graphPeriod === "year") {
|
|
408
|
+
return monthlyBaseline * 12;
|
|
409
|
+
}
|
|
410
|
+
if (graphPeriod === "quarter") {
|
|
411
|
+
return monthlyBaseline * 3;
|
|
412
|
+
}
|
|
413
|
+
return monthlyBaseline;
|
|
414
|
+
};
|
|
268
415
|
const chartArray = viewMode === "scopes" ? scopesArray : subcategoriesArray;
|
|
269
416
|
return /*#__PURE__*/_react.default.createElement(_material.Grid, {
|
|
270
417
|
container: true,
|
|
@@ -273,14 +420,16 @@ const EmissionsChart = _ref10 => {
|
|
|
273
420
|
item: true
|
|
274
421
|
}, /*#__PURE__*/_react.default.createElement(LabeledEmissionsChart, {
|
|
275
422
|
data: chartData,
|
|
276
|
-
chartRef: chartRef,
|
|
277
423
|
type: type,
|
|
278
424
|
displayUnitLabel: isPercentageChart ? "%" : displayUnit || displayUnitLabel,
|
|
279
425
|
chartArray: chartArray.map(obj => _objectSpread(_objectSpread({}, obj), {}, {
|
|
280
426
|
viewMode
|
|
281
427
|
})),
|
|
282
428
|
aspect: aspect,
|
|
283
|
-
showTooltip: showTooltip
|
|
429
|
+
showTooltip: showTooltip,
|
|
430
|
+
graphPeriod: graphPeriod,
|
|
431
|
+
netZeroPercentage: netZeroPercentage,
|
|
432
|
+
baseline: findFirstYearBaseline()
|
|
284
433
|
})));
|
|
285
434
|
};
|
|
286
435
|
var _default = exports.default = EmissionsChart;
|
package/package.json
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useEffect, useState, useRef } from "react";
|
|
2
2
|
import dayjs from "dayjs";
|
|
3
|
-
import dayOfYear from "dayjs/plugin/dayOfYear";
|
|
4
3
|
|
|
5
4
|
import {
|
|
6
5
|
XAxis,
|
|
@@ -11,17 +10,15 @@ import {
|
|
|
11
10
|
Bar,
|
|
12
11
|
ComposedChart,
|
|
13
12
|
Line,
|
|
13
|
+
ReferenceLine,
|
|
14
14
|
Tooltip as ChartTooltip,
|
|
15
15
|
} from "recharts";
|
|
16
16
|
|
|
17
|
-
import { Grid, Box, useTheme } from "@mui/material";
|
|
17
|
+
import { Grid, Box, Chip, useTheme } from "@mui/material";
|
|
18
18
|
|
|
19
|
-
import { formatDecimal } from "@aclymatepackages/formatters";
|
|
19
|
+
import { formatDecimal, ucFirstLetters } from "@aclymatepackages/formatters";
|
|
20
20
|
import { sumTonsCo2e } from "@aclymatepackages/other-helpers";
|
|
21
|
-
|
|
22
|
-
import EmissionsCustomTooltip from "./EmissionsCustomTooltip";
|
|
23
|
-
import useChartWarningLabels from "./useChartWarningLabels";
|
|
24
|
-
|
|
21
|
+
import { hexToRgba } from "@aclymatepackages/converters";
|
|
25
22
|
import {
|
|
26
23
|
buildScopesRealDataObj,
|
|
27
24
|
buildSubcategoriesDataObj,
|
|
@@ -29,10 +26,11 @@ import {
|
|
|
29
26
|
} from "@aclymatepackages/chart-helpers";
|
|
30
27
|
import {
|
|
31
28
|
subcategories,
|
|
32
|
-
|
|
29
|
+
otherSubcategoryObj,
|
|
33
30
|
} from "@aclymatepackages/subcategories";
|
|
34
31
|
|
|
35
|
-
|
|
32
|
+
import EmissionsCustomTooltip from "./EmissionsCustomTooltip";
|
|
33
|
+
import useChartWarningLabels from "./useChartWarningLabels";
|
|
36
34
|
|
|
37
35
|
const findObjectValuesSum = (object) =>
|
|
38
36
|
Object.values(object).reduce((sum, value) => sum + value, 0);
|
|
@@ -43,10 +41,10 @@ const buildGroupedChartData = ({
|
|
|
43
41
|
viewMode,
|
|
44
42
|
}) => {
|
|
45
43
|
return groupedEmissions.map((emissionsArray, idx) => {
|
|
46
|
-
const subcategoriesObj = buildRealDataObj(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
);
|
|
44
|
+
const subcategoriesObj = buildRealDataObj(emissionsArray, [
|
|
45
|
+
...subcategories,
|
|
46
|
+
otherSubcategoryObj,
|
|
47
|
+
]);
|
|
50
48
|
|
|
51
49
|
const buildWarningObj = () => {
|
|
52
50
|
const singleEmissionWarning = emissionsArray.find(
|
|
@@ -110,14 +108,69 @@ const addTrendlineToChartData = (chartData) => {
|
|
|
110
108
|
}));
|
|
111
109
|
};
|
|
112
110
|
|
|
111
|
+
const ReferenceLineLabelChips = ({ x, y, label }) => {
|
|
112
|
+
const CHIP_HEIGHT_PX = 24;
|
|
113
|
+
|
|
114
|
+
const { palette } = useTheme();
|
|
115
|
+
|
|
116
|
+
const [chipWidth, setChipWidth] = useState(0);
|
|
117
|
+
const chipRef = useRef();
|
|
118
|
+
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
if (chipRef.current) {
|
|
121
|
+
setChipWidth(chipRef.current.offsetWidth);
|
|
122
|
+
}
|
|
123
|
+
}, [chipRef]);
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<Chip
|
|
127
|
+
elevation={3}
|
|
128
|
+
ref={chipRef}
|
|
129
|
+
label={label}
|
|
130
|
+
style={{
|
|
131
|
+
position: "absolute",
|
|
132
|
+
top: y - CHIP_HEIGHT_PX / 2,
|
|
133
|
+
left: x - chipWidth / 2,
|
|
134
|
+
background: hexToRgba(palette.backgroundGray.main, 0.85),
|
|
135
|
+
// boxShadow:
|
|
136
|
+
// "0px 2px 1px -1px rgba(0,0,0,0.2), 0px 1px 1px 0px rgba(0,0,0,0.14), 0px 1px 3px 0px rgba(0,0,0,0.12)",
|
|
137
|
+
}}
|
|
138
|
+
size="small"
|
|
139
|
+
/>
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const ChartLineLabelSetter = ({
|
|
144
|
+
viewBox,
|
|
145
|
+
labels,
|
|
146
|
+
setLabels,
|
|
147
|
+
label,
|
|
148
|
+
...otherProps
|
|
149
|
+
}) => {
|
|
150
|
+
useEffect(() => {
|
|
151
|
+
const { y } = viewBox;
|
|
152
|
+
|
|
153
|
+
if (!labels.find((existingLabel) => existingLabel.label === label)) {
|
|
154
|
+
setLabels((existingLabels) => [
|
|
155
|
+
...existingLabels,
|
|
156
|
+
{ y, label, ...otherProps },
|
|
157
|
+
]);
|
|
158
|
+
}
|
|
159
|
+
}, [viewBox, label, labels, setLabels, otherProps]);
|
|
160
|
+
|
|
161
|
+
return <></>;
|
|
162
|
+
};
|
|
163
|
+
|
|
113
164
|
const LabeledEmissionsChart = ({
|
|
165
|
+
graphPeriod,
|
|
114
166
|
data,
|
|
115
|
-
chartRef,
|
|
116
167
|
type,
|
|
117
168
|
displayUnitLabel,
|
|
118
169
|
chartArray,
|
|
119
170
|
aspect = 3,
|
|
120
171
|
showTooltip,
|
|
172
|
+
netZeroPercentage,
|
|
173
|
+
baseline,
|
|
121
174
|
}) => {
|
|
122
175
|
const { palette } = useTheme();
|
|
123
176
|
|
|
@@ -127,14 +180,80 @@ const LabeledEmissionsChart = ({
|
|
|
127
180
|
barSumField: "totalEmissionsSumTons",
|
|
128
181
|
});
|
|
129
182
|
|
|
183
|
+
const [chartWidth, setChartWidth] = useState(0);
|
|
184
|
+
const [referenceLineLabels, setReferenceLineLabels] = useState([]);
|
|
185
|
+
|
|
186
|
+
const chartContainerRef = useRef();
|
|
187
|
+
|
|
188
|
+
useEffect(() => {
|
|
189
|
+
if (chartContainerRef.current) {
|
|
190
|
+
setChartWidth(chartContainerRef.current.offsetWidth);
|
|
191
|
+
}
|
|
192
|
+
}, [chartContainerRef]);
|
|
193
|
+
|
|
130
194
|
const isTrendLineGood = data[0]?.trendLine > data[data.length - 1]?.trendLine;
|
|
131
195
|
|
|
132
196
|
const ChartElement = type === "bar" ? Bar : Area;
|
|
133
197
|
|
|
198
|
+
const buildReferenceLinesArray = () => {
|
|
199
|
+
if (!baseline) {
|
|
200
|
+
return [];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const baselineReferenceLine = {
|
|
204
|
+
value: baseline,
|
|
205
|
+
label: `${ucFirstLetters(graphPeriod)}ly Baseline`,
|
|
206
|
+
labelPosition: "top",
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const ghgReductionLine = {
|
|
210
|
+
value: baseline * 0.5,
|
|
211
|
+
label: "GHG Reduction Mandate (50% by 2030)",
|
|
212
|
+
labelPosition: "top",
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const companyPledgeLabel = `Company pledge to reduce emissions by ${netZeroPercentage}% by 2030`;
|
|
216
|
+
const pledgeReductionValue = baseline * (1 - netZeroPercentage / 100);
|
|
217
|
+
|
|
218
|
+
if (!netZeroPercentage) {
|
|
219
|
+
return [baselineReferenceLine, ghgReductionLine];
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (netZeroPercentage >= 55) {
|
|
223
|
+
return [
|
|
224
|
+
baselineReferenceLine,
|
|
225
|
+
ghgReductionLine,
|
|
226
|
+
{
|
|
227
|
+
value: pledgeReductionValue,
|
|
228
|
+
label: companyPledgeLabel,
|
|
229
|
+
labelPosition: "bottom",
|
|
230
|
+
},
|
|
231
|
+
];
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return [
|
|
235
|
+
baselineReferenceLine,
|
|
236
|
+
{
|
|
237
|
+
label: companyPledgeLabel,
|
|
238
|
+
value: pledgeReductionValue,
|
|
239
|
+
labelPosition: "bottom",
|
|
240
|
+
},
|
|
241
|
+
];
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const referenceLines = buildReferenceLinesArray();
|
|
245
|
+
|
|
246
|
+
//This weird hack is needed because for some reason every label shows up in referenceLineLabels twice.
|
|
247
|
+
const uniqueReferenceLineLabels = [
|
|
248
|
+
...new Set(referenceLineLabels.map(({ label }) => label)),
|
|
249
|
+
].map((label) =>
|
|
250
|
+
referenceLineLabels.find((lineLabel) => lineLabel.label === label)
|
|
251
|
+
);
|
|
252
|
+
|
|
134
253
|
return (
|
|
135
|
-
<Box position="relative">
|
|
254
|
+
<Box position="relative" ref={chartContainerRef}>
|
|
136
255
|
<ResponsiveContainer aspect={aspect}>
|
|
137
|
-
<ComposedChart width={500} height={300} data={data}
|
|
256
|
+
<ComposedChart width={500} height={300} data={data}>
|
|
138
257
|
<XAxis dataKey="label" interval="preserveStartEnd" height={20} />
|
|
139
258
|
<YAxis
|
|
140
259
|
tickFormatter={(tick) =>
|
|
@@ -179,9 +298,33 @@ const LabeledEmissionsChart = ({
|
|
|
179
298
|
dot={false}
|
|
180
299
|
strokeDasharray="5 5"
|
|
181
300
|
/>
|
|
301
|
+
{!!referenceLines.length &&
|
|
302
|
+
referenceLines.map(({ label, value, labelPosition }, idx) => (
|
|
303
|
+
<ReferenceLine
|
|
304
|
+
key={`chart-reference-line-${idx}`}
|
|
305
|
+
y={value}
|
|
306
|
+
strokeWidth={2}
|
|
307
|
+
stroke={palette.backgroundGray.dark}
|
|
308
|
+
label={
|
|
309
|
+
<ChartLineLabelSetter
|
|
310
|
+
label={label}
|
|
311
|
+
labels={referenceLineLabels}
|
|
312
|
+
setLabels={setReferenceLineLabels}
|
|
313
|
+
/>
|
|
314
|
+
}
|
|
315
|
+
/>
|
|
316
|
+
))}
|
|
182
317
|
</ComposedChart>
|
|
183
318
|
</ResponsiveContainer>
|
|
184
319
|
{warningLabels}
|
|
320
|
+
{!!uniqueReferenceLineLabels.length &&
|
|
321
|
+
uniqueReferenceLineLabels.map((label, idx) => (
|
|
322
|
+
<ReferenceLineLabelChips
|
|
323
|
+
key={`reference-line-label-chip-${idx}`}
|
|
324
|
+
x={chartWidth / 2}
|
|
325
|
+
{...label}
|
|
326
|
+
/>
|
|
327
|
+
))}
|
|
185
328
|
</Box>
|
|
186
329
|
);
|
|
187
330
|
};
|
|
@@ -191,7 +334,6 @@ const EmissionsChart = ({
|
|
|
191
334
|
type,
|
|
192
335
|
viewMode = "subcategories",
|
|
193
336
|
graphPeriod,
|
|
194
|
-
chartRef,
|
|
195
337
|
showTrendline,
|
|
196
338
|
displayUnit,
|
|
197
339
|
unitConverter,
|
|
@@ -202,6 +344,7 @@ const EmissionsChart = ({
|
|
|
202
344
|
convertCarbonUnits,
|
|
203
345
|
displayUnitLabel,
|
|
204
346
|
branding,
|
|
347
|
+
netZeroPercentage,
|
|
205
348
|
}) => {
|
|
206
349
|
const {
|
|
207
350
|
chartLabelsArray,
|
|
@@ -287,6 +430,33 @@ const EmissionsChart = ({
|
|
|
287
430
|
|
|
288
431
|
const chartData = buildChartData();
|
|
289
432
|
|
|
433
|
+
const findFirstYearBaseline = () => {
|
|
434
|
+
if (dayjs().diff(dayjs(startDate), "month") < 12) {
|
|
435
|
+
return false;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const { groupedEmissions: firstYearBaselineMonthlyEmissions } =
|
|
439
|
+
buildEmissionGroupData(emissions, "month", startDate);
|
|
440
|
+
|
|
441
|
+
const firstYearMonthlyGroupedEmissions = [...new Array(11)].map(
|
|
442
|
+
(_, idx) => firstYearBaselineMonthlyEmissions[idx]
|
|
443
|
+
);
|
|
444
|
+
const firstYearMonthlyEmissionsSum = firstYearMonthlyGroupedEmissions.map(
|
|
445
|
+
(emissions) => ({ tonsCo2e: sumTonsCo2e(emissions) })
|
|
446
|
+
);
|
|
447
|
+
const monthlyBaseline = sumTonsCo2e(firstYearMonthlyEmissionsSum) / 12;
|
|
448
|
+
|
|
449
|
+
if (graphPeriod === "year") {
|
|
450
|
+
return monthlyBaseline * 12;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (graphPeriod === "quarter") {
|
|
454
|
+
return monthlyBaseline * 3;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return monthlyBaseline;
|
|
458
|
+
};
|
|
459
|
+
|
|
290
460
|
const chartArray = viewMode === "scopes" ? scopesArray : subcategoriesArray;
|
|
291
461
|
|
|
292
462
|
return (
|
|
@@ -294,7 +464,6 @@ const EmissionsChart = ({
|
|
|
294
464
|
<Grid item>
|
|
295
465
|
<LabeledEmissionsChart
|
|
296
466
|
data={chartData}
|
|
297
|
-
chartRef={chartRef}
|
|
298
467
|
type={type}
|
|
299
468
|
displayUnitLabel={
|
|
300
469
|
isPercentageChart ? "%" : displayUnit || displayUnitLabel
|
|
@@ -302,6 +471,9 @@ const EmissionsChart = ({
|
|
|
302
471
|
chartArray={chartArray.map((obj) => ({ ...obj, viewMode }))}
|
|
303
472
|
aspect={aspect}
|
|
304
473
|
showTooltip={showTooltip}
|
|
474
|
+
graphPeriod={graphPeriod}
|
|
475
|
+
netZeroPercentage={netZeroPercentage}
|
|
476
|
+
baseline={findFirstYearBaseline()}
|
|
305
477
|
/>
|
|
306
478
|
</Grid>
|
|
307
479
|
</Grid>
|