@operato/chart 2.0.0-beta.25

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 (114) hide show
  1. package/.editorconfig +29 -0
  2. package/.storybook/main.js +3 -0
  3. package/.storybook/preview.js +29 -0
  4. package/.storybook/server.mjs +8 -0
  5. package/CHANGELOG.md +17 -0
  6. package/README.md +0 -0
  7. package/assets/images/icon-editor-gradient-direction.png +0 -0
  8. package/assets/images/icon-properties-label.png +0 -0
  9. package/assets/images/icon-properties-line-type.png +0 -0
  10. package/assets/images/icon-properties-table.png +0 -0
  11. package/assets/images/no-image.png +0 -0
  12. package/demo/index.html +33 -0
  13. package/dist/src/chartjs/config-converter.d.ts +29 -0
  14. package/dist/src/chartjs/config-converter.js +218 -0
  15. package/dist/src/chartjs/config-converter.js.map +1 -0
  16. package/dist/src/chartjs/ox-chart-js.d.ts +17 -0
  17. package/dist/src/chartjs/ox-chart-js.js +46 -0
  18. package/dist/src/chartjs/ox-chart-js.js.map +1 -0
  19. package/dist/src/editors/configurer.d.ts +52 -0
  20. package/dist/src/editors/configurer.js +198 -0
  21. package/dist/src/editors/configurer.js.map +1 -0
  22. package/dist/src/editors/index.d.ts +6 -0
  23. package/dist/src/editors/index.js +8 -0
  24. package/dist/src/editors/index.js.map +1 -0
  25. package/dist/src/editors/input-chart-abstract.d.ts +27 -0
  26. package/dist/src/editors/input-chart-abstract.js +273 -0
  27. package/dist/src/editors/input-chart-abstract.js.map +1 -0
  28. package/dist/src/editors/input-chart-multi-series-abstract.d.ts +17 -0
  29. package/dist/src/editors/input-chart-multi-series-abstract.js +418 -0
  30. package/dist/src/editors/input-chart-multi-series-abstract.js.map +1 -0
  31. package/dist/src/editors/input-chart-styles.d.ts +1 -0
  32. package/dist/src/editors/input-chart-styles.js +167 -0
  33. package/dist/src/editors/input-chart-styles.js.map +1 -0
  34. package/dist/src/editors/ox-input-chart-hbar.d.ts +11 -0
  35. package/dist/src/editors/ox-input-chart-hbar.js +220 -0
  36. package/dist/src/editors/ox-input-chart-hbar.js.map +1 -0
  37. package/dist/src/editors/ox-input-chart-mixed.d.ts +8 -0
  38. package/dist/src/editors/ox-input-chart-mixed.js +237 -0
  39. package/dist/src/editors/ox-input-chart-mixed.js.map +1 -0
  40. package/dist/src/editors/ox-input-chart-pie.d.ts +11 -0
  41. package/dist/src/editors/ox-input-chart-pie.js +67 -0
  42. package/dist/src/editors/ox-input-chart-pie.js.map +1 -0
  43. package/dist/src/editors/ox-input-chart-radar.d.ts +8 -0
  44. package/dist/src/editors/ox-input-chart-radar.js +52 -0
  45. package/dist/src/editors/ox-input-chart-radar.js.map +1 -0
  46. package/dist/src/editors/ox-property-editor-chart.d.ts +10 -0
  47. package/dist/src/editors/ox-property-editor-chart.js +79 -0
  48. package/dist/src/editors/ox-property-editor-chart.js.map +1 -0
  49. package/dist/src/index.d.ts +0 -0
  50. package/dist/src/index.js +2 -0
  51. package/dist/src/index.js.map +1 -0
  52. package/dist/src/scichart/ox-scichart.d.ts +35 -0
  53. package/dist/src/scichart/ox-scichart.js +122 -0
  54. package/dist/src/scichart/ox-scichart.js.map +1 -0
  55. package/dist/src/scichart/scichart-builder.d.ts +8 -0
  56. package/dist/src/scichart/scichart-builder.js +46 -0
  57. package/dist/src/scichart/scichart-builder.js.map +1 -0
  58. package/dist/stories/common.d.ts +2 -0
  59. package/dist/stories/common.js +44 -0
  60. package/dist/stories/common.js.map +1 -0
  61. package/dist/stories/ox-input-chart-hbar.stories.d.ts +25 -0
  62. package/dist/stories/ox-input-chart-hbar.stories.js +171 -0
  63. package/dist/stories/ox-input-chart-hbar.stories.js.map +1 -0
  64. package/dist/stories/ox-input-chart-mixed.stories.d.ts +25 -0
  65. package/dist/stories/ox-input-chart-mixed.stories.js +180 -0
  66. package/dist/stories/ox-input-chart-mixed.stories.js.map +1 -0
  67. package/dist/stories/ox-input-chart-pie.stories.d.ts +27 -0
  68. package/dist/stories/ox-input-chart-pie.stories.js +111 -0
  69. package/dist/stories/ox-input-chart-pie.stories.js.map +1 -0
  70. package/dist/stories/ox-input-chart-radar.stories.d.ts +27 -0
  71. package/dist/stories/ox-input-chart-radar.stories.js +122 -0
  72. package/dist/stories/ox-input-chart-radar.stories.js.map +1 -0
  73. package/dist/tsconfig.tsbuildinfo +1 -0
  74. package/package.json +104 -0
  75. package/src/chartjs/config-converter.ts +307 -0
  76. package/src/chartjs/ox-chart-js.ts +46 -0
  77. package/src/editors/configurer.ts +243 -0
  78. package/src/editors/index.ts +8 -0
  79. package/src/editors/input-chart-abstract.ts +315 -0
  80. package/src/editors/input-chart-multi-series-abstract.ts +429 -0
  81. package/src/editors/input-chart-styles.ts +167 -0
  82. package/src/editors/ox-input-chart-hbar.ts +226 -0
  83. package/src/editors/ox-input-chart-mixed.ts +240 -0
  84. package/src/editors/ox-input-chart-pie.ts +75 -0
  85. package/src/editors/ox-input-chart-radar.ts +54 -0
  86. package/src/editors/ox-property-editor-chart.ts +79 -0
  87. package/src/globat.d.ts +1 -0
  88. package/src/index.ts +0 -0
  89. package/src/scichart/ox-scichart.ts +143 -0
  90. package/src/scichart/scichart-builder.ts +87 -0
  91. package/src/types.d.ts +74 -0
  92. package/stories/common.ts +47 -0
  93. package/stories/ox-input-chart-hbar.stories.ts +185 -0
  94. package/stories/ox-input-chart-mixed.stories.ts +194 -0
  95. package/stories/ox-input-chart-pie.stories.ts +126 -0
  96. package/stories/ox-input-chart-radar.stories.ts +137 -0
  97. package/themes/dark-hc.css +151 -0
  98. package/themes/dark-mc.css +151 -0
  99. package/themes/dark.css +151 -0
  100. package/themes/grist-theme.css +177 -0
  101. package/themes/light-hc.css +151 -0
  102. package/themes/light-mc.css +151 -0
  103. package/themes/light.css +151 -0
  104. package/themes/md-typescale-styles.css +100 -0
  105. package/themes/spacing.css +43 -0
  106. package/themes/state-color.css +6 -0
  107. package/translations/en.json +44 -0
  108. package/translations/ja.json +44 -0
  109. package/translations/ko.json +44 -0
  110. package/translations/ms.json +8 -0
  111. package/translations/zh.json +44 -0
  112. package/tsconfig.json +24 -0
  113. package/web-dev-server.config.mjs +27 -0
  114. package/web-test-runner.config.mjs +41 -0
