@orbcharts/plugins-basic 3.0.0-alpha.30 → 3.0.0-alpha.31

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. package/dist/orbcharts-plugins-basic.es.js +15826 -15642
  2. package/dist/orbcharts-plugins-basic.umd.js +8 -8
  3. package/dist/src/base/BaseBarStack.d.ts +29 -0
  4. package/dist/src/base/BaseBars.d.ts +29 -0
  5. package/dist/src/base/BaseBarsTriangle.d.ts +28 -0
  6. package/dist/src/base/BaseLegend.d.ts +12 -3
  7. package/dist/src/base/BaseLines.d.ts +27 -0
  8. package/dist/src/base/types.d.ts +2 -2
  9. package/dist/src/grid/plugins/BarStack.d.ts +1 -3
  10. package/dist/src/grid/plugins/Bars.d.ts +1 -3
  11. package/dist/src/grid/plugins/BarsTriangle.d.ts +1 -3
  12. package/dist/src/grid/plugins/Lines.d.ts +1 -3
  13. package/dist/src/index.d.ts +1 -0
  14. package/dist/src/multiGrid/defaults.d.ts +3 -0
  15. package/dist/src/multiGrid/index.d.ts +3 -0
  16. package/dist/src/multiGrid/plugins/BarsAndLines.d.ts +1 -0
  17. package/dist/src/multiGrid/types.d.ts +7 -0
  18. package/package.json +2 -2
  19. package/src/base/BaseBarStack.ts +699 -0
  20. package/src/base/BaseBars.ts +639 -0
  21. package/src/base/BaseBarsTriangle.ts +626 -0
  22. package/src/base/BaseLegend.ts +9 -7
  23. package/src/base/BaseLines.ts +566 -0
  24. package/src/base/types.ts +2 -2
  25. package/src/grid/plugins/BarStack.ts +16 -643
  26. package/src/grid/plugins/Bars.ts +15 -586
  27. package/src/grid/plugins/BarsTriangle.ts +14 -577
  28. package/src/grid/plugins/Lines.ts +14 -508
  29. package/src/index.ts +1 -0
  30. package/src/multiGrid/defaults.ts +14 -0
  31. package/src/multiGrid/index.ts +3 -0
  32. package/src/multiGrid/plugins/BarStackAndLines.ts +0 -0
  33. package/src/multiGrid/plugins/BarsAndLines.ts +110 -0
  34. package/src/multiGrid/plugins/BarsTriangleAndLines.ts +0 -0
  35. package/src/multiGrid/plugins/DivergingValueAxes.ts +0 -0
  36. package/src/multiGrid/plugins/FirstGroupScaleAxis.ts +0 -0
  37. package/src/multiGrid/plugins/MultiGridLegend.ts +0 -0
  38. package/src/multiGrid/plugins/TwoValueScaleAxes.ts +0 -0
  39. package/src/multiGrid/types.ts +8 -0
  40. /package/dist/src/multiGrid/plugins/{DivergingAxes.d.ts → BarStackAndLines.d.ts} +0 -0
  41. /package/dist/src/multiGrid/plugins/{TwoScaleAxes.d.ts → BarsTriangleAndLines.d.ts} +0 -0
  42. /package/dist/src/multiGrid/plugins/{TwoScales.d.ts → DivergingValueAxes.d.ts} +0 -0
  43. /package/{src/multiGrid/plugins/DivergingAxes.ts → dist/src/multiGrid/plugins/FirstGroupScaleAxis.d.ts} +0 -0
  44. /package/{src/multiGrid/plugins/TwoScaleAxes.ts → dist/src/multiGrid/plugins/MultiGridLegend.d.ts} +0 -0
  45. /package/{src/multiGrid/plugins/TwoScales.ts → dist/src/multiGrid/plugins/TwoValueScaleAxes.d.ts} +0 -0
