@mozaic-ds/chart 0.1.0-beta.4 → 0.1.0-beta.40

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 (59) hide show
  1. package/README.md +85 -9
  2. package/dist/mozaic-chart.js +4747 -2614
  3. package/dist/mozaic-chart.umd.cjs +17 -11
  4. package/dist/style.css +1 -1
  5. package/package.json +24 -11
  6. package/src/assets/base.css +1 -1
  7. package/src/assets/img/bubbles.svg +4 -0
  8. package/src/components/bar/BarChart.stories.ts +181 -101
  9. package/src/components/bar/BarChart.vue +370 -139
  10. package/src/components/bar/index.ts +3 -3
  11. package/src/components/bubble/BubbleChart.stories.ts +66 -0
  12. package/src/components/bubble/BubbleChart.vue +432 -0
  13. package/src/components/bubble/index.ts +8 -0
  14. package/src/components/doughnut/DoughnutChart.stories.ts +38 -36
  15. package/src/components/doughnut/DoughnutChart.vue +243 -114
  16. package/src/components/doughnut/index.ts +3 -3
  17. package/src/components/index.ts +4 -4
  18. package/src/components/line/LineChart.stories.ts +52 -27
  19. package/src/components/line/LineChart.vue +381 -255
  20. package/src/components/line/index.ts +3 -3
  21. package/src/components/mixed/MixedBarLineChart.stories.ts +91 -0
  22. package/src/components/mixed/MixedBarLineChart.vue +469 -0
  23. package/src/components/mixed/index.ts +8 -0
  24. package/src/components/radar/RadarChart.stories.ts +102 -102
  25. package/src/components/radar/RadarChart.vue +222 -167
  26. package/src/components/radar/index.ts +3 -3
  27. package/src/main.ts +14 -5
  28. package/src/plugin.ts +9 -7
  29. package/src/services/BarChartFunctions.ts +139 -35
  30. package/src/services/BubbleTooltipService.ts +67 -0
  31. package/src/services/ChartsCommonLegend.ts +315 -139
  32. package/src/services/ColorFunctions.ts +1 -1
  33. package/src/services/DoughnutChartFunctions.ts +132 -55
  34. package/src/services/FormatUtilities.ts +25 -19
  35. package/src/services/GenericTooltipService.ts +141 -66
  36. package/src/services/MixedBarLineFunctions.ts +262 -0
  37. package/src/services/PatternFunctions.ts +25 -18
  38. package/src/services/RadarChartFunctions.ts +39 -12
  39. package/src/services/patterns/ChartDesign.ts +35 -24
  40. package/src/services/patterns/patternCircles.ts +63 -36
  41. package/src/services/patterns/patternDashedDiagonals.ts +64 -57
  42. package/src/services/patterns/patternDiagonals.ts +138 -106
  43. package/src/services/patterns/patternSquares.ts +86 -80
  44. package/src/services/patterns/patternVerticalLines.ts +76 -69
  45. package/src/services/patterns/patternZigzag.ts +92 -85
  46. package/src/stories/Changelog.mdx +6 -0
  47. package/src/stories/Contributing.mdx +101 -0
  48. package/src/stories/GettingStarted.mdx +92 -0
  49. package/src/stories/SupportAndOnboarding.mdx +44 -0
  50. package/src/types/AxisDefinition.ts +4 -0
  51. package/src/types/BarData.ts +1 -0
  52. package/src/types/Chart.ts +9 -7
  53. package/src/types/DoughnutData.ts +8 -0
  54. package/src/types/GenericData.ts +10 -10
  55. package/src/types/LineChart.ts +5 -4
  56. package/src/types/MixedBarLineData.ts +7 -0
  57. package/src/types/RadarData.ts +33 -29
  58. package/src/types/TooltipChartType.ts +7 -6
  59. package/src/vite-env.d.ts +3 -3
