@orbcharts/plugins-basic 3.0.0-alpha.26 → 3.0.0-alpha.28
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 +5882 -5805
- package/dist/orbcharts-plugins-basic.umd.js +6 -6
- package/dist/src/base/BaseLegend.d.ts +15 -0
- package/dist/src/base/types.d.ts +3 -0
- package/dist/src/grid/defaults.d.ts +3 -2
- package/dist/src/grid/index.d.ts +1 -0
- package/dist/src/grid/plugins/GridLegend.d.ts +1 -0
- package/dist/src/grid/plugins/GroupAxis.d.ts +2 -2
- package/dist/src/grid/types.d.ts +4 -1
- package/dist/src/series/plugins/SeriesLegend.d.ts +1 -3
- package/dist/src/series/types.d.ts +4 -8
- package/package.json +2 -2
- package/src/base/BaseLegend.ts +543 -0
- package/src/base/types.ts +3 -0
- package/src/grid/defaults.ts +19 -4
- package/src/grid/index.ts +1 -0
- package/src/grid/plugins/GridLegend.ts +40 -0
- package/src/grid/plugins/GroupAxis.ts +5 -5
- package/src/grid/types.ts +3 -1
- package/src/series/defaults.ts +7 -2
- package/src/series/plugins/Bubbles.ts +3 -3
- package/src/series/plugins/SeriesLegend.ts +9 -426
- package/src/series/types.ts +4 -11
- package/src/utils/orbchartsUtils.ts +13 -6
@@ -0,0 +1,15 @@
|
|
1
|
+
import { BasePluginFn } from './types';
|
2
|
+
import { ColorType } from '@orbcharts/core';
|
3
|
+
|
4
|
+
export interface BaseLegendParams {
|
5
|
+
position: 'top' | 'bottom' | 'left' | 'right';
|
6
|
+
justify: 'start' | 'center' | 'end';
|
7
|
+
padding: number;
|
8
|
+
backgroundFill: ColorType;
|
9
|
+
backgroundStroke: ColorType;
|
10
|
+
gap: number;
|
11
|
+
listRectWidth: number;
|
12
|
+
listRectHeight: number;
|
13
|
+
listRectRadius: number;
|
14
|
+
}
|
15
|
+
export declare const createBaseLegend: BasePluginFn;
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { LinesPluginParams, GroupAreaPluginParams, DotsPluginParams, BarsPluginParams, BarStackPluginParams, BarsTrianglePluginParams,
|
1
|
+
import { LinesPluginParams, GroupAreaPluginParams, DotsPluginParams, BarsPluginParams, BarStackPluginParams, BarsTrianglePluginParams, GroupAxisParams, ValueAxisParams, ValueStackAxisParams, ScalingAreaParams, GridLegendParams } from './types';
|
2
2
|
|
3
3
|
export declare const DEFAULT_LINES_PLUGIN_PARAMS: LinesPluginParams;
|
4
4
|
export declare const DEFAULT_DOTS_PLUGIN_PARAMS: DotsPluginParams;
|
@@ -6,7 +6,8 @@ export declare const DEFAULT_GROUP_AREA_PLUGIN_PARAMS: GroupAreaPluginParams;
|
|
6
6
|
export declare const DEFAULT_BARS_PLUGIN_PARAMS: BarsPluginParams;
|
7
7
|
export declare const DEFAULT_BAR_STACK_PLUGIN_PARAMS: BarStackPluginParams;
|
8
8
|
export declare const DEFAULT_BARS_TRIANGLE_PLUGIN_PARAMS: BarsTrianglePluginParams;
|
9
|
-
export declare const DEFAULT_GROUPING_AXIS_PLUGIN_PARAMS:
|
9
|
+
export declare const DEFAULT_GROUPING_AXIS_PLUGIN_PARAMS: GroupAxisParams;
|
10
10
|
export declare const DEFAULT_VALUE_AXIS_PLUGIN_PARAMS: ValueAxisParams;
|
11
11
|
export declare const DEFAULT_VALUE_STACK_AXIS_PLUGIN_PARAMS: ValueStackAxisParams;
|
12
12
|
export declare const DEFAULT_SCALING_AREA_PLUGIN_PARAMS: ScalingAreaParams;
|
13
|
+
export declare const DEFAULT_GRID_LEGEND_PARAMS: GridLegendParams;
|
package/dist/src/grid/index.d.ts
CHANGED
@@ -5,6 +5,7 @@ export { Bars } from './plugins/Bars';
|
|
5
5
|
export { BarStack } from './plugins/BarStack';
|
6
6
|
export { BarsTriangle } from './plugins/BarsTriangle';
|
7
7
|
export { Dots } from './plugins/Dots';
|
8
|
+
export { GridLegend } from './plugins/GridLegend';
|
8
9
|
export { GroupAxis } from './plugins/GroupAxis';
|
9
10
|
export { ValueAxis } from './plugins/ValueAxis';
|
10
11
|
export { ValueStackAxis } from './plugins/ValueStackAxis';
|
@@ -0,0 +1 @@
|
|
1
|
+
export declare const GridLegend: import('@orbcharts/core').PluginConstructor<"grid", string, import('..').GridLegendParams>;
|
@@ -1,3 +1,3 @@
|
|
1
|
-
import {
|
1
|
+
import { GroupAxisParams } from '../types';
|
2
2
|
|
3
|
-
export declare const GroupAxis: import('@orbcharts/core').PluginConstructor<"grid", string,
|
3
|
+
export declare const GroupAxis: import('@orbcharts/core').PluginConstructor<"grid", string, GroupAxisParams>;
|
package/dist/src/grid/types.d.ts
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import { ColorType } from '@orbcharts/core';
|
2
|
+
import { BaseLegendParams } from '../base/BaseLegend';
|
2
3
|
|
3
4
|
export interface LinesPluginParams {
|
4
5
|
lineCurve: string;
|
@@ -38,7 +39,7 @@ export interface BarsTrianglePluginParams {
|
|
38
39
|
barGroupPadding: number;
|
39
40
|
linearGradientOpacity: [number, number];
|
40
41
|
}
|
41
|
-
export interface
|
42
|
+
export interface GroupAxisParams {
|
42
43
|
labelOffset: [number, number];
|
43
44
|
labelColorType: ColorType;
|
44
45
|
axisLineVisible: boolean;
|
@@ -71,3 +72,5 @@ export interface ValueStackAxisParams extends ValueAxisParams {
|
|
71
72
|
}
|
72
73
|
export interface ScalingAreaParams {
|
73
74
|
}
|
75
|
+
export interface GridLegendParams extends BaseLegendParams {
|
76
|
+
}
|
@@ -1,3 +1 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
export declare const SeriesLegend: import('@orbcharts/core').PluginConstructor<"series", string, SeriesLegendParams>;
|
1
|
+
export declare const SeriesLegend: import('@orbcharts/core').PluginConstructor<"series", string, import('..').SeriesLegendParams>;
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import { ComputedDatumSeries, EventSeries, EventName, ColorType } from '@orbcharts/core';
|
2
|
+
import { BaseLegendParams } from '../base/BaseLegend';
|
2
3
|
|
3
|
-
export type
|
4
|
+
export type BubbleScaleType = 'area' | 'radius';
|
4
5
|
export interface BubblesPluginParams {
|
5
6
|
force: {
|
6
7
|
strength: number;
|
@@ -13,7 +14,7 @@ export interface BubblesPluginParams {
|
|
13
14
|
lineLengthMin: number;
|
14
15
|
};
|
15
16
|
highlightRIncrease: number;
|
16
|
-
|
17
|
+
bubbleScaleType: BubbleScaleType;
|
17
18
|
}
|
18
19
|
export interface PiePluginParams {
|
19
20
|
outerRadius: number;
|
@@ -43,10 +44,5 @@ export interface PieLabelsPluginParams {
|
|
43
44
|
labelFn: ((d: ComputedDatumSeries) => string);
|
44
45
|
labelColorType: ColorType;
|
45
46
|
}
|
46
|
-
export interface SeriesLegendParams {
|
47
|
-
position: 'top' | 'bottom' | 'left' | 'right';
|
48
|
-
justify: 'start' | 'center' | 'end';
|
49
|
-
padding: number;
|
50
|
-
gap: number;
|
51
|
-
rectRadius: number;
|
47
|
+
export interface SeriesLegendParams extends BaseLegendParams {
|
52
48
|
}
|
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.28",
|
4
4
|
"description": "plugins for OrbCharts",
|
5
5
|
"author": "Blue Planet Inc.",
|
6
6
|
"license": "Apache-2.0",
|
@@ -35,7 +35,7 @@
|
|
35
35
|
"vite-plugin-dts": "^3.7.3"
|
36
36
|
},
|
37
37
|
"dependencies": {
|
38
|
-
"@orbcharts/core": "^3.0.0-alpha.
|
38
|
+
"@orbcharts/core": "^3.0.0-alpha.24",
|
39
39
|
"d3": "^7.8.5",
|
40
40
|
"rxjs": "^7.8.1"
|
41
41
|
}
|
@@ -0,0 +1,543 @@
|
|
1
|
+
import * as d3 from 'd3'
|
2
|
+
import {
|
3
|
+
combineLatest,
|
4
|
+
map,
|
5
|
+
switchMap,
|
6
|
+
takeUntil,
|
7
|
+
Observable,
|
8
|
+
Subject } from 'rxjs'
|
9
|
+
import type { BasePluginFn } from './types'
|
10
|
+
import type {
|
11
|
+
ChartParams, Layout, ColorType } from '@orbcharts/core'
|
12
|
+
import { getSeriesColor, getClassName, getColor } from '../utils/orbchartsUtils'
|
13
|
+
import { measureTextWidth } from '../utils/commonUtils'
|
14
|
+
|
15
|
+
export interface BaseLegendParams {
|
16
|
+
position: 'top' | 'bottom' | 'left' | 'right'
|
17
|
+
justify: 'start' | 'center' | 'end'
|
18
|
+
padding: number
|
19
|
+
// offset: [number, number]
|
20
|
+
backgroundFill: ColorType
|
21
|
+
backgroundStroke: ColorType
|
22
|
+
gap: number
|
23
|
+
listRectWidth: number
|
24
|
+
listRectHeight: number
|
25
|
+
listRectRadius: number
|
26
|
+
// highlightEvent: boolean
|
27
|
+
}
|
28
|
+
|
29
|
+
// 第1層 - 定位的容器
|
30
|
+
interface RootPosition {
|
31
|
+
x:number
|
32
|
+
y:number
|
33
|
+
}
|
34
|
+
|
35
|
+
// 第2層 - 卡片
|
36
|
+
interface LegendCard {
|
37
|
+
width: number
|
38
|
+
height: number
|
39
|
+
translateX:number
|
40
|
+
translateY:number
|
41
|
+
}
|
42
|
+
|
43
|
+
// 第3層 - 圖例列表
|
44
|
+
interface LegendList {
|
45
|
+
direction: 'row' | 'column'
|
46
|
+
width: number
|
47
|
+
height: number
|
48
|
+
translateX:number
|
49
|
+
translateY:number
|
50
|
+
// list: LegendItem[][]
|
51
|
+
}
|
52
|
+
|
53
|
+
// 第4層 - 圖例項目
|
54
|
+
interface LegendItem {
|
55
|
+
id: string // seriesLabel
|
56
|
+
seriesLabel: string
|
57
|
+
seriesIndex: number
|
58
|
+
lineIndex: number
|
59
|
+
itemIndex: number // 行內的item
|
60
|
+
text: string
|
61
|
+
itemWidth: number
|
62
|
+
translateX: number
|
63
|
+
translateY: number
|
64
|
+
color: string
|
65
|
+
// fontSize: number
|
66
|
+
// listRectRadius: number
|
67
|
+
}
|
68
|
+
|
69
|
+
export const createBaseLegend: BasePluginFn = (pluginName: string, {
|
70
|
+
rootSelection,
|
71
|
+
seriesLabels$,
|
72
|
+
fullParams$,
|
73
|
+
layout$,
|
74
|
+
fullChartParams$
|
75
|
+
}: {
|
76
|
+
rootSelection: d3.Selection<any, unknown, any, unknown>
|
77
|
+
seriesLabels$: Observable<string[]>
|
78
|
+
fullParams$: Observable<BaseLegendParams>
|
79
|
+
layout$: Observable<Layout>
|
80
|
+
fullChartParams$: Observable<ChartParams>
|
81
|
+
}) => {
|
82
|
+
|
83
|
+
const rootPositionClassName = getClassName(pluginName, 'root-position')
|
84
|
+
const legendCardClassName = getClassName(pluginName, 'legend-card')
|
85
|
+
const legendListClassName = getClassName(pluginName, 'legend-list')
|
86
|
+
const legendItemClassName = getClassName(pluginName, 'legend-item')
|
87
|
+
|
88
|
+
const destroy$ = new Subject()
|
89
|
+
|
90
|
+
// const seriesLabels$: Observable<string[]> = SeriesDataMap$.pipe(
|
91
|
+
// takeUntil(destroy$),
|
92
|
+
// map(data => {
|
93
|
+
// return Array.from(data.keys())
|
94
|
+
// })
|
95
|
+
// )
|
96
|
+
|
97
|
+
const lineDirection$ = fullParams$.pipe(
|
98
|
+
takeUntil(destroy$),
|
99
|
+
map(data => {
|
100
|
+
return data.position === 'bottom' || data.position === 'top'
|
101
|
+
? 'row'
|
102
|
+
: 'column'
|
103
|
+
})
|
104
|
+
)
|
105
|
+
|
106
|
+
const lineMaxSize$ = combineLatest({
|
107
|
+
fullParams: fullParams$,
|
108
|
+
layout: layout$
|
109
|
+
}).pipe(
|
110
|
+
takeUntil(destroy$),
|
111
|
+
switchMap(async d => d),
|
112
|
+
map(data => {
|
113
|
+
const ourterSize = (data.fullParams.padding) * 2 + (data.fullParams.gap * 2) // 卡片離場景的間距 & 卡片內的間距
|
114
|
+
|
115
|
+
return data.fullParams.position === 'bottom' || data.fullParams.position === 'top'
|
116
|
+
? data.layout.rootWidth - ourterSize
|
117
|
+
: data.layout.rootHeight - ourterSize
|
118
|
+
})
|
119
|
+
)
|
120
|
+
|
121
|
+
const rootPosition$ = combineLatest({
|
122
|
+
layout: layout$,
|
123
|
+
fullParams: fullParams$,
|
124
|
+
}).pipe(
|
125
|
+
takeUntil(destroy$),
|
126
|
+
switchMap(async d => d),
|
127
|
+
map(data => {
|
128
|
+
let x = 0
|
129
|
+
let y = 0
|
130
|
+
if (data.fullParams.position === 'bottom') {
|
131
|
+
y = data.layout.rootHeight
|
132
|
+
if (data.fullParams.justify === 'start') {
|
133
|
+
x = 0
|
134
|
+
} else if (data.fullParams.justify === 'center') {
|
135
|
+
x = data.layout.rootWidth / 2
|
136
|
+
} else if (data.fullParams.justify === 'end') {
|
137
|
+
x = data.layout.rootWidth
|
138
|
+
}
|
139
|
+
} else if (data.fullParams.position === 'right') {
|
140
|
+
x = data.layout.rootWidth
|
141
|
+
if (data.fullParams.justify === 'start') {
|
142
|
+
y = 0
|
143
|
+
} else if (data.fullParams.justify === 'center') {
|
144
|
+
y = data.layout.rootHeight / 2
|
145
|
+
} else if (data.fullParams.justify === 'end') {
|
146
|
+
y = data.layout.rootHeight
|
147
|
+
}
|
148
|
+
} else if (data.fullParams.position === 'top') {
|
149
|
+
y = 0
|
150
|
+
if (data.fullParams.justify === 'start') {
|
151
|
+
x = 0
|
152
|
+
} else if (data.fullParams.justify === 'center') {
|
153
|
+
x = data.layout.rootWidth / 2
|
154
|
+
} else if (data.fullParams.justify === 'end') {
|
155
|
+
x = data.layout.rootWidth
|
156
|
+
}
|
157
|
+
} else if (data.fullParams.position === 'left') {
|
158
|
+
x = 0
|
159
|
+
if (data.fullParams.justify === 'start') {
|
160
|
+
y = 0
|
161
|
+
} else if (data.fullParams.justify === 'center') {
|
162
|
+
y = data.layout.rootHeight / 2
|
163
|
+
} else if (data.fullParams.justify === 'end') {
|
164
|
+
y = data.layout.rootHeight
|
165
|
+
}
|
166
|
+
}
|
167
|
+
|
168
|
+
return {
|
169
|
+
x,
|
170
|
+
y
|
171
|
+
}
|
172
|
+
})
|
173
|
+
)
|
174
|
+
|
175
|
+
const rootPositionSelection$: Observable<d3.Selection<SVGGElement, RootPosition, any, any>> = rootPosition$.pipe(
|
176
|
+
takeUntil(destroy$),
|
177
|
+
map(data => {
|
178
|
+
|
179
|
+
return rootSelection
|
180
|
+
.selectAll<SVGGElement, RootPosition>(`g.${rootPositionClassName}`)
|
181
|
+
.data([data])
|
182
|
+
.join(
|
183
|
+
enter => {
|
184
|
+
return enter
|
185
|
+
.append('g')
|
186
|
+
.classed(rootPositionClassName, true)
|
187
|
+
.attr('transform', d => `translate(${d.x}, ${d.y})`)
|
188
|
+
},
|
189
|
+
update => {
|
190
|
+
return update
|
191
|
+
.transition()
|
192
|
+
.attr('transform', d => `translate(${d.x}, ${d.y})`)
|
193
|
+
},
|
194
|
+
exit => exit.remove()
|
195
|
+
)
|
196
|
+
})
|
197
|
+
)
|
198
|
+
|
199
|
+
// 先計算list內每個item
|
200
|
+
const lengendItems$: Observable<LegendItem[][]> = combineLatest({
|
201
|
+
fullParams: fullParams$,
|
202
|
+
fullChartParams: fullChartParams$,
|
203
|
+
seriesLabels: seriesLabels$,
|
204
|
+
lineDirection: lineDirection$,
|
205
|
+
lineMaxSize: lineMaxSize$
|
206
|
+
}).pipe(
|
207
|
+
takeUntil(destroy$),
|
208
|
+
switchMap(async d => d),
|
209
|
+
map(data => {
|
210
|
+
return data.seriesLabels.reduce((prev: LegendItem[][], current, currentIndex) => {
|
211
|
+
const textWidth = measureTextWidth(current, data.fullChartParams.styles.textSize)
|
212
|
+
const itemWidth = (data.fullChartParams.styles.textSize * 1.5) + textWidth
|
213
|
+
const color = getSeriesColor(currentIndex, data.fullChartParams)
|
214
|
+
const lastItem: LegendItem | null = prev[0] && prev[0][0]
|
215
|
+
? prev[prev.length - 1][prev[prev.length - 1].length - 1]
|
216
|
+
: null
|
217
|
+
|
218
|
+
const { translateX, translateY, lineIndex, itemIndex } = ((_data, _prev, _lastItem) => {
|
219
|
+
let translateX = 0
|
220
|
+
let translateY = 0
|
221
|
+
let lineIndex = 0
|
222
|
+
let itemIndex = 0
|
223
|
+
|
224
|
+
if (_data.lineDirection === 'column') {
|
225
|
+
let tempTranslateY = _lastItem
|
226
|
+
? _lastItem.translateY + _data.fullChartParams.styles.textSize + _data.fullParams.gap
|
227
|
+
: 0
|
228
|
+
|
229
|
+
if ((tempTranslateY + _data.fullChartParams.styles.textSize) > _data.lineMaxSize) {
|
230
|
+
// 換行
|
231
|
+
lineIndex = _lastItem.lineIndex + 1
|
232
|
+
itemIndex = 0
|
233
|
+
translateY = 0
|
234
|
+
// 前一行最寬寬度
|
235
|
+
const maxItemWidthInLastLine = _prev[_prev.length - 1].reduce((p, c) => {
|
236
|
+
return c.itemWidth > p ? c.itemWidth : p
|
237
|
+
}, 0)
|
238
|
+
translateX = _lastItem.translateX + maxItemWidthInLastLine + _data.fullParams.gap
|
239
|
+
} else {
|
240
|
+
lineIndex = _lastItem ? _lastItem.lineIndex : 0
|
241
|
+
itemIndex = _lastItem ? _lastItem.itemIndex + 1 : 0
|
242
|
+
translateY = tempTranslateY
|
243
|
+
translateX = _lastItem ? _lastItem.translateX : 0
|
244
|
+
}
|
245
|
+
} else {
|
246
|
+
let tempTranslateX = _lastItem
|
247
|
+
? _lastItem.translateX + _lastItem.itemWidth + _data.fullParams.gap
|
248
|
+
: 0
|
249
|
+
if ((tempTranslateX + itemWidth) > _data.lineMaxSize) {
|
250
|
+
// 換行
|
251
|
+
lineIndex = _lastItem.lineIndex + 1
|
252
|
+
itemIndex = 0
|
253
|
+
translateX = 0
|
254
|
+
} else {
|
255
|
+
lineIndex = _lastItem ? _lastItem.lineIndex : 0
|
256
|
+
itemIndex = _lastItem ? _lastItem.itemIndex + 1 : 0
|
257
|
+
translateX = tempTranslateX
|
258
|
+
}
|
259
|
+
translateY = (_data.fullChartParams.styles.textSize + _data.fullParams.gap) * lineIndex
|
260
|
+
}
|
261
|
+
|
262
|
+
return { translateX, translateY, lineIndex, itemIndex }
|
263
|
+
})(data, prev, lastItem)
|
264
|
+
|
265
|
+
if (!prev[lineIndex]) {
|
266
|
+
prev[lineIndex] = []
|
267
|
+
}
|
268
|
+
|
269
|
+
prev[lineIndex].push({
|
270
|
+
id: current,
|
271
|
+
seriesLabel: current,
|
272
|
+
seriesIndex: currentIndex,
|
273
|
+
lineIndex,
|
274
|
+
itemIndex,
|
275
|
+
text: current,
|
276
|
+
itemWidth,
|
277
|
+
translateX,
|
278
|
+
translateY,
|
279
|
+
color,
|
280
|
+
})
|
281
|
+
console.log('items', prev)
|
282
|
+
return prev
|
283
|
+
}, [])
|
284
|
+
})
|
285
|
+
)
|
286
|
+
|
287
|
+
// 依list計算出來的排序位置來計算整體容器的尺寸
|
288
|
+
const lengendList$: Observable<LegendList> = combineLatest({
|
289
|
+
fullParams: fullParams$,
|
290
|
+
fullChartParams: fullChartParams$,
|
291
|
+
lineDirection: lineDirection$,
|
292
|
+
lengendItems: lengendItems$
|
293
|
+
}).pipe(
|
294
|
+
takeUntil(destroy$),
|
295
|
+
switchMap(async d => d),
|
296
|
+
map(data => {
|
297
|
+
// 依list計算出來的排序位置來計算整體容器的偏移位置
|
298
|
+
const { width, height } = ((_data, _lengendItems) => {
|
299
|
+
let width = 0
|
300
|
+
let height = 0
|
301
|
+
|
302
|
+
if (!_lengendItems.length || !_lengendItems[0].length) {
|
303
|
+
return { width, height }
|
304
|
+
}
|
305
|
+
|
306
|
+
const firstLineLastItem = _lengendItems[0][_lengendItems[0].length - 1]
|
307
|
+
if (_data.lineDirection === 'column') {
|
308
|
+
width = _lengendItems.reduce((p, c) => {
|
309
|
+
const maxWidthInLine = c.reduce((_p, _c) => {
|
310
|
+
// 找出最寬的寬度
|
311
|
+
return _c.itemWidth > _p ? _c.itemWidth : _p
|
312
|
+
}, 0)
|
313
|
+
// 每行寬度加總
|
314
|
+
return p + maxWidthInLine
|
315
|
+
}, 0)
|
316
|
+
width += _data.fullParams.gap * (_lengendItems.length - 1)
|
317
|
+
height = firstLineLastItem.translateY + _data.fullChartParams.styles.textSize
|
318
|
+
} else {
|
319
|
+
width = firstLineLastItem.translateX + firstLineLastItem.itemWidth
|
320
|
+
height = (_data.fullChartParams.styles.textSize * _lengendItems.length) + (_data.fullParams.gap * (_lengendItems.length - 1))
|
321
|
+
}
|
322
|
+
|
323
|
+
return { width, height }
|
324
|
+
})(data, data.lengendItems)
|
325
|
+
|
326
|
+
return {
|
327
|
+
direction: data.lineDirection,
|
328
|
+
width,
|
329
|
+
height,
|
330
|
+
translateX: data.fullParams.gap,
|
331
|
+
translateY: data.fullParams.gap
|
332
|
+
}
|
333
|
+
})
|
334
|
+
)
|
335
|
+
|
336
|
+
const legendCard$: Observable<LegendCard> = combineLatest({
|
337
|
+
fullParams: fullParams$,
|
338
|
+
lengendList: lengendList$
|
339
|
+
}).pipe(
|
340
|
+
takeUntil(destroy$),
|
341
|
+
switchMap(async d => d),
|
342
|
+
map(data => {
|
343
|
+
const width = data.lengendList.width + (data.fullParams.gap * 2)
|
344
|
+
const height = data.lengendList.height + (data.fullParams.gap * 2)
|
345
|
+
let translateX = 0
|
346
|
+
let translateY = 0
|
347
|
+
|
348
|
+
if (data.fullParams.position === 'left') {
|
349
|
+
if (data.fullParams.justify === 'start') {
|
350
|
+
translateX = data.fullParams.padding
|
351
|
+
translateY = data.fullParams.padding
|
352
|
+
} else if (data.fullParams.justify === 'center') {
|
353
|
+
translateX = data.fullParams.padding
|
354
|
+
translateY = - height / 2
|
355
|
+
} else if (data.fullParams.justify === 'end') {
|
356
|
+
translateX = data.fullParams.padding
|
357
|
+
translateY = - height - data.fullParams.padding
|
358
|
+
}
|
359
|
+
} else if (data.fullParams.position === 'right') {
|
360
|
+
if (data.fullParams.justify === 'start') {
|
361
|
+
translateX = - width - data.fullParams.padding
|
362
|
+
translateY = data.fullParams.padding
|
363
|
+
} else if (data.fullParams.justify === 'center') {
|
364
|
+
translateX = - width - data.fullParams.padding
|
365
|
+
translateY = - height / 2
|
366
|
+
} else if (data.fullParams.justify === 'end') {
|
367
|
+
translateX = - width - data.fullParams.padding
|
368
|
+
translateY = - height - data.fullParams.padding
|
369
|
+
}
|
370
|
+
} else if (data.fullParams.position === 'top') {
|
371
|
+
if (data.fullParams.justify === 'start') {
|
372
|
+
translateX = data.fullParams.padding
|
373
|
+
translateY = data.fullParams.padding
|
374
|
+
} else if (data.fullParams.justify === 'center') {
|
375
|
+
translateX = - width / 2
|
376
|
+
translateY = data.fullParams.padding
|
377
|
+
} else if (data.fullParams.justify === 'end') {
|
378
|
+
translateX = - width - data.fullParams.padding
|
379
|
+
translateY = data.fullParams.padding
|
380
|
+
}
|
381
|
+
} else {
|
382
|
+
if (data.fullParams.justify === 'start') {
|
383
|
+
translateX = data.fullParams.padding
|
384
|
+
translateY = - height - data.fullParams.padding
|
385
|
+
} else if (data.fullParams.justify === 'center') {
|
386
|
+
translateX = - width / 2
|
387
|
+
translateY = - height - data.fullParams.padding
|
388
|
+
} else if (data.fullParams.justify === 'end') {
|
389
|
+
translateX = - width - data.fullParams.padding
|
390
|
+
translateY = - height - data.fullParams.padding
|
391
|
+
}
|
392
|
+
}
|
393
|
+
// translateX += _data.fullParams.offset[0]
|
394
|
+
// translateY += _data.fullParams.offset[1]
|
395
|
+
|
396
|
+
return {
|
397
|
+
width,
|
398
|
+
height,
|
399
|
+
translateX,
|
400
|
+
translateY
|
401
|
+
}
|
402
|
+
})
|
403
|
+
)
|
404
|
+
|
405
|
+
const lengendCardSelection$ = combineLatest({
|
406
|
+
rootPositionSelection: rootPositionSelection$,
|
407
|
+
fullParams: fullParams$,
|
408
|
+
fullChartParams: fullChartParams$,
|
409
|
+
legendCard: legendCard$
|
410
|
+
}).pipe(
|
411
|
+
takeUntil(destroy$),
|
412
|
+
switchMap(async d => d),
|
413
|
+
map(data => {
|
414
|
+
return data.rootPositionSelection
|
415
|
+
.selectAll<SVGGElement, BaseLegendParams>('g')
|
416
|
+
.data([data.legendCard])
|
417
|
+
.join(
|
418
|
+
enter => {
|
419
|
+
return enter
|
420
|
+
.append('g')
|
421
|
+
.classed(legendCardClassName, true)
|
422
|
+
.attr('transform', d => `translate(${d.translateX}, ${d.translateY})`)
|
423
|
+
},
|
424
|
+
update => {
|
425
|
+
return update
|
426
|
+
.transition()
|
427
|
+
.attr('transform', d => `translate(${d.translateX}, ${d.translateY})`)
|
428
|
+
},
|
429
|
+
exit => exit.remove()
|
430
|
+
)
|
431
|
+
.each((d, i, g) => {
|
432
|
+
const rect = d3.select(g[i])
|
433
|
+
.selectAll('rect')
|
434
|
+
.data([d])
|
435
|
+
.join('rect')
|
436
|
+
.attr('width', d => d.width)
|
437
|
+
.attr('height', d => d.height)
|
438
|
+
.attr('fill', getColor(data.fullParams.backgroundFill, data.fullChartParams))
|
439
|
+
.attr('stroke', getColor(data.fullParams.backgroundStroke, data.fullChartParams))
|
440
|
+
})
|
441
|
+
})
|
442
|
+
)
|
443
|
+
|
444
|
+
|
445
|
+
const lengendListSelection$ = combineLatest({
|
446
|
+
lengendCardSelection: lengendCardSelection$,
|
447
|
+
fullParams: fullParams$,
|
448
|
+
lengendList: lengendList$
|
449
|
+
}).pipe(
|
450
|
+
takeUntil(destroy$),
|
451
|
+
switchMap(async d => d),
|
452
|
+
map(data => {
|
453
|
+
return data.lengendCardSelection
|
454
|
+
.selectAll<SVGGElement, BaseLegendParams>('g')
|
455
|
+
.data([data.lengendList])
|
456
|
+
.join(
|
457
|
+
enter => {
|
458
|
+
return enter
|
459
|
+
.append('g')
|
460
|
+
.classed(legendListClassName, true)
|
461
|
+
.attr('transform', d => `translate(${d.translateX}, ${d.translateY})`)
|
462
|
+
},
|
463
|
+
update => {
|
464
|
+
return update
|
465
|
+
.transition()
|
466
|
+
.attr('transform', d => `translate(${d.translateX}, ${d.translateY})`)
|
467
|
+
},
|
468
|
+
exit => exit.remove()
|
469
|
+
)
|
470
|
+
})
|
471
|
+
)
|
472
|
+
|
473
|
+
const itemSelection$ = combineLatest({
|
474
|
+
lengendListSelection: lengendListSelection$,
|
475
|
+
fullParams: fullParams$,
|
476
|
+
fullChartParams: fullChartParams$,
|
477
|
+
lengendItems: lengendItems$
|
478
|
+
}).pipe(
|
479
|
+
takeUntil(destroy$),
|
480
|
+
switchMap(async d => d),
|
481
|
+
map(data => {
|
482
|
+
const items = data.lengendItems[0] ? data.lengendItems.flat() : []
|
483
|
+
|
484
|
+
return data.lengendListSelection
|
485
|
+
.selectAll<SVGGElement, string>(`g.${legendItemClassName}`)
|
486
|
+
.data(items)
|
487
|
+
.join(
|
488
|
+
enter => {
|
489
|
+
return enter
|
490
|
+
.append('g')
|
491
|
+
.classed(legendItemClassName, true)
|
492
|
+
.attr('cursor', 'default')
|
493
|
+
},
|
494
|
+
update => update,
|
495
|
+
exit => exit.remove()
|
496
|
+
)
|
497
|
+
.attr('transform', (d, i) => {
|
498
|
+
return `translate(${d.translateX}, ${d.translateY})`
|
499
|
+
})
|
500
|
+
.each((d, i, g) => {
|
501
|
+
const rectCenterX = data.fullChartParams.styles.textSize / 2
|
502
|
+
const rectWidth = data.fullParams.listRectWidth
|
503
|
+
const rectHeight = data.fullParams.listRectHeight
|
504
|
+
// 方塊
|
505
|
+
d3.select(g[i])
|
506
|
+
.selectAll('rect')
|
507
|
+
.data([d])
|
508
|
+
.join('rect')
|
509
|
+
.attr('x', rectCenterX)
|
510
|
+
.attr('y', rectCenterX)
|
511
|
+
.attr('width', rectWidth)
|
512
|
+
.attr('height', rectHeight)
|
513
|
+
.attr('transform', `translate(${- rectWidth / 2}, ${- rectHeight / 2})`)
|
514
|
+
.attr('fill', _d => _d.color)
|
515
|
+
.attr('rx', data.fullParams.listRectRadius)
|
516
|
+
// 文字
|
517
|
+
d3.select(g[i])
|
518
|
+
.selectAll('text')
|
519
|
+
.data([d])
|
520
|
+
.join(
|
521
|
+
enter => {
|
522
|
+
return enter
|
523
|
+
.append('text')
|
524
|
+
.attr('dominant-baseline', 'hanging')
|
525
|
+
},
|
526
|
+
update => {
|
527
|
+
return update
|
528
|
+
},
|
529
|
+
exit => exit.remove()
|
530
|
+
)
|
531
|
+
.attr('x', data.fullChartParams.styles.textSize * 1.5)
|
532
|
+
.attr('font-size', data.fullChartParams.styles.textSize)
|
533
|
+
.text(d => d.text)
|
534
|
+
})
|
535
|
+
})
|
536
|
+
)
|
537
|
+
|
538
|
+
itemSelection$.subscribe()
|
539
|
+
|
540
|
+
return () => {
|
541
|
+
destroy$.next(undefined)
|
542
|
+
}
|
543
|
+
}
|