@@ -0,0 +1,626 @@
1
+ import * as d3 from 'd3'
2
+ import {
3
+ combineLatest,
4
+ map,
5
+ switchMap,
6
+ takeUntil,
7
+ distinctUntilChanged,
8
+ Observable,
9
+ Subject } from 'rxjs'
10
+ import type { BasePluginFn } from './types'
11
+ import type {
12
+ ComputedDatumGrid,
13
+ ComputedDataGrid,
14
+ EventGrid,
15
+ ChartParams,
16
+ Layout,
17
+ TransformData } from '@orbcharts/core'
18
+ import { getD3TransitionEase } from '../utils/d3Utils'
19
+ import { getClassName, getUniID } from '../utils/orbchartsUtils'
20
+
21
+ export interface BaseBarsTriangleParams {
22
+ barWidth: number
23
+ barPadding: number
24
+ barGroupPadding: number // 群組和群組間的間隔
25
+ linearGradientOpacity: [number, number]
26
+ }
27
+
28
+ interface BaseBarsContext {
29
+ selection: d3.Selection<any, unknown, any, unknown>
30
+ computedData$: Observable<ComputedDataGrid>
31
+ SeriesDataMap$: Observable<Map<string, ComputedDatumGrid[]>>
32
+ GroupDataMap$: Observable<Map<string, ComputedDatumGrid[]>>
33
+ fullParams$: Observable<BaseBarsTriangleParams>
34
+ fullChartParams$: Observable<ChartParams>
35
+ gridAxesTransform$: Observable<TransformData>
36
+ gridGraphicTransform$: Observable<TransformData>
37
+ gridAxesSize$: Observable<{
38
+ width: number;
39
+ height: number;
40
+ }>
41
+ gridHighlight$: Observable<string[]>
42
+ event$: Subject<EventGrid>
43
+ }
44
+
45
+
46
+ interface RenderBarParams {
47
+ selection: d3.Selection<SVGGElement, unknown, any, any>
48
+ barData: BarDatumGrid[][]
49
+ zeroY: number
50
+ groupLabels: string[]
51
+ barScale: d3.ScalePoint<string>
52
+ params: BaseBarsTriangleParams
53
+ chartParams: ChartParams
54
+ barWidth: number
55
+ delayGroup: number
56
+ transitionItem: number
57
+ }
58
+
59
+ interface BarDatumGrid extends ComputedDatumGrid {
60
+ linearGradientId: string
61
+ }
62
+
63
+ type ClipPathDatum = {
64
+ id: string;
65
+ // x: number;
66
+ // y: number;
67
+ width: number;
68
+ height: number;
69
+ }
70
+
71
+ const pluginName = 'BaseBarsTriangle'
72
+ const gClassName = getClassName(pluginName, 'g')
73
+ const gContentClassName = getClassName(pluginName, 'g-content')
74
+ // group的delay在動畫中的佔比(剩餘部份的時間為圖形本身的動畫時間,因為delay時間和最後一個group的動畫時間加總為1)
75
+ const groupDelayProportionOfDuration = 0.3
76
+
77
+ function calcBarWidth ({ axisWidth, groupAmount, barAmountOfGroup, barPadding = 0, barGroupPadding = 0 }: {
78
+ axisWidth: number
79
+ groupAmount: number
80
+ barAmountOfGroup: number
81
+ barPadding: number
82
+ barGroupPadding: number
83
+ }) {
84
+ const width = (axisWidth / groupAmount - barGroupPadding) / barAmountOfGroup - barPadding
85
+ return width > 1 ? width : 1
86
+ }
87
+
88
+ function makeBarScale (barWidth: number, seriesLabels: string[], params: BaseBarsTriangleParams) {
89
+ const barHalfWidth = barWidth! / 2
90
+ const barGroupWidth = barWidth * seriesLabels.length + params.barPadding! * seriesLabels.length
91
+ return d3.scalePoint()
92
+ .domain(seriesLabels)
93
+ .range([-barGroupWidth / 2 + barHalfWidth, barGroupWidth / 2 - barHalfWidth])
94
+ }
95
+
96
+ function calcDelayGroup (barGroupAmount: number, totalDuration: number) {
97
+ if (barGroupAmount <= 1) {
98
+ // 一筆內計算會出錯所以不算
99
+ return 0
100
+ }
101
+ return totalDuration / (barGroupAmount - 1) * groupDelayProportionOfDuration // 依group數量計算
102
+ }
103
+
104
+ function calctransitionItem (barGroupAmount: number, totalDuration: number) {
105
+ if (barGroupAmount <= 1) {
106
+ // 一筆內不會有delay
107
+ return totalDuration
108
+ }
109
+ return totalDuration * (1 - groupDelayProportionOfDuration) // delay後剩餘的時間
110
+ }
111
+
112
+ function renderTriangleBars ({ selection, barData, zeroY, groupLabels, barScale, params, chartParams, barWidth, delayGroup, transitionItem }: RenderBarParams) {
113
+ // console.log({ selection, data, zeroY, seriesLabels, barScale, params, chartParams, barWidth, delayGroup })
114
+ // if (barWidth <= 0) {
115
+ // return
116
+ // }
117
+ const update = selection!
118
+ .selectAll<SVGGElement, ComputedDatumGrid[]>(`g.${gClassName}`)
119
+ .data(barData, (d, i) => groupLabels[i])
120
+ const enter = update.enter()
121
+ .append('g')
122
+ .classed(gClassName, true)
123
+ .attr('cursor', 'pointer')
124
+ update.exit().remove()
125
+ const graphicGroupSelection = update.merge(enter)
126
+ enter
127
+ .attr('transform', (d, i) => `translate(${d[0] ? d[0].axisX : 0}, ${0})`)
128
+ update
129
+ // .transition()
130
+ // .duration(200)
131
+ .attr('transform', (d, i) => `translate(${d[0] ? d[0].axisX : 0}, ${0})`)
132
+
133
+ const barHalfWidth = barWidth! / 2
134
+
135
+ graphicGroupSelection
136
+ .each((d, i, g) => {
137
+ const pathUpdate = d3.select(g[i])
138
+ .selectAll<SVGGElement, ComputedDatumGrid>('g')
139
+ .data(d, _d => _d.id)
140
+ const pathEnter = pathUpdate
141
+ .enter()
142
+ .append('g')
143
+ .classed(gContentClassName, true)
144
+ pathEnter
145
+ .append('path')
146
+ // .attr('transform', `translate(${-barHalfWidth}, 0)`)
147
+ // .attr('x', d => itemScale(d.itemLabel)!)
148
+ // .attr('y', d => 0)
149
+ .style('vector-effect', 'non-scaling-stroke')
150
+ .attr('height', d => 0)
151
+ .attr('d', (d) => {
152
+ const x = barScale(d.seriesLabel)!
153
+ const y1 = zeroY
154
+ const y2 = y1
155
+ return `M${x - (barWidth! / 2)},${y1} L${x},${y2} ${x + (barWidth! / 2)},${y1}`
156
+ })
157
+ pathUpdate.merge(pathEnter)
158
+ .select('path')
159
+ .style('fill', d => `url(#${d.linearGradientId})`)
160
+ .attr('stroke', d => d.color)
161
+ .attr('transform', `translate(${-barHalfWidth}, 0)`)
162
+ .transition()
163
+ .duration(transitionItem)
164
+ .ease(getD3TransitionEase(chartParams.transitionEase))
165
+ .delay((d, i) => d.groupIndex * delayGroup)
166
+ .attr('transform', `translate(${-barHalfWidth}, 0)`)
167
+ // .attr('x', d => itemScale(d.itemLabel)!)
168
+ // .attr('y', d => -d.y)
169
+ .attr('height', d => Math.abs(d.axisYFromZero))
170
+ .attr('d', (d) => {
171
+ const x = barScale(d.seriesLabel)!
172
+ const y1 = zeroY
173
+ const y2 = d.axisY
174
+ return `M${x},${y1} L${x + (barWidth! / 2)},${y2} ${x + barWidth!},${y1}`
175
+ })
176
+ // .on('end', () => initHighlight())
177
+ pathUpdate.exit().remove()
178
+ })
179
+
180
+ const graphicBarSelection: d3.Selection<SVGPathElement, ComputedDatumGrid, any, any> = graphicGroupSelection.selectAll(`g.${gContentClassName}`)
181
+
182
+ return graphicBarSelection
183
+ }
184
+
185
+ function renderLinearGradient ({ defsSelection, barData, params }: {
186
+ defsSelection: d3.Selection<SVGDefsElement, ComputedDatumGrid, any, any>
187
+ barData: BarDatumGrid[][]
188
+ params: BaseBarsTriangleParams
189
+ }) {
190
+ const linearGradientUpdate = defsSelection!
191
+ .selectAll<SVGLinearGradientElement, ComputedDatumGrid>('linearGradient')
192
+ .data(barData[0] ?? [], d => d.seriesLabel)
193
+ const linearGradientEnter = linearGradientUpdate
194
+ .enter()
195
+ .append('linearGradient')
196
+ .attr('x1', '0%')
197
+ .attr('x2', '0%')
198
+ .attr('y1', '100%')
199
+ .attr('y2', '0%')
200
+ .attr('spreadMethod', 'pad')
201
+ linearGradientUpdate.merge(linearGradientEnter)
202
+ .attr('id', (d, i) => d.linearGradientId)
203
+ .html((d, i) => `
204
+ <stop offset="0%" stop-color="${d.color}" stop-opacity="${params.linearGradientOpacity[0]}"/>
205
+ <stop offset="100%" stop-color="${d.color}" stop-opacity="${params.linearGradientOpacity[1]}"/>
206
+ `)
207
+ linearGradientUpdate.exit().remove()
208
+ }
209
+
210
+
211
+ function renderClipPath ({ defsSelection, clipPathData }: {
212
+ defsSelection: d3.Selection<SVGDefsElement, any, any, any>
213
+ clipPathData: ClipPathDatum[]
214
+ }) {
215
+ const update = defsSelection
216
+ .selectAll<SVGClipPathElement, Layout>('clipPath')
217
+ .data(clipPathData)
218
+ const enter = update.enter()
219
+ .append('clipPath')
220
+ const cutRect = update.merge(enter)
221
+ .attr('id', d => d.id)
222
+ update.exit().remove()
223
+
224
+ update.merge(enter).each((d, i, g) => {
225
+ const updateRect = d3.select(g[i])
226
+ .selectAll<SVGRectElement, typeof d>('rect')
227
+ .data([d])
228
+ const enterRect = updateRect.enter().append('rect')
229
+ updateRect.merge(enterRect)
230
+ .attr('x', 0)
231
+ .attr('y', 0)
232
+ .attr('width', _d => _d.width)
233
+ .attr('height', _d => _d.height)
234
+ updateRect.exit().remove()
235
+ })
236
+ }
237
+
238
+ function highlight ({ selection, ids, fullChartParams }: {
239
+ selection: d3.Selection<SVGPathElement, ComputedDatumGrid, any, any>
240
+ ids: string[]
241
+ fullChartParams: ChartParams
242
+ }) {
243
+ selection.interrupt('highlight')
244
+
245
+ const removeHighlight = () => {
246
+ selection
247
+ .transition('highlight')
248
+ .duration(200)
249
+ .style('opacity', 1)
250
+ }
251
+
252
+ if (!ids.length) {
253
+ removeHighlight()
254
+ return
255
+ }
256
+
257
+ selection
258
+ .each((d, i, n) => {
259
+ if (ids.includes(d.id)) {
260
+ d3.select(n[i])
261
+ .style('opacity', 1)
262
+ } else {
263
+ d3.select(n[i])
264
+ .style('opacity', fullChartParams.styles.unhighlightedOpacity)
265
+ }
266
+ })
267
+ }
268
+
269
+
270
+ export const createBaseBarsTriangle: BasePluginFn<BaseBarsContext> = (pluginName: string, {
271
+ selection,
272
+ computedData$,
273
+ SeriesDataMap$,
274
+ GroupDataMap$,
275
+ fullParams$,
276
+ fullChartParams$,
277
+ gridAxesTransform$,
278
+ gridGraphicTransform$,
279
+ gridAxesSize$,
280
+ gridHighlight$,
281
+ event$
282
+ }) => {
283
+ const destroy$ = new Subject()
284
+
285
+ const clipPathID = getUniID(pluginName, 'clipPath-box')
286
+
287
+ const axisSelection: d3.Selection<SVGGElement, any, any, any> = selection
288
+ .append('g')
289
+ .attr('clip-path', `url(#${clipPathID})`)
290
+ const defsSelection: d3.Selection<SVGDefsElement, ComputedDatumGrid, any, any> = axisSelection.append('defs')
291
+ const graphicGSelection: d3.Selection<SVGGElement, any, any, any> = axisSelection.append('g')
292
+ let graphicSelection: d3.Selection<SVGGElement, any, any, any> | undefined
293
+ const pathSelection$: Subject<d3.Selection<SVGPathElement, ComputedDatumGrid, any, any>> = new Subject()
294
+ // .style('transform', 'translate(0px, 0px) scale(1)')
295
+
296
+ gridAxesTransform$
297
+ .pipe(
298
+ takeUntil(destroy$),
299
+ map(d => d.value),
300
+ distinctUntilChanged()
301
+ ).subscribe(d => {
302
+ axisSelection
303
+ .style('transform', d)
304
+ })
305
+
306
+ gridGraphicTransform$
307
+ .pipe(
308
+ takeUntil(destroy$),
309
+ switchMap(async d => d.value),
310
+ distinctUntilChanged()
311
+ ).subscribe(d => {
312
+ graphicGSelection
313
+ .transition()
314
+ .duration(50)
315
+ .style('transform', d)
316
+ })
317
+
318
+ // const axisSize$ = gridAxisSizeObservable({
319
+ // dataFormatter$,
320
+ // layout$
321
+ // })
322
+
323
+ const zeroY$ = computedData$.pipe(
324
+ map(d => d[0] && d[0][0]
325
+ ? d[0][0].axisY - d[0][0].axisYFromZero
326
+ : 0),
327
+ distinctUntilChanged()
328
+ )
329
+
330
+ const barWidth$ = new Observable<number>(subscriber => {
331
+ combineLatest({
332
+ computedData: computedData$,
333
+ params: fullParams$,
334
+ axisSize: gridAxesSize$
335
+ }).pipe(
336
+ switchMap(async d => d)
337
+ ).subscribe(data => {
338
+ const barWidth = calcBarWidth({
339
+ axisWidth: data.axisSize.width,
340
+ groupAmount: data.computedData[0] ? data.computedData[0].length : 0,
341
+ barAmountOfGroup: data.computedData.length,
342
+ barPadding: data.params.barPadding,
343
+ barGroupPadding: data.params.barGroupPadding
344
+ })
345
+ subscriber.next(barWidth)
346
+ })
347
+ }).pipe(
348
+ takeUntil(destroy$),
349
+ distinctUntilChanged()
350
+ )
351
+
352
+ const seriesLabels$ = computedData$.pipe(
353
+ takeUntil(destroy$),
354
+ map(data => data.map((d, i) => d[0] ? d[0].seriesLabel : String(i)))
355
+ )
356
+
357
+ const groupLabels$ = computedData$.pipe(
358
+ takeUntil(destroy$),
359
+ map(data => {
360
+ return data[0] ? data[0].map(d => d.groupLabel) : []
361
+ })
362
+ )
363
+
364
+ const barScale$: Observable<d3.ScalePoint<string>> = new Observable(subscriber => {
365
+ combineLatest({
366
+ seriesLabels: seriesLabels$,
367
+ barWidth: barWidth$,
368
+ params: fullParams$,
369
+ }).pipe(
370
+ takeUntil(destroy$),
371
+ switchMap(async d => d)
372
+ ).subscribe(data => {
373
+ const barScale = makeBarScale(data.barWidth, data.seriesLabels, data.params)
374
+ subscriber.next(barScale)
375
+ })
376
+ })
377
+
378
+ const transitionDuration$ = fullChartParams$.pipe(
379
+ takeUntil(destroy$),
380
+ map(d => d.transitionDuration),
381
+ distinctUntilChanged()
382
+ )
383
+
384
+ const delayGroup$ = new Observable<number>(subscriber => {
385
+ combineLatest({
386
+ groupLabels: groupLabels$,
387
+ transitionDuration: transitionDuration$,
388
+ }).pipe(
389
+ switchMap(async d => d)
390
+ ).subscribe(data => {
391
+ const delay = calcDelayGroup(data.groupLabels.length, data.transitionDuration)
392
+ subscriber.next(delay)
393
+ })
394
+ }).pipe(
395
+ takeUntil(destroy$),
396
+ distinctUntilChanged()
397
+ )
398
+
399
+ const transitionItem$ = new Observable<number>(subscriber => {
400
+ combineLatest({
401
+ groupLabels: groupLabels$,
402
+ transitionDuration: transitionDuration$
403
+ }).pipe(
404
+ switchMap(async d => d)
405
+ ).subscribe(data => {
406
+ const transition = calctransitionItem(data.groupLabels.length, data.transitionDuration)
407
+ subscriber.next(transition)
408
+ })
409
+ }).pipe(
410
+ takeUntil(destroy$),
411
+ distinctUntilChanged()
412
+ )
413
+
414
+ const transposedData$ = computedData$.pipe(
415
+ takeUntil(destroy$),
416
+ map(data => {
417
+ // 取得原始陣列的維度
418
+ const rows = data.length;
419
+ const cols = data.reduce((prev, current) => {
420
+ return Math.max(prev, current.length)
421
+ }, 0)
422
+
423
+ // 初始化轉換後的陣列
424
+ const transposedArray: typeof data = new Array(cols).fill(null).map(() => new Array(rows).fill(null))
425
+
426
+ // 遍歷原始陣列,進行轉換
427
+ for (let i = 0; i < rows; i++) {
428
+ for (let j = 0; j < cols; j++) {
429
+ transposedArray[j][i] = data[i][j]
430
+ }
431
+ }
432
+
433
+ return transposedArray
434
+ })
435
+ )
436
+
437
+ const barData$ = transposedData$.pipe(
438
+ takeUntil(destroy$),
439
+ map(data => {
440
+ const linearGradientIds = data.length
441
+ ? data[0].map(d => getUniID(pluginName, `lineargradient-${d.seriesLabel}`))
442
+ : []
443
+ return data.map(d => {
444
+ return d.map((_d, _i) => {
445
+ return <BarDatumGrid>{
446
+ linearGradientId: linearGradientIds[_i],
447
+ ..._d
448
+ }
449
+ })
450
+ })
451
+ })
452
+ )
453
+
454
+ gridAxesSize$.pipe(
455
+ takeUntil(destroy$)
456
+ ).subscribe(data => {
457
+ const clipPathData = [{
458
+ id: clipPathID,
459
+ width: data.width,
460
+ height: data.height
461
+ }]
462
+ renderClipPath({
463
+ defsSelection,
464
+ clipPathData
465
+ })
466
+ })
467
+
468
+ // const SeriesDataMap$ = computedData$.pipe(
469
+ // map(d => makeGridSeriesDataMap(d))
470
+ // )
471
+
472
+ // const GroupDataMap$ = computedData$.pipe(
473
+ // map(d => makeGridGroupDataMap(d))
474
+ // )
475
+
476
+ const highlightTarget$ = fullChartParams$.pipe(
477
+ takeUntil(destroy$),
478
+ map(d => d.highlightTarget),
479
+ distinctUntilChanged()
480
+ )
481
+
482
+ combineLatest({
483
+ barData: barData$,
484
+ computedData: computedData$,
485
+ zeroY: zeroY$,
486
+ groupLabels: groupLabels$,
487
+ barScale: barScale$,
488
+ params: fullParams$,
489
+ chartParams: fullChartParams$,
490
+ highlightTarget: highlightTarget$,
491
+ barWidth: barWidth$,
492
+ delayGroup: delayGroup$,
493
+ transitionItem: transitionItem$,
494
+ SeriesDataMap: SeriesDataMap$,
495
+ GroupDataMap: GroupDataMap$
496
+ }).pipe(
497
+ takeUntil(destroy$),
498
+ // 轉換後會退訂前一個未完成的訂閱事件,因此可以取到「同時間」最後一次的訂閱事件
499
+ switchMap(async (d) => d),
500
+ ).subscribe(data => {
501
+ const pathSelection = renderTriangleBars({
502
+ selection: graphicGSelection,
503
+ barData: data.barData,
504
+ zeroY: data.zeroY,
505
+ groupLabels: data.groupLabels,
506
+ barScale: data.barScale,
507
+ params: data.params,
508
+ chartParams: data.chartParams,
509
+ barWidth: data.barWidth,
510
+ delayGroup: data.delayGroup,
511
+ transitionItem: data.transitionItem
512
+ })
513
+ renderLinearGradient({
514
+ defsSelection,
515
+ barData: data.barData,
516
+ params: data.params
517
+ })
518
+
519
+ pathSelection!
520
+ .on('mouseover', (event, datum) => {
521
+ event.stopPropagation()
522
+
523
+ event$.next({
524
+ type: 'grid',
525
+ eventName: 'mouseover',
526
+ pluginName,
527
+ highlightTarget: data.highlightTarget,
528
+ datum,
529
+ series: data.SeriesDataMap.get(datum.seriesLabel)!,
530
+ seriesIndex: datum.seriesIndex,
531
+ seriesLabel: datum.seriesLabel,
532
+ groups: data.GroupDataMap.get(datum.groupLabel)!,
533
+ groupIndex: datum.groupIndex,
534
+ groupLabel: datum.groupLabel,
535
+ event,
536
+ data: data.computedData
537
+ })
538
+ })
539
+ .on('mousemove', (event, datum) => {
540
+ event.stopPropagation()
541
+
542
+ event$.next({
543
+ type: 'grid',
544
+ eventName: 'mousemove',
545
+ pluginName,
546
+ highlightTarget: data.highlightTarget,
547
+ datum,
548
+ series: data.SeriesDataMap.get(datum.seriesLabel)!,
549
+ seriesIndex: datum.seriesIndex,
550
+ seriesLabel: datum.seriesLabel,
551
+ groups: data.GroupDataMap.get(datum.groupLabel)!,
552
+ groupIndex: datum.groupIndex,
553
+ groupLabel: datum.groupLabel,
554
+ event,
555
+ data: data.computedData
556
+ })
557
+ })
558
+ .on('mouseout', (event, datum) => {
559
+ event.stopPropagation()
560
+
561
+ event$.next({
562
+ type: 'grid',
563
+ eventName: 'mouseout',
564
+ pluginName,
565
+ highlightTarget: data.highlightTarget,
566
+ datum,
567
+ series: data.SeriesDataMap.get(datum.seriesLabel)!,
568
+ seriesIndex: datum.seriesIndex,
569
+ seriesLabel: datum.seriesLabel,
570
+ groups: data.GroupDataMap.get(datum.groupLabel)!,
571
+ groupIndex: datum.groupIndex,
572
+ groupLabel: datum.groupLabel,
573
+ event,
574
+ data: data.computedData
575
+ })
576
+ })
577
+ .on('click', (event, datum) => {
578
+ event.stopPropagation()
579
+
580
+ event$.next({
581
+ type: 'grid',
582
+ eventName: 'click',
583
+ pluginName,
584
+ highlightTarget: data.highlightTarget,
585
+ datum,
586
+ series: data.SeriesDataMap.get(datum.seriesLabel)!,
587
+ seriesIndex: datum.seriesIndex,
588
+ seriesLabel: datum.seriesLabel,
589
+ groups: data.GroupDataMap.get(datum.groupLabel)!,
590
+ groupIndex: datum.groupIndex,
591
+ groupLabel: datum.groupLabel,
592
+ event,
593
+ data: data.computedData
594
+ })
595
+ })
596
+
597
+ pathSelection$.next(pathSelection)
598
+ })
599
+
600
+ // const datumList$ = computedData$.pipe(
601
+ // takeUntil(destroy$),
602
+ // map(d => d.flat())
603
+ // )
604
+ // const highlight$ = highlightObservable({ datumList$, fullChartParams$, event$: event$ })
605
+ const highlightSubscription = gridHighlight$.subscribe()
606
+
607
+ combineLatest({
608
+ pathSelection: pathSelection$,
609
+ highlight: gridHighlight$,
610
+ fullChartParams: fullChartParams$
611
+ }).pipe(
612
+ takeUntil(destroy$),
613
+ switchMap(async d => d)
614
+ ).subscribe(data => {
615
+ highlight({
616
+ selection: data.pathSelection,
617
+ ids: data.highlight,
618
+ fullChartParams: data.fullChartParams
619
+ })
620
+ })
621
+
622
+ return () => {
623
+ destroy$.next(undefined)
624
+ highlightSubscription.unsubscribe()
625
+ }
626
+ }
@@ -26,6 +26,14 @@ export interface BaseLegendParams {
26
26
  // highlightEvent: boolean
27
27
  }
28
28
 
29
+ interface BaseLegendContext {
30
+ rootSelection: d3.Selection<any, unknown, any, unknown>
31
+ seriesLabels$: Observable<string[]>
32
+ fullParams$: Observable<BaseLegendParams>
33
+ layout$: Observable<Layout>
34
+ fullChartParams$: Observable<ChartParams>
35
+ }
36
+
29
37
  // 第1層 - 定位的容器
30
38
  interface RootPosition {
31
39
  x:number
@@ -66,18 +74,12 @@ interface LegendItem {
66
74
  // listRectRadius: number
67
75
  }
68
76
 
69
- export const createBaseLegend: BasePluginFn = (pluginName: string, {
77
+ export const createBaseLegend: BasePluginFn<BaseLegendContext> = (pluginName: string, {
70
78
  rootSelection,
71
79
  seriesLabels$,
72
80
  fullParams$,
73
81
  layout$,
74
82
  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
83
  }) => {
82
84
 
83
85
  const rootPositionClassName = getClassName(pluginName, 'root-position')