@@ -0,0 +1,91 @@
1
+ // MixedBarLineChart.stories.ts
2
+
3
+ import type { Meta, StoryObj } from '@storybook/vue3';
4
+ import MixedBarLineChart from './MixedBarLineChart.vue';
5
+
6
+ const meta = {
7
+ title: 'Charts/MixedBarLine',
8
+ component: MixedBarLineChart
9
+ } satisfies Meta<typeof MixedBarLineChart>;
10
+
11
+ export default meta;
12
+ type Story = StoryObj<typeof meta>;
13
+
14
+ export const Default = {
15
+ args: {
16
+ width: '500px',
17
+ height: '400px',
18
+ tooltipFirstLineLabel: 'content',
19
+ tooltipSecondLineLabel: 'content2',
20
+ disableAccessibility: false,
21
+ newPatternsOrder: [0, 1, 0, 1, 2, 3],
22
+ colourSet: 0,
23
+ barUnit: '',
24
+ lineUnit: '€',
25
+ xAxisTitle: 'X Axis',
26
+ yLeftAxisTitle: 'Y1 Axis',
27
+ yRightAxisTitle: 'Y2 Axis',
28
+ barDatasets: [
29
+ {
30
+ type: 'bar',
31
+ label: 'Bar Label 1',
32
+ data: [10, 20, 30, 40],
33
+ borderColor: 'rgb(255, 99, 132)',
34
+ backgroundColor: 'rgba(255, 99, 132, 0.2)'
35
+ }
36
+ ],
37
+ lineDatasets: [
38
+ {
39
+ type: 'line',
40
+ label: 'Line Label 1',
41
+ data: [5, 15, 20, 25],
42
+ borderColor: 'rgb(54, 162, 235)'
43
+ }
44
+ ],
45
+ labels: ['Label 1', 'Label 2', 'Label 3', 'Label 4']
46
+ }
47
+ } satisfies Story;
48
+
49
+ export const Multiple_Data = {
50
+ args: {
51
+ width: '500px',
52
+ height: '400px',
53
+ tooltipFirstLineLabel: 'content',
54
+ tooltipSecondLineLabel: 'content2',
55
+ disableAccessibility: false,
56
+ newPatternsOrder: [0, 1, 0, 1, 2, 3],
57
+ colourSet: 0,
58
+ lineUnit: '€',
59
+ barDatasets: [
60
+ {
61
+ type: 'bar',
62
+ label: 'Bar Label 1',
63
+ data: [10, 20, 30, 40],
64
+ borderColor: 'rgb(255, 99, 132)',
65
+ backgroundColor: 'rgba(255, 99, 132, 0.2)'
66
+ },
67
+ {
68
+ type: 'bar',
69
+ label: 'Bar Label 2',
70
+ data: [20, 20, 30, 40],
71
+ borderColor: 'rgb(255, 99, 132)',
72
+ backgroundColor: 'rgba(255, 99, 132, 0.2)'
73
+ }
74
+ ],
75
+ lineDatasets: [
76
+ {
77
+ type: 'line',
78
+ label: 'Line Label 1',
79
+ data: [50, 150, 20, 250],
80
+ borderColor: 'rgb(54, 162, 235)'
81
+ },
82
+ {
83
+ type: 'line',
84
+ label: 'Line Label 2',
85
+ data: [1, 8, 6, 24],
86
+ borderColor: 'rgb(54, 162, 235)'
87
+ }
88
+ ],
89
+ labels: ['Label 1', 'Label 2', 'Label 3', 'Label 4']
90
+ }
91
+ } satisfies Story;
@@ -0,0 +1,469 @@
1
+ <template>
2
+ <div class="container">
3
+ <div class="main">
4
+ <Bar
5
+ v-if="chartData"
6
+ ref="mixedBarLineChartRef"
7
+ :id="chartId"
8
+ :data="chartData"
9
+ :options="options"
10
+ :plugins="htmlLegendPlugin"
11
+ :class="cssClasses"
12
+ :style="[{ width, height, cursor: 'pointer' }, styles]"
13
+ />
14
+ </div>
15
+ <div ref="legendContainer" />
16
+ </div>
17
+ </template>
18
+
19
+ <script setup lang="ts">
20
+ import { computed, ref, PropType } from 'vue';
21
+ import { Bar } from 'vue-chartjs';
22
+ import type { Context } from '../../services/GenericTooltipService';
23
+ import { GenericTooltipService } from '../../services/GenericTooltipService';
24
+ import { TooltipChartType } from '../../types/TooltipChartType';
25
+ import {
26
+ formatDecimalNumber,
27
+ formatTicks,
28
+ formatWithThousandsSeprators
29
+ } from '../../services/FormatUtilities';
30
+ import {
31
+ BarElement,
32
+ CategoryScale,
33
+ Chart as ChartJS,
34
+ Legend,
35
+ LinearScale,
36
+ Title,
37
+ Tooltip,
38
+ LineElement,
39
+ PointElement,
40
+ Plugin
41
+ } from 'chart.js';
42
+ import { MixedBarLineData } from '../../types/MixedBarLineData';
43
+ import ChartDesign from '../../services/patterns/ChartDesign';
44
+ import mixedBarLineFunctions from '../../services/MixedBarLineFunctions';
45
+
46
+ ChartJS.register(
47
+ Title,
48
+ Tooltip,
49
+ Legend,
50
+ BarElement,
51
+ CategoryScale,
52
+ LinearScale,
53
+ LineElement,
54
+ PointElement
55
+ );
56
+
57
+ const { privateGetHtmlLegendPlugin, getMixedDatasets } =
58
+ mixedBarLineFunctions();
59
+
60
+ const { colourSets, patternsStandardList } = ChartDesign();
61
+
62
+ const legendContainer = ref(null);
63
+ const selectMode = ref(false);
64
+ const mixedBarLineDataProps = defineProps({
65
+ /**
66
+ * Value of the id attribute present on the <canvas> tag element the chart
67
+ */
68
+ chartId: {
69
+ type: String,
70
+ default: 'mixed-bar-line-chart'
71
+ },
72
+ /**
73
+ * Unit of values on canvas Y axis
74
+ */
75
+ unit: {
76
+ type: String,
77
+ default: ''
78
+ },
79
+ /**
80
+ * Labels used to label the index axis (default x axes). See [Data structures documentation](https://www.chartjs.org/docs/latest/general/data-structures.html)
81
+ */
82
+ labels: {
83
+ type: Array as PropType<string[]>,
84
+ default: () => []
85
+ },
86
+ /**
87
+ * Used to choose the colour set of the charts as defined in the Figma prototypes.
88
+ * 7 colour sets are currently defined:
89
+ * - Default 0 corresponds to the current one
90
+ * - 1 to 6 corresponds to the "new" [colour sets](https://www.figma.com/file/Hn6PyvnR385Ta0XN3KqOI9/04.-Dataviz---Documentation-(read-only)?type=design&node-id=1-69316&mode=design&t=sDytQ5BipsryWkuA-0)
91
+ * Note: All the sets are defined in /src/services/patterns/ChartDesign.ts
92
+ */
93
+ colourSet: {
94
+ type: Number,
95
+ default: 0
96
+ },
97
+ /**
98
+ * 6 patterns exist and are not randomly given but follow the order defined in [patternsStandardList](/src/services/patterns/ChartDesign.ts)
99
+ * Additionally, a pattern has only one possible colour per colour set as defined in the Figma prototype.
100
+ * In some use cases, the chart may need to show a different orders of these patterns, this can be changed using the props newPatternsOrder
101
+ */
102
+ newPatternsOrder: {
103
+ type: Array as PropType<number[]>,
104
+ default: () => [0, 1, 2, 3, 4, 5]
105
+ },
106
+ /**
107
+ * Value of the `bar datasets` key present in the `data` object passed to the Chart config
108
+ */
109
+ barDatasets: {
110
+ type: Array as PropType<MixedBarLineData[]>,
111
+ default: () => []
112
+ },
113
+ /**
114
+ * Unit of the `bar datasets`
115
+ */
116
+ barUnit: {
117
+ type: String,
118
+ default: ''
119
+ },
120
+ /**
121
+ * Value of the `line datasets` key present in the `data` object passed to the Chart config
122
+ */
123
+ lineDatasets: {
124
+ type: Array as PropType<MixedBarLineData[]>,
125
+ default: () => []
126
+ },
127
+ /**
128
+ * Unit of the `line datasets`
129
+ */
130
+ lineUnit: {
131
+ type: String,
132
+ default: ''
133
+ },
134
+ /**
135
+ * Value of the `width` css property used to define the width of the <canvas> element
136
+ */
137
+ width: {
138
+ type: String,
139
+ default: '400px'
140
+ },
141
+ /**
142
+ * Value of the `height` css property used to define the height of the <canvas> element
143
+ */
144
+ height: {
145
+ type: String,
146
+ default: '300px'
147
+ },
148
+ /**
149
+ * Add custom CSS classes to the <canvas> element
150
+ */
151
+ cssClasses: {
152
+ type: String,
153
+ default: undefined
154
+ },
155
+ /**
156
+ * Add custom CSS styles to the <canvas> element
157
+ */
158
+ styles: {
159
+ type: Object as PropType<Partial<CSSStyleDeclaration>>,
160
+ default: () => {}
161
+ },
162
+ /**
163
+ * Value of the `plugins` key passed to the Chart config
164
+ */
165
+ plugins: {
166
+ type: Array as PropType<Plugin<'bar'>[]>,
167
+ default: () => []
168
+ },
169
+ /**
170
+ * Activates "stacked" mode
171
+ */
172
+ stacked: {
173
+ type: Boolean,
174
+ default: false
175
+ },
176
+ /**
177
+ * Disable accessibility patterns
178
+ */
179
+ disableAccessibility: {
180
+ type: Boolean,
181
+ default: false
182
+ },
183
+ /**
184
+ * Label of the first line in the Tooltip
185
+ */
186
+ tooltipFirstLineLabel: {
187
+ type: String,
188
+ default: 'content'
189
+ },
190
+ /**
191
+ * Label of the second line in the Tooltip
192
+ */
193
+ tooltipSecondLineLabel: {
194
+ type: String,
195
+ default: 'content2'
196
+ },
197
+ /**
198
+ * Title of the x axis
199
+ */
200
+ xAxisTitle: {
201
+ type: String,
202
+ default: null
203
+ },
204
+ /**
205
+ * Title of the y left axis
206
+ */
207
+ yLeftAxisTitle: {
208
+ type: String,
209
+ default: null
210
+ },
211
+ /**
212
+ * Title of the y right axis
213
+ */
214
+ yRightAxisTitle: {
215
+ type: String,
216
+ default: null
217
+ },
218
+ /**
219
+ * Value of the minimum number of fraction digits used to format the line values
220
+ */
221
+ lineValueMinimumFractionDigits: {
222
+ type: Number,
223
+ required: false
224
+ },
225
+ /**
226
+ * Value of the maximum number of fraction digits used to format the line values
227
+ */
228
+ lineValueMaximumFractionDigits: {
229
+ type: Number,
230
+ required: false
231
+ },
232
+ /**
233
+ * Value of the minimum number of fraction digits used to format the bar values
234
+ */
235
+ barValueMinimumFractionDigits: {
236
+ type: Number,
237
+ required: false
238
+ },
239
+ /**
240
+ * Value of the maximum number of fraction digits used to format the bar values
241
+ */
242
+ barValueMaximumFractionDigits: {
243
+ type: Number,
244
+ required: false
245
+ },
246
+ /**
247
+ * Value of the minimum number of fraction digits used to format the line ticks values
248
+ */
249
+ lineTicksMinimumFractionDigits: {
250
+ type: Number,
251
+ required: false
252
+ },
253
+ /**
254
+ * Value of the maximum number of fraction digits used to format the line ticks values
255
+ */
256
+ lineTicksMaximumFractionDigits: {
257
+ type: Number,
258
+ required: false
259
+ },
260
+ /**
261
+ * Value of the minimum number of fraction digits used to format the bar ticks values
262
+ */
263
+ barTicksMinimumFractionDigits: {
264
+ type: Number,
265
+ required: false
266
+ },
267
+ /**
268
+ * Value of the maximum number of fraction digits used to format the bar ticks values
269
+ */
270
+ barTicksMaximumFractionDigits: {
271
+ type: Number,
272
+ required: false
273
+ }
274
+ });
275
+
276
+ const chartDatasets = computed(() => {
277
+ let datasets: MixedBarLineData[] = [];
278
+ if (mixedBarLineDataProps.barDatasets) {
279
+ datasets = datasets.concat(mixedBarLineDataProps.barDatasets);
280
+ }
281
+ if (mixedBarLineDataProps.lineDatasets) {
282
+ datasets = datasets.concat(mixedBarLineDataProps.lineDatasets);
283
+ }
284
+ return datasets;
285
+ });
286
+
287
+ // computed to make the colors list reactive to the props
288
+ const patternsColors = computed(() => {
289
+ return mixedBarLineDataProps.newPatternsOrder.length !==
290
+ patternsStandardList.length
291
+ ? colourSets[mixedBarLineDataProps.colourSet]
292
+ : mixedBarLineDataProps.newPatternsOrder.map((id) => {
293
+ return colourSets[mixedBarLineDataProps.colourSet][id];
294
+ });
295
+ });
296
+
297
+ // computed to make the patterns list reactive to the props
298
+ const patternsOrderedList = computed(() => {
299
+ return mixedBarLineDataProps.newPatternsOrder.length !==
300
+ patternsStandardList.length
301
+ ? patternsStandardList
302
+ : mixedBarLineDataProps.newPatternsOrder.map((id) => {
303
+ return patternsStandardList[id];
304
+ });
305
+ });
306
+
307
+ const disablePattern = computed(() => {
308
+ return mixedBarLineDataProps.disableAccessibility;
309
+ });
310
+
311
+ const chartData = computed(() => {
312
+ const chartsLabels = getChartLabels();
313
+ return {
314
+ labels: chartsLabels,
315
+ datasets: getMixedDatasets(
316
+ chartDatasets.value,
317
+ disablePattern.value,
318
+ patternsColors.value,
319
+ patternsOrderedList.value,
320
+ 0
321
+ )
322
+ };
323
+ });
324
+
325
+ const getTooltipData = (context: Context) => {
326
+ const datasetIndex = context.tooltip.dataPoints[0].datasetIndex as number;
327
+ const dataIndex = context.tooltip.dataPoints[0].dataIndex as number;
328
+ const rawdata: number = chartDatasets.value[datasetIndex].data[dataIndex];
329
+ const amountUnit =
330
+ chartDatasets.value[datasetIndex].type === 'bar'
331
+ ? mixedBarLineDataProps.barUnit
332
+ : mixedBarLineDataProps.lineUnit;
333
+ const data = chartDatasets.value[datasetIndex].type === 'bar'?
334
+ formatDecimalNumber(rawdata, mixedBarLineDataProps.barValueMinimumFractionDigits, mixedBarLineDataProps.barValueMaximumFractionDigits) :
335
+ formatDecimalNumber(rawdata, mixedBarLineDataProps.lineValueMinimumFractionDigits, mixedBarLineDataProps.lineValueMaximumFractionDigits);
336
+ return data + ' ' + amountUnit;
337
+ };
338
+
339
+ const getChartLabels = () => {
340
+ const labels = Object.assign([], mixedBarLineDataProps.labels);
341
+
342
+ return labels.map((label: string) => label);
343
+ };
344
+
345
+ const options = computed(() => ({
346
+ type: 'bar',
347
+ responsive: true,
348
+ maintainAspectRatio: false,
349
+ maxBarThickness: 64,
350
+ categoryPercentage: 0.6,
351
+ elements: {
352
+ bar: {
353
+ borderSkipped: false
354
+ }
355
+ },
356
+ plugins: {
357
+ legend: {
358
+ display: false
359
+ },
360
+ title: {
361
+ display: false
362
+ },
363
+ datalabels: {
364
+ display: false
365
+ },
366
+ tooltip: {
367
+ enabled: false,
368
+ external: function (context: Context) {
369
+ new GenericTooltipService().createTooltip(
370
+ context,
371
+ getTooltipData,
372
+ {
373
+ chartType: TooltipChartType.MIXED_BAR_LINE_CHART,
374
+ firstLineLabel: mixedBarLineDataProps.tooltipFirstLineLabel,
375
+ secondLineLabel: mixedBarLineDataProps.tooltipSecondLineLabel
376
+ },
377
+ patternsColors.value,
378
+ patternsOrderedList.value,
379
+ mixedBarLineDataProps.disableAccessibility
380
+ );
381
+ }
382
+ }
383
+ },
384
+ scales: {
385
+ x: {
386
+ offset: true,
387
+ title: {
388
+ display: mixedBarLineDataProps.xAxisTitle !== null,
389
+ text: mixedBarLineDataProps.xAxisTitle
390
+ }
391
+ },
392
+ A: {
393
+ type: 'linear' as const,
394
+ display: true,
395
+ position: 'left' as const,
396
+ grid: {
397
+ drawOnChartArea: true
398
+ },
399
+ title: {
400
+ display: mixedBarLineDataProps.yLeftAxisTitle !== null,
401
+ text: mixedBarLineDataProps.yLeftAxisTitle
402
+ },
403
+ ticks: {
404
+ callback: barTicksCallback,
405
+ }
406
+ },
407
+ B: {
408
+ type: 'linear' as const,
409
+ display: true,
410
+ position: 'right' as const,
411
+ grid: {
412
+ drawOnChartArea: false
413
+ },
414
+ title: {
415
+ display: mixedBarLineDataProps.yRightAxisTitle !== null,
416
+ text: mixedBarLineDataProps.yRightAxisTitle
417
+ },
418
+ ticks: {
419
+ callback: lineTicksCallback,
420
+ }
421
+ }
422
+ }
423
+ }));
424
+
425
+ function barTicksCallback(val: string | number) {
426
+ return getTickValue(val, mixedBarLineDataProps.barUnit, mixedBarLineDataProps.barTicksMinimumFractionDigits, mixedBarLineDataProps.barTicksMaximumFractionDigits);
427
+ }
428
+
429
+ function lineTicksCallback(val: string | number) {
430
+ return getTickValue(val, mixedBarLineDataProps.lineUnit, mixedBarLineDataProps.lineTicksMinimumFractionDigits, mixedBarLineDataProps.lineTicksMaximumFractionDigits);
431
+ }
432
+
433
+ function getTickValue(val: string | number, typeUnit: string, minimumFractionDigits?: number, maximumFractionDigits?: number) {
434
+ return typeUnit === '%'
435
+ ? `${formatTicks(val as number, mixedBarLineDataProps.unit, minimumFractionDigits, maximumFractionDigits)} ${typeUnit}`
436
+ : `${formatWithThousandsSeprators(val as number, minimumFractionDigits, maximumFractionDigits)} ${typeUnit}`;
437
+ }
438
+
439
+ const htmlLegendPlugin = ref(
440
+ privateGetHtmlLegendPlugin(
441
+ legendContainer,
442
+ selectMode,
443
+ disablePattern,
444
+ patternsColors,
445
+ patternsOrderedList
446
+ )
447
+ );
448
+ </script>
449
+
450
+ <style lang="scss">
451
+ @import 'settings-tools/all-settings';
452
+ @import 'components/c.checkbox';
453
+ </style>
454
+
455
+ <style scoped>
456
+ .container {
457
+ -moz-osx-font-smoothing: grayscale;
458
+ -webkit-font-smoothing: antialiased;
459
+ font-weight: 400;
460
+ font-family: 'Roboto', sans-serif;
461
+ }
462
+ .main {
463
+ display: flex;
464
+ flex-direction: column;
465
+ justify-content: center;
466
+ align-items: center;
467
+ margin-bottom: 20px;
468
+ }
469
+ </style>
@@ -0,0 +1,8 @@
1
+ import type { App } from 'vue';
2
+ import MixedBarLineChart from './MixedBarLineChart.vue';
3
+
4
+ MixedBarLineChart.install = (app: App) => {
5
+ app.component('MixedBarLineChart', MixedBarLineChart);
6
+ };
7
+
8
+ export { MixedBarLineChart };