@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.
@@ -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;
@@ -0,0 +1,3 @@
1
+ export interface BasePluginFn {
2
+ (pluginName: string, context: any): () => void;
3
+ }
@@ -1,4 +1,4 @@
1
- import { LinesPluginParams, GroupAreaPluginParams, DotsPluginParams, BarsPluginParams, BarStackPluginParams, BarsTrianglePluginParams, GroupingAxisParams, ValueAxisParams, ValueStackAxisParams, ScalingAreaParams } from './types';
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: GroupingAxisParams;
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;
@@ -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 { GroupingAxisParams } from '../types';
1
+ import { GroupAxisParams } from '../types';
2
2
 
3
- export declare const GroupAxis: import('@orbcharts/core').PluginConstructor<"grid", string, GroupingAxisParams>;
3
+ export declare const GroupAxis: import('@orbcharts/core').PluginConstructor<"grid", string, GroupAxisParams>;
@@ -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 GroupingAxisParams {
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
- import { SeriesLegendParams } from '../types';
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 ScaleType = 'area' | 'radius';
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
- scaleType: ScaleType;
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.26",
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.22",
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
+ }
@@ -0,0 +1,3 @@
1
+ export interface BasePluginFn {
2
+ (pluginName: string, context: any): () => void // return unsubscribe function
3
+ }