@mozaic-ds/chart 0.1.0-beta.1 → 0.1.0-beta.11
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/mozaic-chart.js +2801 -2129
- package/dist/mozaic-chart.umd.cjs +11 -11
- package/dist/style.css +1 -1
- package/package.json +25 -11
- package/src/components/bar/BarChart.stories.ts +7 -10
- package/src/components/bar/BarChart.vue +212 -136
- package/src/components/bar/index.ts +8 -0
- package/src/components/doughnut/DoughnutChart.stories.ts +1 -0
- package/src/components/doughnut/DoughnutChart.vue +163 -100
- package/src/components/doughnut/index.ts +8 -0
- package/src/components/index.ts +4 -0
- package/src/components/line/LineChart.stories.ts +0 -1
- package/src/components/line/LineChart.vue +305 -257
- package/src/components/line/index.ts +8 -0
- package/src/components/mixed/MixedBarLineChart.stories.ts +87 -0
- package/src/components/mixed/MixedBarLineChart.vue +404 -0
- package/src/components/mixed/index.ts +8 -0
- package/src/components/radar/RadarChart.stories.ts +2 -2
- package/src/components/radar/RadarChart.vue +210 -150
- package/src/components/radar/index.ts +8 -0
- package/src/main.ts +2 -1
- package/src/plugin.ts +19 -0
- package/src/services/ChartsCommonLegend.ts +35 -23
- package/src/services/DoughnutChartFunctions.ts +101 -56
- package/src/services/FormatUtilities.ts +1 -1
- package/src/services/GenericTooltipService.ts +21 -7
- package/src/services/MixedBarLineFunctions.ts +280 -0
- package/src/services/RadarChartFunctions.ts +13 -11
- package/src/stories/Changelog.mdx +6 -0
- package/src/stories/Contributing.mdx +101 -0
- package/src/stories/GettingStarted.mdx +92 -0
- package/src/stories/SupportAndOnboarding.mdx +44 -0
- package/src/types/MixedBarLineData.ts +7 -0
- package/src/types/TooltipChartType.ts +1 -0
|
@@ -1,85 +1,130 @@
|
|
|
1
|
-
import { ref } from
|
|
2
|
-
import type { Ref } from
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
} from '../services/ChartsCommonLegend';
|
|
6
|
-
import { formatWithThousandsSeprators } from '../services/FormatUtilities';
|
|
1
|
+
import { ref } from "vue";
|
|
2
|
+
import type { Ref } from "vue";
|
|
3
|
+
import { getHtmlLegendPlugin } from "../services/ChartsCommonLegend";
|
|
4
|
+
import { formatWithThousandsSeprators } from "../services/FormatUtilities";
|
|
7
5
|
|
|
8
|
-
import
|
|
9
|
-
import {addAlpha} from './ColorFunctions';
|
|
6
|
+
import { addAlpha } from "./ColorFunctions";
|
|
10
7
|
|
|
11
8
|
export default function () {
|
|
12
9
|
const doughnutRef: Ref = ref(null);
|
|
13
10
|
const onHoverIndex: Ref<number | null> = ref(null);
|
|
14
11
|
const backgroundColor: Ref<CanvasPattern[] | null> = ref(null);
|
|
15
12
|
|
|
16
|
-
function privateGetHtmlLegendPlugin(
|
|
17
|
-
|
|
13
|
+
function privateGetHtmlLegendPlugin(
|
|
14
|
+
legendContainer: Ref,
|
|
15
|
+
selectMode: Ref<boolean>,
|
|
16
|
+
disableAccessibility: Ref<boolean>,
|
|
17
|
+
patternsColors: Ref<string[]>,
|
|
18
|
+
patternsList: Ref<
|
|
19
|
+
((
|
|
20
|
+
hover: boolean,
|
|
21
|
+
color: string,
|
|
22
|
+
disableAccessibility: boolean
|
|
23
|
+
) => CanvasPattern)[]
|
|
24
|
+
>,
|
|
25
|
+
maxValueToDisplay: number,
|
|
26
|
+
doughnutData: any
|
|
27
|
+
) {
|
|
28
|
+
return getHtmlLegendPlugin(
|
|
29
|
+
legendContainer,
|
|
30
|
+
selectMode,
|
|
31
|
+
onHoverIndex,
|
|
32
|
+
disableAccessibility,
|
|
33
|
+
patternsColors,
|
|
34
|
+
patternsList,
|
|
35
|
+
maxValueToDisplay,
|
|
36
|
+
doughnutData
|
|
37
|
+
);
|
|
18
38
|
}
|
|
19
39
|
|
|
20
|
-
|
|
21
|
-
|
|
40
|
+
function getBackgroundColor(
|
|
41
|
+
patternsColors: string[],
|
|
42
|
+
patternsList: ((
|
|
43
|
+
hover: boolean,
|
|
44
|
+
color: string,
|
|
45
|
+
disableAccessibility: boolean
|
|
46
|
+
) => CanvasPattern)[],
|
|
47
|
+
disableAccessibility: boolean
|
|
48
|
+
) {
|
|
22
49
|
if (onHoverIndex.value !== null) {
|
|
23
|
-
return patternsList
|
|
24
|
-
|
|
50
|
+
return patternsList.map((pattern, index) =>
|
|
51
|
+
onHoverIndex.value === index
|
|
52
|
+
? pattern(false, patternsColors[index], disableAccessibility)
|
|
53
|
+
: pattern(true, patternsColors[index], disableAccessibility)
|
|
54
|
+
);
|
|
25
55
|
} else {
|
|
26
|
-
return patternsList
|
|
27
|
-
|
|
56
|
+
return patternsList.map((pattern, index) =>
|
|
57
|
+
pattern(false, patternsColors[index], disableAccessibility)
|
|
58
|
+
);
|
|
28
59
|
}
|
|
29
60
|
}
|
|
30
61
|
|
|
31
62
|
function getBorderColor(patternsColors: string[]): string[] {
|
|
32
63
|
if (onHoverIndex.value !== null) {
|
|
33
|
-
return patternsColors.map((color, index) =>
|
|
64
|
+
return patternsColors.map((color, index) =>
|
|
65
|
+
onHoverIndex.value === index ? color : addAlpha(color, 0.2)
|
|
66
|
+
);
|
|
34
67
|
} else {
|
|
35
68
|
return patternsColors;
|
|
36
69
|
}
|
|
37
70
|
}
|
|
38
71
|
|
|
72
|
+
function getOnHoverOptions() {
|
|
73
|
+
return (_ignore: unknown, activeElements: Array<any>): void => {
|
|
74
|
+
if (activeElements[0] !== undefined) {
|
|
75
|
+
onHoverIndex.value = activeElements[0].element.$context.index;
|
|
76
|
+
} else {
|
|
77
|
+
onHoverIndex.value = null;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
39
81
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (activeElements[0] !== undefined) {
|
|
43
|
-
onHoverIndex.value = activeElements[0].element.$context.index;
|
|
44
|
-
} else {
|
|
45
|
-
onHoverIndex.value = null;
|
|
46
|
-
}
|
|
82
|
+
const getFormatedText = (str: string) => {
|
|
83
|
+
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
|
47
84
|
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const getFormatedText = (str: string) => {
|
|
51
|
-
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
|
52
|
-
};
|
|
53
85
|
|
|
54
|
-
function getDoughnutLabels(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
truncatedLabels.
|
|
86
|
+
function getDoughnutLabels(
|
|
87
|
+
labels: string[],
|
|
88
|
+
data: any[],
|
|
89
|
+
maxValues: number,
|
|
90
|
+
othersLabel: string
|
|
91
|
+
) {
|
|
92
|
+
let truncatedLabels = labels.slice(0);
|
|
93
|
+
let truncatedData = data.slice(0);
|
|
94
|
+
if (labels.length > maxValues) {
|
|
95
|
+
truncatedData = groupDataAfterNthValue(data, maxValues);
|
|
96
|
+
truncatedLabels = truncatedLabels.slice(0, maxValues - 1);
|
|
97
|
+
truncatedLabels.push(othersLabel);
|
|
98
|
+
}
|
|
99
|
+
return truncatedLabels.map(
|
|
100
|
+
(label: string, index: number) =>
|
|
101
|
+
`${getFormatedText(label)} (${formatWithThousandsSeprators(
|
|
102
|
+
truncatedData[index].rate as number
|
|
103
|
+
)} %)`
|
|
104
|
+
);
|
|
61
105
|
}
|
|
62
|
-
return truncatedLabels.map((label: string, index: number) => `${getFormatedText(label)} (${formatWithThousandsSeprators(truncatedData[index].rate as number)} %)`);
|
|
63
|
-
}
|
|
64
106
|
|
|
65
|
-
function groupDataAfterNthValue
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
107
|
+
function groupDataAfterNthValue(data: any, maxValues: number): any[] {
|
|
108
|
+
if (maxValues < 1) {
|
|
109
|
+
return data;
|
|
110
|
+
}
|
|
111
|
+
let truncatedData = data.slice(0);
|
|
112
|
+
if (data.length > maxValues) {
|
|
113
|
+
truncatedData = truncatedData.slice(0, maxValues);
|
|
114
|
+
truncatedData[maxValues - 1] = data.slice(maxValues).reduce(
|
|
115
|
+
(result: any, current: any) => {
|
|
116
|
+
result.rate += current.rate;
|
|
117
|
+
result.value += current.value;
|
|
118
|
+
return result;
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
rate: data[maxValues - 1].rate,
|
|
122
|
+
value: data[maxValues - 1].value,
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
return truncatedData;
|
|
80
127
|
}
|
|
81
|
-
return truncatedData;
|
|
82
|
-
}
|
|
83
128
|
|
|
84
129
|
return {
|
|
85
130
|
onHoverIndex,
|
|
@@ -91,6 +136,6 @@ function groupDataAfterNthValue (data: any, maxValues: number): any[] {
|
|
|
91
136
|
getFormatedText,
|
|
92
137
|
getBorderColor,
|
|
93
138
|
backgroundColor,
|
|
94
|
-
doughnutRef
|
|
139
|
+
doughnutRef,
|
|
95
140
|
};
|
|
96
141
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export function formatTicks (val: number, unit?: string
|
|
1
|
+
export function formatTicks (val: number, unit?: string): string {
|
|
2
2
|
const fixedValue = parseInt(val.toFixed());
|
|
3
3
|
return `${new Intl.NumberFormat().format(fixedValue)}${unit ? ' ' + unit : ''}`;
|
|
4
4
|
}
|
|
@@ -39,6 +39,9 @@ export type Context = {
|
|
|
39
39
|
replay?: unknown;
|
|
40
40
|
tooltip: {
|
|
41
41
|
dataPoints: {
|
|
42
|
+
dataset?: {
|
|
43
|
+
type?: string
|
|
44
|
+
},
|
|
42
45
|
dataIndex?: number,
|
|
43
46
|
datasetIndex?: number
|
|
44
47
|
}[];
|
|
@@ -110,6 +113,7 @@ export class GenericTooltipService {
|
|
|
110
113
|
const body = this.chartType === 'DOUGHNUT' ? [tooltipModel.title[0].split('(')[0].trim()]: bodyLines[0];
|
|
111
114
|
let legendIconStyle = '';
|
|
112
115
|
let legendInnerStyle = '';
|
|
116
|
+
const datasetType = context.tooltip?.dataPoints[0]?.dataset?.type;
|
|
113
117
|
|
|
114
118
|
if (this.chartType === 'RADAR' || this.chartType === 'LINE_CHART') {
|
|
115
119
|
legendIconStyle = this.createLegendStyle(context);
|
|
@@ -118,6 +122,13 @@ export class GenericTooltipService {
|
|
|
118
122
|
this.chartType === 'DETAILS_BAR_CHART' ||
|
|
119
123
|
this.chartType === 'DOUGHNUT') {
|
|
120
124
|
legendIconStyle = this.createPatternLegendStyle(context);
|
|
125
|
+
} else if (this.chartType === 'MIXED_BAR_LINE_CHART') {
|
|
126
|
+
if (datasetType === 'bar') {
|
|
127
|
+
legendIconStyle = this.createPatternLegendStyle(context);
|
|
128
|
+
} else {
|
|
129
|
+
legendIconStyle = this.createLegendStyle(context);
|
|
130
|
+
legendInnerStyle = this.createLegendInnerStyle(context);
|
|
131
|
+
}
|
|
121
132
|
}
|
|
122
133
|
|
|
123
134
|
this.addLegendToDom(
|
|
@@ -129,7 +140,8 @@ export class GenericTooltipService {
|
|
|
129
140
|
tooltipEl,
|
|
130
141
|
patternsColors,
|
|
131
142
|
patternsList,
|
|
132
|
-
disableAccessibility
|
|
143
|
+
disableAccessibility,
|
|
144
|
+
datasetType
|
|
133
145
|
);
|
|
134
146
|
}
|
|
135
147
|
|
|
@@ -188,7 +200,8 @@ export class GenericTooltipService {
|
|
|
188
200
|
style += ';margin-right: 10px';
|
|
189
201
|
style += ';display: flex';
|
|
190
202
|
style += ';align-items: center';
|
|
191
|
-
style += ';justify-content: center
|
|
203
|
+
style += ';justify-content: center';
|
|
204
|
+
style += ';background: rgba(0, 0, 0, 0.1);';
|
|
192
205
|
return style;
|
|
193
206
|
}
|
|
194
207
|
|
|
@@ -215,7 +228,8 @@ export class GenericTooltipService {
|
|
|
215
228
|
tooltipEl: HTMLElement,
|
|
216
229
|
patternsColors: string[],
|
|
217
230
|
patternsList: ((hover: boolean, color: string, disableAccessibility: boolean) => CanvasPattern)[],
|
|
218
|
-
disableAccessibility: boolean = false
|
|
231
|
+
disableAccessibility: boolean = false,
|
|
232
|
+
datasetType?: string
|
|
219
233
|
) {
|
|
220
234
|
let innerHtml = innerHTMLtext;
|
|
221
235
|
let legendImage = `<div class="legendIcon" style="${legendIconStyle}">`;
|
|
@@ -225,9 +239,9 @@ export class GenericTooltipService {
|
|
|
225
239
|
const innerHtmlToAdd = this.setInnerHtmlToAdd(body, style, legendImage);
|
|
226
240
|
innerHtml += innerHtmlToAdd;
|
|
227
241
|
const tableRoot = tooltipEl?.querySelector('.tooltipCtn') as HTMLElement | null;
|
|
228
|
-
|
|
229
242
|
if (tableRoot?.innerHTML != null) {
|
|
230
|
-
this.setInnerHtmlAndPattern(tableRoot, innerHtml, patternsColors, patternsList, disableAccessibility)
|
|
243
|
+
datasetType ? this.setInnerHtmlAndPattern(tableRoot, innerHtml, patternsColors, patternsList, disableAccessibility, datasetType)
|
|
244
|
+
: this.setInnerHtmlAndPattern(tableRoot, innerHtml, patternsColors, patternsList, disableAccessibility);
|
|
231
245
|
}
|
|
232
246
|
}
|
|
233
247
|
|
|
@@ -285,7 +299,7 @@ export class GenericTooltipService {
|
|
|
285
299
|
}
|
|
286
300
|
|
|
287
301
|
|
|
288
|
-
setInnerHtmlAndPattern(tableRoot: HTMLElement, innerHtml: string, patternsColors: string[], patternsList: ((hover: boolean, color: string, disableAccessibility: boolean) => CanvasPattern)[], disableAccessibility: boolean = false) {
|
|
302
|
+
setInnerHtmlAndPattern(tableRoot: HTMLElement, innerHtml: string, patternsColors: string[], patternsList: ((hover: boolean, color: string, disableAccessibility: boolean) => CanvasPattern)[], disableAccessibility: boolean = false, datasetType?: string) {
|
|
289
303
|
tableRoot.innerHTML = innerHtml;
|
|
290
304
|
const legendIconHtml = document.querySelector('.legendIcon') as HTMLElement;
|
|
291
305
|
const img: HTMLImageElement = new Image();
|
|
@@ -298,7 +312,7 @@ export class GenericTooltipService {
|
|
|
298
312
|
index = this.datasetIndex + 1;
|
|
299
313
|
}
|
|
300
314
|
const patternIndex = getPatternIndexWithShift(index, this.patternShifting);
|
|
301
|
-
if (this.chartType !== 'LINE_CHART' && this.chartType !== 'RADAR') {
|
|
315
|
+
if (this.chartType !== 'LINE_CHART' && this.chartType !== 'RADAR' && datasetType !== 'line') {
|
|
302
316
|
const pattern: CanvasPattern = patternsList[patternIndex - 1](false, patternsColors[patternIndex - 1], disableAccessibility);
|
|
303
317
|
const patternCanvas: HTMLCanvasElement = getPatternCanvas(pattern, 22, 22);
|
|
304
318
|
img.src = patternCanvas.toDataURL();
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { reactive, ref, type Ref } from 'vue';
|
|
2
|
+
import PatternFunctions from './PatternFunctions';
|
|
3
|
+
import { addAlpha } from './ColorFunctions';
|
|
4
|
+
import type { HTMLLegendPlugin } from '../types/Chart';
|
|
5
|
+
import {
|
|
6
|
+
getOrCreateLegendList,
|
|
7
|
+
createHtmlLegendListElement,
|
|
8
|
+
createLegendElementWithPatterns,
|
|
9
|
+
createLegendElementWithCheckbox,
|
|
10
|
+
createHtmlLegendItemText,
|
|
11
|
+
createLegendElementWithSquareArea,
|
|
12
|
+
type ChartItem,
|
|
13
|
+
} from './ChartsCommonLegend';
|
|
14
|
+
|
|
15
|
+
const { getPatternIndexWithShift } = PatternFunctions();
|
|
16
|
+
|
|
17
|
+
export default function () {
|
|
18
|
+
const borderWidth = ref(2);
|
|
19
|
+
const onHoverIndex: { dataSetIndex: number; columnIndex: number } = reactive({
|
|
20
|
+
dataSetIndex: -1,
|
|
21
|
+
columnIndex: -1,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
interface Dataset {
|
|
25
|
+
data: any;
|
|
26
|
+
label: string;
|
|
27
|
+
type: any;
|
|
28
|
+
fill?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getHtmlLegendPlugin(
|
|
32
|
+
legendContainer: Ref,
|
|
33
|
+
selectMode: Ref<boolean>,
|
|
34
|
+
onHoverIndex: any,
|
|
35
|
+
disableAccessibility: Ref<boolean>,
|
|
36
|
+
patternsColors: Ref<string[]>,
|
|
37
|
+
patternsList: Ref<
|
|
38
|
+
((
|
|
39
|
+
hover: boolean,
|
|
40
|
+
color: string,
|
|
41
|
+
disableAccessibility: boolean
|
|
42
|
+
) => CanvasPattern)[]
|
|
43
|
+
>,
|
|
44
|
+
): HTMLLegendPlugin[] {
|
|
45
|
+
return [
|
|
46
|
+
{
|
|
47
|
+
id: 'htmlLegend',
|
|
48
|
+
afterUpdate(chart: any) {
|
|
49
|
+
const ul: HTMLLIElement = getOrCreateLegendList(
|
|
50
|
+
legendContainer,
|
|
51
|
+
'column'
|
|
52
|
+
);
|
|
53
|
+
ul.style.display = 'flex';
|
|
54
|
+
ul.style.margin = '1.375rem 1.0625rem';
|
|
55
|
+
ul.style.flexDirection = 'row-reverse';
|
|
56
|
+
ul.style.justifyContent = 'flex-end';
|
|
57
|
+
while (ul.firstChild) {
|
|
58
|
+
ul.firstChild.remove();
|
|
59
|
+
}
|
|
60
|
+
const items: ChartItem[] =
|
|
61
|
+
chart.options.plugins.legend.labels.generateLabels(chart);
|
|
62
|
+
items.forEach((item: ChartItem): void => {
|
|
63
|
+
const li: HTMLElement = createHtmlLegendListElement(
|
|
64
|
+
chart,
|
|
65
|
+
selectMode,
|
|
66
|
+
item.datasetIndex
|
|
67
|
+
);
|
|
68
|
+
let liContent: HTMLElement;
|
|
69
|
+
if (!selectMode.value) {
|
|
70
|
+
if (item?.lineCap) {
|
|
71
|
+
liContent = createLegendElementWithSquareArea(item, onHoverIndex);
|
|
72
|
+
} else {
|
|
73
|
+
liContent = createLegendElementWithPatterns(
|
|
74
|
+
item,
|
|
75
|
+
chart,
|
|
76
|
+
onHoverIndex,
|
|
77
|
+
disableAccessibility.value,
|
|
78
|
+
patternsColors.value,
|
|
79
|
+
patternsList.value
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
liContent = createLegendElementWithCheckbox(
|
|
84
|
+
chart,
|
|
85
|
+
item,
|
|
86
|
+
selectMode,
|
|
87
|
+
onHoverIndex,
|
|
88
|
+
disableAccessibility.value
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
liContent.style.boxSizing = 'border-box';
|
|
92
|
+
li.style.marginRight = '10px';
|
|
93
|
+
li.appendChild(liContent);
|
|
94
|
+
li.appendChild(createHtmlLegendItemText(item));
|
|
95
|
+
ul.appendChild(li);
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function privateGetHtmlLegendPlugin(
|
|
103
|
+
legendContainer: Ref,
|
|
104
|
+
selectMode: Ref<boolean>,
|
|
105
|
+
disableAccessibility: Ref<boolean>,
|
|
106
|
+
patternsColors: Ref<string[]>,
|
|
107
|
+
patternsList: Ref<
|
|
108
|
+
((
|
|
109
|
+
hover: boolean,
|
|
110
|
+
color: string,
|
|
111
|
+
disableAccessibility: boolean
|
|
112
|
+
) => CanvasPattern)[]
|
|
113
|
+
>
|
|
114
|
+
) {
|
|
115
|
+
return getHtmlLegendPlugin(
|
|
116
|
+
legendContainer,
|
|
117
|
+
selectMode,
|
|
118
|
+
onHoverIndex,
|
|
119
|
+
disableAccessibility,
|
|
120
|
+
patternsColors,
|
|
121
|
+
patternsList
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function getOnHoverOptions() {
|
|
126
|
+
return (
|
|
127
|
+
_ignore: unknown,
|
|
128
|
+
activeElements: Array<{ index: number; datasetIndex: number }>
|
|
129
|
+
) => {
|
|
130
|
+
if (activeElements[0] !== undefined) {
|
|
131
|
+
onHoverIndex.dataSetIndex = activeElements[0].datasetIndex;
|
|
132
|
+
onHoverIndex.columnIndex = activeElements[0].index;
|
|
133
|
+
} else {
|
|
134
|
+
resetOnHoverIndex();
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function resetOnHoverIndex() {
|
|
140
|
+
onHoverIndex.dataSetIndex = -1;
|
|
141
|
+
onHoverIndex.columnIndex = -1;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Hack to force the chart to reload on Hover
|
|
145
|
+
function reloadChart() {
|
|
146
|
+
borderWidth.value = 3;
|
|
147
|
+
borderWidth.value = 2;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function getMixedDatasets(
|
|
151
|
+
datasets: Dataset[],
|
|
152
|
+
disableAccessibility: boolean,
|
|
153
|
+
patternsColors: string[],
|
|
154
|
+
patternsList: ((
|
|
155
|
+
hover: boolean,
|
|
156
|
+
color: string,
|
|
157
|
+
disableAccessibility: boolean
|
|
158
|
+
) => CanvasPattern)[],
|
|
159
|
+
patternShifting?: number
|
|
160
|
+
) {
|
|
161
|
+
// Hack to force refresh
|
|
162
|
+
const borderWithValue = borderWidth.value;
|
|
163
|
+
return datasets.map((dataset, index) => {
|
|
164
|
+
return {
|
|
165
|
+
type: dataset.type,
|
|
166
|
+
fill: dataset.type === 'bar' ? null : false,
|
|
167
|
+
borderWidth: function () {
|
|
168
|
+
return disableAccessibility ? 1 : borderWithValue;
|
|
169
|
+
},
|
|
170
|
+
borderColor: function (context: any) {
|
|
171
|
+
return disableAccessibility
|
|
172
|
+
? '#00000000'
|
|
173
|
+
: getBorderColor(
|
|
174
|
+
index,
|
|
175
|
+
context.index,
|
|
176
|
+
patternsColors,
|
|
177
|
+
patternShifting
|
|
178
|
+
);
|
|
179
|
+
},
|
|
180
|
+
backgroundColor: function (context: any) {
|
|
181
|
+
return getPattern(
|
|
182
|
+
index,
|
|
183
|
+
context.index,
|
|
184
|
+
disableAccessibility,
|
|
185
|
+
patternsColors,
|
|
186
|
+
patternsList,
|
|
187
|
+
patternShifting
|
|
188
|
+
);
|
|
189
|
+
},
|
|
190
|
+
yAxisID: dataset.type === 'bar' ? 'A' : 'B',
|
|
191
|
+
pointStyle: index % 2 === 0 ? 'rectRot' : 'circle',
|
|
192
|
+
data: dataset.data,
|
|
193
|
+
label: dataset.label,
|
|
194
|
+
pointBackgroundColor: '#FFFFFF',
|
|
195
|
+
pointRadius: 5,
|
|
196
|
+
order: datasets.length - index,
|
|
197
|
+
};
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function getBorderColor(
|
|
202
|
+
dataSetIndex: number,
|
|
203
|
+
contextIndex: number,
|
|
204
|
+
patternsColors: string[],
|
|
205
|
+
patternShifting?: number
|
|
206
|
+
) {
|
|
207
|
+
const index = getPatternIndexWithShift(dataSetIndex, patternShifting);
|
|
208
|
+
if (displayFullOpacity(dataSetIndex, contextIndex)) {
|
|
209
|
+
return patternsColors[index];
|
|
210
|
+
} else {
|
|
211
|
+
return addAlpha(patternsColors[index], 0.2);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function getPattern(
|
|
216
|
+
dataSetIndex: number,
|
|
217
|
+
contextIndex: number,
|
|
218
|
+
disableAccessibility: boolean,
|
|
219
|
+
patternsColors: string[],
|
|
220
|
+
patternsList: ((
|
|
221
|
+
hover: boolean,
|
|
222
|
+
color: string,
|
|
223
|
+
disableAccessibility: boolean
|
|
224
|
+
) => CanvasPattern)[],
|
|
225
|
+
patternShifting?: number
|
|
226
|
+
) {
|
|
227
|
+
const index = getPatternIndexWithShift(dataSetIndex, patternShifting);
|
|
228
|
+
if (displayFullOpacity(dataSetIndex, contextIndex)) {
|
|
229
|
+
return patternsList[index](
|
|
230
|
+
false,
|
|
231
|
+
patternsColors[index],
|
|
232
|
+
disableAccessibility
|
|
233
|
+
);
|
|
234
|
+
} else {
|
|
235
|
+
return patternsList[index](
|
|
236
|
+
true,
|
|
237
|
+
patternsColors[index],
|
|
238
|
+
disableAccessibility
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function displayFullOpacity(
|
|
244
|
+
dataSetIndex: number,
|
|
245
|
+
contextIndex: number
|
|
246
|
+
): boolean {
|
|
247
|
+
return (
|
|
248
|
+
nothingHovered() ||
|
|
249
|
+
columnHovered(dataSetIndex, contextIndex) ||
|
|
250
|
+
legendHovered(dataSetIndex)
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function nothingHovered(): boolean {
|
|
255
|
+
return onHoverIndex.dataSetIndex < 0;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function columnHovered(dataSetIndex: number, contextIndex: number): boolean {
|
|
259
|
+
return (
|
|
260
|
+
onHoverIndex.dataSetIndex === dataSetIndex &&
|
|
261
|
+
onHoverIndex.columnIndex === contextIndex
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function legendHovered(dataSetIndex: number): boolean {
|
|
266
|
+
return (
|
|
267
|
+
onHoverIndex.dataSetIndex === dataSetIndex && onHoverIndex.columnIndex < 0
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
privateGetHtmlLegendPlugin,
|
|
273
|
+
getOnHoverOptions,
|
|
274
|
+
getMixedDatasets,
|
|
275
|
+
reloadChart,
|
|
276
|
+
getBorderColor,
|
|
277
|
+
getPattern,
|
|
278
|
+
onHoverIndex,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Chart, RadialLinearScale } from 'chart.js';
|
|
2
2
|
|
|
3
|
-
export function drawLabels
|
|
3
|
+
export function drawLabels(chart: Chart, props: any) {
|
|
4
4
|
const ctx = chart.ctx;
|
|
5
5
|
const scale = chart.scales.r as RadialLinearScale;
|
|
6
6
|
const labels = chart.data.labels as string[][];
|
|
@@ -14,37 +14,39 @@ export function drawLabels (chart: Chart, props: any) {
|
|
|
14
14
|
const angle = (scale.getIndexAngle(index) - Math.PI / 2) % (2 * Math.PI);
|
|
15
15
|
const position = scale.getPointPositionForValue(index, scale.max);
|
|
16
16
|
|
|
17
|
-
ctx.textAlign =
|
|
18
|
-
|
|
17
|
+
ctx.textAlign =
|
|
18
|
+
angle <= Math.PI / 2 || angle > (3 * Math.PI) / 2 ? 'left' : 'right';
|
|
19
|
+
let xOffset = angle <= Math.PI / 2 || angle > (3 * Math.PI) / 2 ? 15 : -15;
|
|
19
20
|
|
|
20
21
|
let yOffset;
|
|
21
22
|
//top or bottom labels
|
|
22
|
-
if (
|
|
23
|
+
if (angle < 0 || angle > Math.PI) {
|
|
23
24
|
yOffset = -15;
|
|
24
25
|
} else {
|
|
25
26
|
yOffset = 15;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
//bottom labels
|
|
29
|
-
if (
|
|
30
|
+
if (angle > Math.PI / 4 && angle < (3 * Math.PI) / 4) {
|
|
30
31
|
yOffset *= 3;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
//top labels left and right
|
|
34
|
-
if (
|
|
35
|
+
if (angle < -Math.PI / 4 || angle > (5 * Math.PI) / 4) {
|
|
35
36
|
yOffset *= 3;
|
|
36
37
|
xOffset = 0;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
// full top label
|
|
40
|
-
if (
|
|
41
|
+
if (angle > (11 * Math.PI) / 8 || angle < (-3 * Math.PI) / 8) {
|
|
41
42
|
ctx.textAlign = 'center';
|
|
42
43
|
}
|
|
43
|
-
const yPos = position.y + yOffset * (label.length - 1) / 2;
|
|
44
|
+
const yPos = position.y + (yOffset * (label.length - 1)) / 2;
|
|
44
45
|
ctx.font = '15px Arial';
|
|
45
46
|
|
|
46
47
|
label.forEach((text, i) => {
|
|
47
|
-
const color =
|
|
48
|
+
const color =
|
|
49
|
+
i === label.length - 1 ? buildColorArray(props)[index] : '#000000';
|
|
48
50
|
ctx.fillStyle = color;
|
|
49
51
|
|
|
50
52
|
const x = position.x + xOffset;
|
|
@@ -56,8 +58,8 @@ export function drawLabels (chart: Chart, props: any) {
|
|
|
56
58
|
ctx.restore();
|
|
57
59
|
}
|
|
58
60
|
|
|
59
|
-
const buildColorArray = (props: any): string[]=> {
|
|
60
|
-
return props.
|
|
61
|
+
const buildColorArray = (props: any): string[] => {
|
|
62
|
+
return props.datasets[0].areaData.map((x: { color: string }) => {
|
|
61
63
|
switch (x.color) {
|
|
62
64
|
case 'red':
|
|
63
65
|
return '#C61112';
|