@orbcharts/plugins-basic 3.0.0-alpha.25 → 3.0.0-alpha.26

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,7 @@
1
- import { BubblesPluginParams, PiePluginParams, PieEventTextsPluginParams, PieLabelsPluginParams } from './types';
1
+ import { BubblesPluginParams, PiePluginParams, PieEventTextsPluginParams, PieLabelsPluginParams, SeriesLegendParams } from './types';
2
2
 
3
3
  export declare const DEFAULT_BUBBLES_PLUGIN_PARAMS: BubblesPluginParams;
4
4
  export declare const DEFAULT_PIE_PLUGIN_PARAMS: PiePluginParams;
5
5
  export declare const DEFAULT_PIE_EVENT_TEXTS_PARAMS: PieEventTextsPluginParams;
6
6
  export declare const DEFAULT_PIE_LABELS_PARAMS: PieLabelsPluginParams;
7
+ export declare const DEFAULT_SERIES_LEGEND_PARAMS: SeriesLegendParams;
@@ -4,3 +4,4 @@ export { Bubbles } from './plugins/Bubbles';
4
4
  export { Pie } from './plugins/Pie';
5
5
  export { PieEventTexts } from './plugins/PieEventTexts';
6
6
  export { PieLabels } from './plugins/PieLabels';
7
+ export { SeriesLegend } from './plugins/SeriesLegend';
@@ -0,0 +1,3 @@
1
+ import { SeriesLegendParams } from '../types';
2
+
3
+ export declare const SeriesLegend: import('@orbcharts/core').PluginConstructor<"series", string, SeriesLegendParams>;
@@ -43,3 +43,10 @@ export interface PieLabelsPluginParams {
43
43
  labelFn: ((d: ComputedDatumSeries) => string);
44
44
  labelColorType: ColorType;
45
45
  }
46
+ export interface SeriesLegendParams {
47
+ position: 'top' | 'bottom' | 'left' | 'right';
48
+ justify: 'start' | 'center' | 'end';
49
+ padding: number;
50
+ gap: number;
51
+ rectRadius: number;
52
+ }
@@ -2,6 +2,7 @@ import { AxisPosition, ColorType, ChartParams, ComputedDatumBase } from '@orbcha
2
2
 
3
3
  export declare function getMinAndMaxValue(data: ComputedDatumBase[]): [number, number];
4
4
  export declare function getColor(colorType: ColorType, fullChartParams: ChartParams): string;
