@orbcharts/plugins-basic 3.0.0-alpha.25 → 3.0.0-alpha.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/orbcharts-plugins-basic.es.js +9883 -10268
- package/dist/orbcharts-plugins-basic.umd.js +11 -11
- package/dist/src/series/defaults.d.ts +2 -1
- package/dist/src/series/index.d.ts +1 -0
- package/dist/src/series/plugins/SeriesLegend.d.ts +3 -0
- package/dist/src/series/types.d.ts +7 -0
- package/dist/src/utils/orbchartsUtils.d.ts +1 -0
- package/package.json +3 -2
- package/src/series/defaults.ts +13 -2
- package/src/series/index.ts +2 -1
- package/src/series/plugins/Bubbles.ts +0 -3
- package/src/series/plugins/Pie.ts +0 -4
- package/src/series/plugins/PieLabels.ts +0 -4
- package/src/series/plugins/SeriesLegend.ts +457 -0
- package/src/series/types.ts +10 -0
- package/src/utils/orbchartsUtils.ts +10 -1
@@ -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;
|
@@ -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.
|
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.
|
38
|
+
"@orbcharts/core": "^3.0.0-alpha.22",
|
38
39
|
"d3": "^7.8.5",
|
39
40
|
"rxjs": "^7.8.1"
|
40
41
|
}
|
package/src/series/defaults.ts
CHANGED
@@ -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
|
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
|
+
}
|
package/src/series/index.ts
CHANGED
@@ -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
|
+
})
|
package/src/series/types.ts
CHANGED
@@ -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') {
|