@@ -0,0 +1,143 @@
1
+ import { LitElement, html, css } from 'lit'
2
+ import { property, query, customElement } from 'lit/decorators.js'
3
+ import { buildSciChart } from './scichart-builder'
4
+
5
+ // 전역 변수로 SciChart 로딩 상태 관리
6
+ declare global {
7
+ interface Window {
8
+ sciChartLoaded: boolean
9
+ sciChartLoadingPromise: Promise<void>
10
+ }
11
+ }
12
+
13
+ @customElement('ox-scichart')
14
+ class OxSciChart extends LitElement {
15
+ @property({ type: Array }) data: { [attr: string]: any }[] = [
16
+ { x: 1718612376646, y: 10 },
17
+ { x: 1718612386646, y: 20 },
18
+ { x: 1718612396646, y: 15 },
19
+ { x: 1718612406646, y: 25 },
20
+ { x: 1718612416646, y: 22 },
21
+ { x: 1718612426646, y: 30 },
22
+ { x: 1718612436646, y: 28 }
23
+ ]
24
+ @property({ type: String }) attrX: string = 'x'
25
+ @property({ type: String }) attrY: string = 'y'
26
+ @property({ type: Object }) config: any = null
27
+
28
+ private chart: any = null
29
+ dataSeries?: any
30
+
31
+ @query('div#container') container!: HTMLDivElement
32
+
33
+ static styles = css`
34
+ :host {
35
+ display: block;
36
+ }
37
+
38
+ div {
39
+ width: 100%;
40
+ height: 100%;
41
+ }
42
+ `
43
+
44
+ firstUpdated() {
45
+ this.loadSciChart()
46
+ }
47
+
48
+ async loadSciChart() {
49
+ if (!window.sciChartLoaded) {
50
+ if (!window.sciChartLoadingPromise) {
51
+ window.sciChartLoadingPromise = new Promise<void>(resolve => {
52
+ const script = document.createElement('script')
53
+ script.src = 'https://cdn.jsdelivr.net/npm/scichart/index.min.js'
54
+ script.crossOrigin = 'anonymous'
55
+ script.onload = () => {
56
+ window.sciChartLoaded = true
57
+
58
+ SciChart.SciChartSurface.UseCommunityLicense()
59
+
60
+ resolve()
61
+ }
62
+ document.head.appendChild(script)
63
+ })
64
+ }
65
+
66
+ await window.sciChartLoadingPromise
67
+ }
68
+
69
+ this.initializeSciChart()
70
+ }
71
+
72
+ async initializeSciChart() {
73
+ const { chart, dataSeries } = (await buildSciChart(this.config, this.container, {})) || {}
74
+
75
+ this.chart = chart
76
+ this.dataSeries = dataSeries
77
+
78
+ this.updateDataSeries()
79
+ }
80
+
81
+ async updated(changedProperties: Map<string | number | symbol, unknown>) {
82
+ if (changedProperties.has('config')) {
83
+ await this.initializeSciChart()
84
+ }
85
+
86
+ if (changedProperties.has('data')) {
87
+ this.updateDataSeries()
88
+ this.chart?.sciChartSurface.zoomExtents()
89
+ }
90
+ }
91
+
92
+ updateDataSeries() {
93
+ if (!this.dataSeries) return
94
+
95
+ this.dataSeries.clear()
96
+ const newData = this.dataSet
97
+
98
+ this.dataSeries.appendRange(
99
+ newData.map(d => d.xValue),
100
+ newData.map(d => d.yValue)
101
+ )
102
+
103
+ this.chart?.sciChartSurface.zoomExtents()
104
+ this.chart?.sciChartSurface.invalidateElement()
105
+ }
106
+
107
+ get dataSet(): { xValue: number; yValue: number }[] {
108
+ const { attrX, attrY, data } = this
109
+
110
+ if (!(data instanceof Array)) {
111
+ return []
112
+ }
113
+
114
+ return data
115
+ .map(item => {
116
+ if (!item || typeof item !== 'object') {
117
+ return
118
+ }
119
+
120
+ const xValue = new Date(item[attrX])
121
+ if (isNaN(xValue.getTime())) {
122
+ console.error('Invalid date:', item[attrX])
123
+ return
124
+ }
125
+
126
+ return {
127
+ xValue: xValue.getTime() / 1000,
128
+ yValue: item[attrY]
129
+ }
130
+ })
131
+ .filter(Boolean) as { xValue: number; yValue: number }[]
132
+ }
133
+
134
+ render() {
135
+ return html`<div id="container"></div>`
136
+ }
137
+ }
138
+
139
+ declare global {
140
+ interface HTMLElementTagNameMap {
141
+ 'ox-scichart': OxSciChart
142
+ }
143
+ }
@@ -0,0 +1,87 @@
1
+ export async function buildSciChart(
2
+ config: OperatoChart.ChartConfig | undefined | null,
3
+ container: any,
4
+ { fontSize, fontFamily, fontColor }: { fontSize?: number; fontFamily?: string; fontColor?: string }
5
+ ): Promise<{ chart: any; dataSeries: any } | undefined> {
6
+ if (!config) {
7
+ return
8
+ }
9
+
10
+ const {
11
+ SciChartSurface,
12
+ SciChartDefaults,
13
+ chartBuilder,
14
+ SciChartJsNavyTheme,
15
+ XyDataSeries,
16
+ FastLineRenderableSeries,
17
+ NumberRange,
18
+ EAutoRange,
19
+ EAxisAlignment,
20
+ NumericAxis,
21
+ DateTimeNumericAxis
22
+ } = SciChart
23
+
24
+ const { type: chartType, options, data: fromData } = config
25
+ const {
26
+ theme,
27
+ animation,
28
+ tooltip,
29
+ stacked,
30
+ legend,
31
+ scales: fromScales,
32
+ xGridLine,
33
+ yGridLine,
34
+ y2ndGridLine,
35
+ multiAxis
36
+ } = options || {}
37
+ const { datasets = [] } = fromData || {}
38
+
39
+ const { xAxes = [], yAxes = [] } = fromScales || {}
40
+ const toScales = {} as any
41
+
42
+ const chart = await SciChartSurface.create(container, {
43
+ theme: new SciChartJsNavyTheme()
44
+ })
45
+ const { sciChartSurface, wasmContext } = chart
46
+
47
+ xAxes.forEach((axis, index) => {
48
+ const { axisTitle, barSpacing, categorySpacing, barPercentage, ticks } = axis
49
+ const { autoMax, autoMin } = ticks || {}
50
+ const id = xAxes.length > 1 ? `x${index + 1}` : 'x'
51
+
52
+ const xAxis = new DateTimeNumericAxis(wasmContext, {
53
+ axisTitle,
54
+ autoRange: EAutoRange.Always,
55
+ axisAlignment: EAxisAlignment.Bottom
56
+
57
+ // visibleRange: new NumberRange(minDate.getTime() / 1000, maxDate.getTime() / 1000)
58
+ })
59
+
60
+ sciChartSurface.xAxes.add(xAxis)
61
+ })
62
+
63
+ yAxes.forEach((axis, index) => {
64
+ const { axisTitle, barSpacing, categorySpacing, barPercentage, ticks } = axis
65
+ const { autoMax, autoMin } = ticks || {}
66
+
67
+ const id = yAxes.length > 1 ? `right` : 'left'
68
+
69
+ const yAxis = new NumericAxis(wasmContext, {
70
+ axisTitle,
71
+ autoRange: EAutoRange.Always,
72
+ axisAlignment: yAxes.length > 1 ? EAxisAlignment.Right : EAxisAlignment.Left
73
+ })
74
+ sciChartSurface.yAxes.add(yAxis)
75
+ })
76
+
77
+ const dataSeries = new XyDataSeries(wasmContext)
78
+
79
+ const lineSeries = new FastLineRenderableSeries(wasmContext)
80
+ lineSeries.strokeThickness = 2
81
+ lineSeries.stroke = '#FF6600'
82
+ lineSeries.dataSeries = dataSeries
83
+
84
+ sciChartSurface.renderableSeries.add(lineSeries)
85
+
86
+ return { chart, dataSeries }
87
+ }
package/src/types.d.ts ADDED
@@ -0,0 +1,74 @@
1
+ declare namespace OperatoChart {
2
+ export interface ChartConfig {
3
+ data: ChartData
4
+ options?: ChartOptions
5
+ type?: string
6
+ }
7
+
8
+ export interface ChartData {
9
+ labels?: Array<string | string[]>
10
+ datasets: Dataset[]
11
+ labelDataKey?: string
12
+ }
13
+
14
+ export interface Dataset {
15
+ label?: string
16
+ data?: any[]
17
+ borderWidth?: number
18
+ dataKey?: string
19
+ yAxisID?: string
20
+ color?: string | string[]
21
+ type?: 'bar' | 'horizontalBar' | 'line' | 'radar' | 'pie' | 'mixed'
22
+ backgroundColor?: string | string[]
23
+ stack?: string
24
+ fill?: boolean
25
+ lineTension?: number
26
+ pointStyle?: string
27
+ pointRadius?: number
28
+ valuePrefix?: string
29
+ valueSuffix?: string
30
+ displayValue?: boolean
31
+ [key: string]: any
32
+ }
33
+
34
+ export interface ChartOptions {
35
+ theme?: 'dark' | 'light'
36
+ tooltip?: boolean
37
+ animation?: boolean
38
+ legend?: LegendOptions
39
+ scales?: ScalesOptions
40
+ stacked?: boolean
41
+ xGridLine?: boolean
42
+ yGridLine?: boolean
43
+ y2ndGridLine?: boolean
44
+ multiAxis?: boolean
45
+ }
46
+
47
+ export interface LegendOptions {
48
+ display?: boolean
49
+ position?: 'top' | 'right' | 'bottom' | 'left'
50
+ }
51
+
52
+ export interface ScalesOptions {
53
+ xAxes?: AxisOptions[]
54
+ yAxes?: AxisOptions[]
55
+ }
56
+
57
+ export interface AxisOptions {
58
+ axisTitle?: string
59
+ barSpacing?: number
60
+ categorySpacing?: number
61
+ barPercentage?: number
62
+ ticks?: TickOptions
63
+ }
64
+
65
+ export interface TickOptions {
66
+ display?: boolean
67
+ autoMin?: boolean
68
+ autoMax?: boolean
69
+ min?: number
70
+ max?: number
71
+ stepSize?: number
72
+ beginAtZero?: boolean
73
+ }
74
+ }
@@ -0,0 +1,47 @@
1
+ export function getDefaultChartConfig(
2
+ type: 'bar' | 'line' | 'pie' | 'radar' | 'mixed',
3
+ datasets?: OperatoChart.Dataset[]
4
+ ): OperatoChart.ChartConfig {
5
+ return {
6
+ data: {
7
+ datasets: datasets || [],
8
+ labelDataKey: ''
9
+ },
10
+ options: {
11
+ theme: 'light',
12
+ tooltip: true,
13
+ animation: true,
14
+ legend: {
15
+ display: true,
16
+ position: 'top'
17
+ },
18
+ scales: {
19
+ xAxes: [getDefaultAxisOptions()],
20
+ yAxes: [getDefaultAxisOptions(), getDefaultAxisOptions()] // Two y-axes for multi-axis support
21
+ },
22
+ stacked: false,
23
+ xGridLine: true,
24
+ yGridLine: true,
25
+ y2ndGridLine: false,
26
+ multiAxis: false
27
+ },
28
+ type
29
+ }
30
+ }
31
+
32
+ export function getDefaultAxisOptions(): OperatoChart.AxisOptions {
33
+ return {
34
+ axisTitle: '',
35
+ barSpacing: 0,
36
+ categorySpacing: 0,
37
+ barPercentage: 0.9,
38
+ ticks: {
39
+ display: true,
40
+ autoMin: true,
41
+ autoMax: true,
42
+ min: undefined,
43
+ max: undefined,
44
+ stepSize: undefined
45
+ }
46
+ }
47
+ }
@@ -0,0 +1,185 @@
1
+ import { html, TemplateResult } from 'lit'
2
+
3
+ import '../src/editors/ox-input-chart-hbar.js'
4
+ import '../src/chartjs/ox-chart-js.js'
5
+ import '../src/scichart/ox-scichart.js'
6
+ import { convertConfigure } from '../src/chartjs/config-converter.js'
7
+
8
+ import { getDefaultChartConfig } from './common.js'
9
+
10
+ export default {
11
+ title: 'ox-input-chart-hbar',
12
+ component: 'ox-input-chart-hbar',
13
+ argTypes: {
14
+ value: { control: 'object' }
15
+ }
16
+ }
17
+
18
+ interface Story<T> {
19
+ (args: T): TemplateResult
20
+ args?: Partial<T>
21
+ argTypes?: Record<string, unknown>
22
+ }
23
+
24
+ interface ArgTypes {
25
+ value: OperatoChart.ChartConfig
26
+ }
27
+
28
+ const Template: Story<ArgTypes> = ({ value }: ArgTypes) => html`
29
+ <link
30
+ href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL@20..48,100..700,0..1"
31
+ rel="stylesheet"
32
+ />
33
+ <link
34
+ href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL@20..48,100..700,0..1"
35
+ rel="stylesheet"
36
+ />
37
+ <link
38
+ href="https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp:opsz,wght,FILL@20..48,100..700,0..1"
39
+ rel="stylesheet"
40
+ />
41
+
42
+ <link href="/themes/light.css" rel="stylesheet" />
43
+ <link href="/themes/dark.css" rel="stylesheet" />
44
+ <link href="/themes/spacing.css" rel="stylesheet" />
45
+
46
+ <style>
47
+ .container {
48
+ width: 100%;
49
+ height: 100%;
50
+
51
+ display: flex;
52
+ flex-direction: row;
53
+ overflow-y: auto;
54
+ padding: var(--spacing-medium);
55
+ }
56
+
57
+ .container.dark {
58
+ background-color: black;
59
+ }
60
+
61
+ #editor {
62
+ width: 300px;
63
+ }
64
+
65
+ #charts {
66
+ flex: 1;
67
+
68
+ display: flex;
69
+ flex-direction: column;
70
+ padding: 10px;
71
+ }
72
+
73
+ #chartjs {
74
+ flex: 1;
75
+ }
76
+
77
+ #scichart {
78
+ flex: 1;
79
+ }
80
+ </style>
81
+
82
+ <div class="container light">
83
+ <ox-input-chart-hbar
84
+ id="editor"
85
+ .value=${value}
86
+ @change=${async (e: CustomEvent) => {
87
+ const target = e.currentTarget as any
88
+ const config = target.value
89
+ const chartjs = document.querySelector('#chartjs') as HTMLElement
90
+ if (chartjs) {
91
+ ;(chartjs as any).config = convertConfigure(config, {})
92
+ }
93
+ const scichart = document.querySelector('#scichart') as HTMLElement
94
+ if (scichart) {
95
+ ;(scichart as any).config = config
96
+ }
97
+
98
+ const container = document.querySelector('.container') as HTMLElement
99
+ if (container) {
100
+ const theme = config.options.theme
101
+ if (theme == 'dark') {
102
+ container.classList.remove('light')
103
+ container.classList.add('dark')
104
+ } else {
105
+ container.classList.remove('dark')
106
+ container.classList.add('light')
107
+ }
108
+ }
109
+ }}
110
+ ></ox-input-chart-hbar>
111
+ <div id="charts">
112
+ <ox-chart-js id="chartjs"></ox-chart-js>
113
+ <ox-scichart id="scichart"></ox-scichart>
114
+ </div>
115
+ </div>
116
+ `
117
+
118
+ export const Default = Template.bind({})
119
+ Default.args = {}
120
+
121
+ // 다양한 데이터셋과 함께 사용하는 스토리
122
+ export const WithData = Template.bind({})
123
+ WithData.args = {
124
+ value: {
125
+ ...getDefaultChartConfig('bar'),
126
+ data: {
127
+ datasets: [
128
+ {
129
+ label: 'Series 1',
130
+ data: [10, 20, 30],
131
+ backgroundColor: 'rgba(255, 99, 132, 0.2)',
132
+ borderColor: 'rgba(255, 99, 132, 1)',
133
+ borderWidth: 1
134
+ },
135
+ {
136
+ label: 'Series 2',
137
+ data: [15, 25, 35],
138
+ backgroundColor: 'rgba(54, 162, 235, 0.2)',
139
+ borderColor: 'rgba(54, 162, 235, 1)',
140
+ borderWidth: 1
141
+ }
142
+ ],
143
+ labelDataKey: 'labels'
144
+ },
145
+ options: {
146
+ scales: {
147
+ xAxes: [{ ticks: { beginAtZero: true } }],
148
+ yAxes: [{ ticks: { beginAtZero: true } }]
149
+ },
150
+ legend: { display: true }
151
+ }
152
+ }
153
+ }
154
+
155
+ export const MultiAxis = Template.bind({})
156
+ MultiAxis.args = {
157
+ value: {
158
+ ...getDefaultChartConfig('bar'),
159
+ options: {
160
+ ...getDefaultChartConfig('bar').options,
161
+ multiAxis: true
162
+ },
163
+ data: {
164
+ datasets: [
165
+ {
166
+ label: 'Series 1',
167
+ data: [10, 20, 30],
168
+ backgroundColor: 'rgba(255, 99, 132, 0.2)',
169
+ borderColor: 'rgba(255, 99, 132, 1)',
170
+ borderWidth: 1,
171
+ yAxisID: 'left'
172
+ },
173
+ {
174
+ label: 'Series 2',
175
+ data: [15, 25, 35],
176
+ backgroundColor: 'rgba(54, 162, 235, 0.2)',
177
+ borderColor: 'rgba(54, 162, 235, 1)',
178
+ borderWidth: 1,
179
+ yAxisID: 'right'
180
+ }
181
+ ],
182
+ labelDataKey: 'labels'
183
+ }
184
+ }
185
+ }