5
+ export declare function getSeriesColor(seriesIndex: number, fullChartParams: ChartParams): string;
5
6
  export declare function getDatumColor({ datum, colorType, fullChartParams }: {
6
7
  datum: ComputedDatumBase;
7
8
  colorType: ColorType;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orbcharts/plugins-basic",
3
- "version": "3.0.0-alpha.25",
3
+ "version": "3.0.0-alpha.26",
4
4
  "description": "plugins for OrbCharts",
5
5
  "author": "Blue Planet Inc.",
6
6
  "license": "Apache-2.0",
@@ -31,10 +31,11 @@
31
31
  "@types/d3": "^7.4.0",
32
32
  "ts-loader": "^9.4.2",
33
33
  "typescript": "^5.0.4",
34
+ "vite": "^5.3.5",
34
35
  "vite-plugin-dts": "^3.7.3"
35
36
  },
36
37
  "dependencies": {
37
- "@orbcharts/core": "^3.0.0-alpha.21",
38
+ "@orbcharts/core": "^3.0.0-alpha.22",
38
39
  "d3": "^7.8.5",
39
40
  "rxjs": "^7.8.1"
40
41
  }
@@ -1,9 +1,10 @@
1
1
  import type { ComputedDatumSeries, EventSeries, EventName, ColorType } from '@orbcharts/core'
2
- import {
2
+ import type {
3
3
  BubblesPluginParams,
4
4
  PiePluginParams,
5
5
  PieEventTextsPluginParams,
6
- PieLabelsPluginParams } from './types'
6
+ PieLabelsPluginParams,
7
+ SeriesLegendParams } from './types'
7
8
 
8
9
  export const DEFAULT_BUBBLES_PLUGIN_PARAMS: BubblesPluginParams = {
9
10
  force: {
@@ -80,3 +81,13 @@ export const DEFAULT_PIE_LABELS_PARAMS: PieLabelsPluginParams = {
80
81
  labelColorType: 'series',
81
82
  labelFn: d => String(d.value),
82
83
  }
84
+
85
+ export const DEFAULT_SERIES_LEGEND_PARAMS: SeriesLegendParams = {
86
+ position: 'right',
87
+ justify: 'end',
88
+ padding: 28,
89
+ // offset: [0, 0],
90
+ gap: 10,
91
+ rectRadius: 0,
92
+ // highlightEvent: false
93
+ }
@@ -3,4 +3,5 @@ export * from './types'
3
3
  export { Bubbles } from './plugins/Bubbles'
4
4
  export { Pie } from './plugins/Pie'
5
5
  export { PieEventTexts } from './plugins/PieEventTexts'
6
- export { PieLabels } from './plugins/PieLabels'
6
+ export { PieLabels } from './plugins/PieLabels'
7
+ export { SeriesLegend } from './plugins/SeriesLegend'
@@ -506,8 +506,6 @@ export const Bubbles = defineSeriesPlugin('Bubbles', DEFAULT_BUBBLES_PLUGIN_PARA
506
506
  bubblesSelection$.next(bubblesSelection)
507
507
  })
508
508
 
509
- // const highlight$ = highlightObservable({ datumList$: computedData$, fullChartParams$, event$: store.event$ })
510
- const highlightSubscription = observer.seriesHighlight$.subscribe()
511
509
  combineLatest({
512
510
  bubblesSelection: bubblesSelection$,
513
511
  bubblesData: bubblesData$,
@@ -548,6 +546,5 @@ export const Bubbles = defineSeriesPlugin('Bubbles', DEFAULT_BUBBLES_PLUGIN_PARA
548
546
 
549
547
  return () => {
550
548
  destroy$.next(undefined)
551
- highlightSubscription.unsubscribe()
552
549
  }
553
550
  })
@@ -508,9 +508,6 @@ export const Pie = defineSeriesPlugin(pluginName, DEFAULT_PIE_PLUGIN_PARAMS)(({
508
508
  // arcMouseover: data.arcMouseover
509
509
  // })
510
510
  // })
511
-
512
- // const highlight$ = highlightObservable({ datumList$: computedData$, fullChartParams$, event$: store.event$ })
513
- const highlightSubscription = observer.seriesHighlight$.subscribe()
514
511
 
515
512
  combineLatest({
516
513
  pathSelection: pathSelection$,
@@ -598,6 +595,5 @@ export const Pie = defineSeriesPlugin(pluginName, DEFAULT_PIE_PLUGIN_PARAMS)(({
598
595
 
599
596
  return () => {
600
597
  destroy$.next(undefined)
601
- highlightSubscription.unsubscribe()
602
598
  }
603
599
  })
@@ -263,9 +263,6 @@ export const PieLabels = defineSeriesPlugin(pluginName, DEFAULT_PIE_LABELS_PARAM
263
263
  labelSelection$.next(labelSelection)
264
264
 
265
265
  })
266
-
267
- // const highlight$ = highlightObservable({ datumList$: computedData$, fullChartParams$, event$: store.event$ })
268
- const highlightSubscription = observer.seriesHighlight$.subscribe()
269
266
 
270
267
  combineLatest({
271
268
  labelSelection: labelSelection$,
@@ -284,6 +281,5 @@ export const PieLabels = defineSeriesPlugin(pluginName, DEFAULT_PIE_LABELS_PARAM
284
281
 
285
282
  return () => {
286
283
  destroy$.next(undefined)
287
- highlightSubscription.unsubscribe()
288
284
  }
289
285
  })
@@ -0,0 +1,457 @@
1
+ import * as d3 from 'd3'
2
+ import {
3
+ combineLatest,
4
+ map,
5
+ switchMap,
6
+ takeUntil,
7
+ Observable,
8
+ Subject } from 'rxjs'
9
+ import {
10
+ defineSeriesPlugin } from '@orbcharts/core'
11
+ import type {
12
+ ChartParams } from '@orbcharts/core'
13
+ import type { SeriesLegendParams } from '../types'
14
+ import { DEFAULT_SERIES_LEGEND_PARAMS } from '../defaults'
15
+ import { getSeriesColor, getClassName } from '../../utils/orbchartsUtils'
16
+ import { measureTextWidth } from '../../utils/commonUtils'
17
+
18
+
19
+ // 第1層 - 定位的容器(絕對位置)
20
+ interface Position {
21
+ x:number
22
+ y:number
23
+ }
24
+
25
+ // 第2層 - 圖例列表
26
+ interface LegendList {
27
+ direction: 'row' | 'column'
28
+ width: number
29
+ height: number
30
+ translateX:number
31
+ translateY:number
32
+ list: LegendItem[][]
33
+ }
34
+
35
+ // 第3層 - 圖例項目
36
+ interface LegendItem {
37
+ id: string // seriesLabel
38
+ seriesLabel: string
39
+ seriesIndex: number
40
+ lineIndex: number
41
+ itemIndex: number // 行內的item
42
+ text: string
43
+ itemWidth: number
44
+ translateX: number
45
+ translateY: number
46
+ color: string
47
+ // fontSize: number
48
+ // rectRadius: number
49
+ }
50
+
51
+ const pluginName = 'SeriesLegend'
52
+ const boxClassName = getClassName(pluginName, 'box')
53
+ const legendListClassName = getClassName(pluginName, 'legend-list')
54
+ const itemClassName = getClassName(pluginName, 'item')
55
+
56
+ function renderSeriesLegend ({ itemSelection, lengendList, seriesLabel, fullParams, fullChartParams }: {
57
+ itemSelection: d3.Selection<SVGGElement, LegendItem, any, any>
58
+ lengendList: LegendList
59
+ seriesLabel: string[]
60
+ fullParams: SeriesLegendParams
61
+ fullChartParams: ChartParams
62
+ }) {
63
+ itemSelection
64
+ .each((d, i, g) => {
65
+ // 方塊
66
+ d3.select(g[i])
67
+ .selectAll('rect')
68
+ .data([d])
69
+ .join('rect')
70
+ .attr('width', fullChartParams.styles.textSize)
71
+ .attr('height', fullChartParams.styles.textSize)
72
+ .attr('fill', _d => _d.color)
73
+ .attr('rx', fullParams.rectRadius)
74
+ // 文字
75
+ d3.select(g[i])
76
+ .selectAll('text')
77
+ .data([d])
78
+ .join(
79
+ enter => {
80
+ return enter
81
+ .append('text')
82
+ .attr('dominant-baseline', 'hanging')
83
+ },
84
+ update => {
85
+ return update
86
+ },
87
+ exit => exit.remove()
88
+ )
89
+ .attr('x', fullChartParams.styles.textSize * 1.5)
90
+ .attr('font-size', fullChartParams.styles.textSize)
91
+ .text(d => d.text)
92
+ })
93
+ }
94
+
95
+
96
+ export const SeriesLegend = defineSeriesPlugin(pluginName, DEFAULT_SERIES_LEGEND_PARAMS)(({ selection, rootSelection, observer, subject }) => {
97
+
98
+ const destroy$ = new Subject()
99
+
100
+ const seriesLabels$: Observable<string[]> = observer.SeriesDataMap$.pipe(
101
+ takeUntil(destroy$),
102
+ map(data => {
103
+ return Array.from(data.keys())
104
+ })
105
+ )
106
+
107
+ const lineDirection$ = observer.fullParams$.pipe(
108
+ takeUntil(destroy$),
109
+ map(data => {
110
+ return data.position === 'bottom' || data.position === 'top'
111
+ ? 'row'
112
+ : 'column'
113
+ })
114
+ )
115
+
116
+ const lineMaxSize$ = combineLatest({
117
+ fullParams: observer.fullParams$,
118
+ layout: observer.layout$
119
+ }).pipe(
120
+ takeUntil(destroy$),
121
+ map(data => {
122
+ return data.fullParams.position === 'bottom' || data.fullParams.position === 'top'
123
+ ? data.layout.rootWidth - 2 // 減2是避免完全貼到邊線上
124
+ : data.layout.rootHeight - 2
125
+ })
126
+ )
127
+
128
+ const boxPosition$ = combineLatest({
129
+ layout: observer.layout$,
130
+ fullParams: observer.fullParams$,
131
+ }).pipe(
132
+ takeUntil(destroy$),
133
+ switchMap(async d => d),
134
+ map(data => {
135
+ let x = 0
136
+ let y = 0
137
+ if (data.fullParams.position === 'bottom') {
138
+ y = data.layout.rootHeight
139
+ if (data.fullParams.justify === 'start') {
140
+ x = 0
141
+ } else if (data.fullParams.justify === 'center') {
142
+ x = data.layout.rootWidth / 2
143
+ } else if (data.fullParams.justify === 'end') {
144
+ x = data.layout.rootWidth
145
+ }
146
+ } else if (data.fullParams.position === 'right') {
147
+ x = data.layout.rootWidth
148
+ if (data.fullParams.justify === 'start') {
149
+ y = 0
150
+ } else if (data.fullParams.justify === 'center') {
151
+ y = data.layout.rootHeight / 2
152
+ } else if (data.fullParams.justify === 'end') {
153
+ y = data.layout.rootHeight
154
+ }
155
+ } else if (data.fullParams.position === 'top') {
156
+ y = 0
157
+ if (data.fullParams.justify === 'start') {
158
+ x = 0
159
+ } else if (data.fullParams.justify === 'center') {
160
+ x = data.layout.rootWidth / 2
161
+ } else if (data.fullParams.justify === 'end') {
162
+ x = data.layout.rootWidth
163
+ }
164
+ } else if (data.fullParams.position === 'left') {
165
+ x = 0
166
+ if (data.fullParams.justify === 'start') {
167
+ y = 0
168
+ } else if (data.fullParams.justify === 'center') {
169
+ y = data.layout.rootHeight / 2
170
+ } else if (data.fullParams.justify === 'end') {
171
+ y = data.layout.rootHeight
172
+ }
173
+ }
174
+
175
+ return {
176
+ x,
177
+ y
178
+ }
179
+ })
180
+ )
181
+
182
+ const boxSelection$: Observable<d3.Selection<SVGGElement, Position, any, any>> = boxPosition$.pipe(
183
+ takeUntil(destroy$),
184
+ map(data => {
185
+
186
+ return rootSelection
187
+ .selectAll<SVGGElement, Position>(`g.${boxClassName}`)
188
+ .data([data])
189
+ .join(
190
+ enter => {
191
+ return enter
192
+ .append('g')
193
+ .classed(boxClassName, true)
194
+ .attr('transform', d => `translate(${d.x}, ${d.y})`)
195
+ },
196
+ update => {
197
+ return update
198
+ .transition()
199
+ .attr('transform', d => `translate(${d.x}, ${d.y})`)
200
+ },
201
+ exit => exit.remove()
202
+ )
203
+ })
204
+ )
205
+
206
+ const lengendList$: Observable<LegendList> = combineLatest({
207
+ layout: observer.layout$,
208
+ fullParams: observer.fullParams$,
209
+ fullChartParams: observer.fullChartParams$,
210
+ seriesLabels: seriesLabels$,
211
+ lineDirection: lineDirection$,
212
+ lineMaxSize: lineMaxSize$
213
+ }).pipe(
214
+ takeUntil(destroy$),
215
+ switchMap(async d => d),
216
+ map(data => {
217
+ const list: LegendItem[][] = data.seriesLabels.reduce((prev: LegendItem[][], current, currentIndex) => {
218
+ const textWidth = measureTextWidth(current, data.fullChartParams.styles.textSize)
219
+ const itemWidth = (data.fullChartParams.styles.textSize * 1.5) + textWidth
220
+ const color = getSeriesColor(currentIndex, data.fullChartParams)
221
+ const lastItem: LegendItem | null = prev[0] && prev[0][0]
222
+ ? prev[prev.length - 1][prev[prev.length - 1].length - 1]
223
+ : null
224
+
225
+ const { translateX, translateY, lineIndex, itemIndex } = ((_data, _prev, _lastItem) => {
226
+ let translateX = 0
227
+ let translateY = 0
228
+ let lineIndex = 0
229
+ let itemIndex = 0
230
+
231
+ if (_data.lineDirection === 'column') {
232
+ let tempTranslateY = _lastItem
233
+ ? _lastItem.translateY + _data.fullChartParams.styles.textSize + _data.fullParams.gap
234
+ : 0
235
+
236
+ if ((tempTranslateY + _data.fullChartParams.styles.textSize) > _data.lineMaxSize) {
237
+ // 換行
238
+ lineIndex = _lastItem.lineIndex + 1
239
+ itemIndex = 0
240
+ translateY = 0
241
+ // 前一行最寬寬度
242
+ const maxItemWidthInLastLine = _prev[_prev.length - 1].reduce((p, c) => {
243
+ return c.itemWidth > p ? c.itemWidth : p
244
+ }, 0)
245
+ translateX = _lastItem.translateX + maxItemWidthInLastLine + _data.fullParams.gap
246
+ } else {
247
+ lineIndex = _lastItem ? _lastItem.lineIndex : 0
248
+ itemIndex = _lastItem ? _lastItem.itemIndex + 1 : 0
249
+ translateY = tempTranslateY
250
+ translateX = _lastItem ? _lastItem.translateX : 0
251
+ }
252
+ } else {
253
+ let tempTranslateX = _lastItem
254
+ ? _lastItem.translateX + _lastItem.itemWidth + _data.fullParams.gap
255
+ : 0
256
+ if ((tempTranslateX + itemWidth) > _data.lineMaxSize) {
257
+ // 換行
258
+ lineIndex = _lastItem.lineIndex + 1
259
+ itemIndex = 0
260
+ translateX = 0
261
+ } else {
262
+ lineIndex = _lastItem ? _lastItem.lineIndex : 0
263
+ itemIndex = _lastItem ? _lastItem.itemIndex + 1 : 0
264
+ translateX = tempTranslateX
265
+ }
266
+ translateY = (_data.fullChartParams.styles.textSize + _data.fullParams.gap) * lineIndex
267
+ }
268
+
269
+ return { translateX, translateY, lineIndex, itemIndex }
270
+ })(data, prev, lastItem)
271
+
272
+ if (!prev[lineIndex]) {
273
+ prev[lineIndex] = []
274
+ }
275
+
276
+ prev[lineIndex].push({
277
+ id: current,
278
+ seriesLabel: current,
279
+ seriesIndex: currentIndex,
280
+ lineIndex,
281
+ itemIndex,
282
+ text: current,
283
+ itemWidth,
284
+ translateX,
285
+ translateY,
286
+ color,
287
+ })
288
+
289
+ return prev
290
+ }, [])
291
+
292
+ // 依list計算出來的排序位置來計算整體的偏移位置
293
+ const { width, height, translateX, translateY } = ((_data, _list) => {
294
+ let width = 0
295
+ let height = 0
296
+ let translateX = 0
297
+ let translateY = 0
298
+
299
+ if (!_list.length || !_list[0].length) {
300
+ return { width, height, translateX, translateY }
301
+ }
302
+
303
+ const firstLineLastItem = _list[0][_list[0].length - 1]
304
+ if (_data.lineDirection === 'column') {
305
+ width = _list.reduce((p, c) => {
306
+ const maxWidthInLine = c.reduce((_p, _c) => {
307
+ // 找出最寬的寬度
308
+ return _c.itemWidth > _p ? _c.itemWidth : _p
309
+ }, 0)
310
+ // 每行寬度加總
311
+ return p + maxWidthInLine
312
+ }, 0)
313
+ height = firstLineLastItem.translateY + _data.fullChartParams.styles.textSize + _data.fullParams.gap
314
+ } else {
315
+ width = firstLineLastItem.translateX + firstLineLastItem.itemWidth
316
+ height = (_data.fullChartParams.styles.textSize * _list.length) + (_data.fullParams.gap * (_list.length - 1))
317
+ }
318
+
319
+ if (_data.fullParams.position === 'left') {
320
+ if (_data.fullParams.justify === 'start') {
321
+ translateX = _data.fullParams.padding
322
+ translateY = _data.fullParams.padding
323
+ } else if (_data.fullParams.justify === 'center') {
324
+ translateX = _data.fullParams.padding
325
+ translateY = - height / 2
326
+ } else if (_data.fullParams.justify === 'end') {
327
+ translateX = _data.fullParams.padding
328
+ translateY = - height - _data.fullParams.padding
329
+ }
330
+ } else if (_data.fullParams.position === 'right') {
331
+ if (_data.fullParams.justify === 'start') {
332
+ translateX = - width - _data.fullParams.padding
333
+ translateY = _data.fullParams.padding
334
+ } else if (_data.fullParams.justify === 'center') {
335
+ translateX = - width - _data.fullParams.padding
336
+ translateY = - height / 2
337
+ } else if (_data.fullParams.justify === 'end') {
338
+ translateX = - width - _data.fullParams.padding
339
+ translateY = - height - _data.fullParams.padding
340
+ }
341
+ } else if (_data.fullParams.position === 'top') {
342
+ if (_data.fullParams.justify === 'start') {
343
+ translateX = _data.fullParams.padding
344
+ translateY = _data.fullParams.padding
345
+ } else if (_data.fullParams.justify === 'center') {
346
+ translateX = - width / 2
347
+ translateY = _data.fullParams.padding
348
+ } else if (_data.fullParams.justify === 'end') {
349
+ translateX = - width - _data.fullParams.padding
350
+ translateY = _data.fullParams.padding
351
+ }
352
+ } else {
353
+ if (_data.fullParams.justify === 'start') {
354
+ translateX = _data.fullParams.padding
355
+ translateY = - height - _data.fullParams.padding
356
+ } else if (_data.fullParams.justify === 'center') {
357
+ translateX = - width / 2
358
+ translateY = - height - _data.fullParams.padding
359
+ } else if (_data.fullParams.justify === 'end') {
360
+ translateX = - width - _data.fullParams.padding
361
+ translateY = - height - _data.fullParams.padding
362
+ }
363
+ }
364
+
365
+ // translateX += _data.fullParams.offset[0]
366
+ // translateY += _data.fullParams.offset[1]
367
+
368
+ return { width, height, translateX, translateY }
369
+ })(data, list)
370
+
371
+ return {
372
+ direction: data.lineDirection,
373
+ width,
374
+ height,
375
+ translateX,
376
+ translateY,
377
+ list
378
+ }
379
+ })
380
+ )
381
+
382
+ const lengendListSelection$ = combineLatest({
383
+ boxSelection: boxSelection$,
384
+ fullParams: observer.fullParams$,
385
+ lengendList: lengendList$
386
+ }).pipe(
387
+ takeUntil(destroy$),
388
+ switchMap(async d => d),
389
+ map(data => {
390
+ return data.boxSelection
391
+ .selectAll<SVGGElement, SeriesLegendParams>('g')
392
+ .data([data.lengendList])
393
+ .join(
394
+ enter => {
395
+ return enter
396
+ .append('g')
397
+ .classed(legendListClassName, true)
398
+ .attr('transform', d => `translate(${d.translateX}, ${d.translateY})`)
399
+ },
400
+ update => {
401
+ return update
402
+ .transition()
403
+ .attr('transform', d => `translate(${d.translateX}, ${d.translateY})`)
404
+ },
405
+ exit => exit.remove()
406
+ )
407
+ })
408
+ )
409
+
410
+ const itemSelection$ = lengendListSelection$.pipe(
411
+ takeUntil(destroy$),
412
+ map(lengendListSelection => {
413
+ const legendListData = lengendListSelection.data()
414
+ const data = legendListData[0] ? legendListData[0].list.flat() : []
415
+
416
+ return lengendListSelection
417
+ .selectAll<SVGGElement, string>(`g.${itemClassName}`)
418
+ .data(data)
419
+ .join(
420
+ enter => {
421
+ return enter
422
+ .append('g')
423
+ .classed(itemClassName, true)
424
+ .attr('cursor', 'default')
425
+ },
426
+ update => update,
427
+ exit => exit.remove()
428
+ )
429
+ .attr('transform', (d, i) => {
430
+ return `translate(${d.translateX}, ${d.translateY})`
431
+ })
432
+ })
433
+ )
434
+
435
+ combineLatest({
436
+ itemSelection: itemSelection$,
437
+ lengendList: lengendList$,
438
+ seriesLabels: seriesLabels$,
439
+ fullParams: observer.fullParams$,
440
+ fullChartParams: observer.fullChartParams$
441
+ }).pipe(
442
+ takeUntil(destroy$),
443
+ switchMap(async d => d),
444
+ ).subscribe(data => {
445
+ renderSeriesLegend({
446
+ itemSelection: data.itemSelection,
447
+ lengendList: data.lengendList,
448
+ seriesLabel: data.seriesLabels,
449
+ fullParams: data.fullParams,
450
+ fullChartParams: data.fullChartParams
451
+ })
452
+ })
453
+
454
+ return () => {
455
+ destroy$.next(undefined)
456
+ }
457
+ })
@@ -51,3 +51,13 @@ export interface PieLabelsPluginParams {
51
51
  labelFn: ((d: ComputedDatumSeries) => string)
52
52
  labelColorType: ColorType
53
53
  }
54
+
55
+ export interface SeriesLegendParams {
56
+ position: 'top' | 'bottom' | 'left' | 'right'
57
+ justify: 'start' | 'center' | 'end'
58
+ padding: number
59
+ // offset: [number, number]
60
+ gap: number
61
+ rectRadius: number
62
+ // highlightEvent: boolean
63
+ }
@@ -9,7 +9,7 @@ export function getMinAndMaxValue (data: ComputedDatumBase[]): [number, number]
9
9
  return getMinAndMax(arr)
10
10
  }
11
11
 
12
-
12
+ // 取得colorType顏色
13
13
  export function getColor (colorType: ColorType, fullChartParams: ChartParams) {
14
14
  const colors = fullChartParams.colors[fullChartParams.colorScheme]
15
15
  // 對應series資料中第1個顏色
@@ -22,6 +22,15 @@ export function getColor (colorType: ColorType, fullChartParams: ChartParams) {
22
22
  : colors.primary
23
23
  }
24
24
 
25
+ // 取得Series顏色
26
+ export function getSeriesColor (seriesIndex: number, fullChartParams: ChartParams) {
27
+ const colorIndex = seriesIndex < fullChartParams.colors[fullChartParams.colorScheme].series.length
28
+ ? seriesIndex
29
+ : seriesIndex % fullChartParams.colors[fullChartParams.colorScheme].series.length
30
+ return fullChartParams.colors[fullChartParams.colorScheme].series[colorIndex]
31
+ }
32
+
33
+ // 取得Datum顏色
25
34
  export function getDatumColor ({ datum, colorType, fullChartParams }: { datum: ComputedDatumBase, colorType: ColorType, fullChartParams: ChartParams }) {
26
35
  // 對應series資料中的顏色
27
36
  if (colorType === 'series') {