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

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.
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')