@orbcharts/plugins-basic 3.0.0-alpha.67 → 3.0.0-alpha.69

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. package/LICENSE +200 -200
  2. package/dist/orbcharts-plugins-basic.es.js +3436 -3358
  3. package/dist/orbcharts-plugins-basic.umd.js +14 -13
  4. package/dist/src/utils/d3Graphics.d.ts +10 -0
  5. package/package.json +42 -42
  6. package/src/base/BaseBarStack.ts +779 -779
  7. package/src/base/BaseBars.ts +764 -764
  8. package/src/base/BaseBarsTriangle.ts +672 -672
  9. package/src/base/BaseDots.ts +513 -513
  10. package/src/base/BaseGroupAxis.ts +675 -652
  11. package/src/base/BaseLegend.ts +642 -642
  12. package/src/base/BaseLineAreas.ts +628 -628
  13. package/src/base/BaseLines.ts +704 -704
  14. package/src/base/BaseValueAxis.ts +578 -578
  15. package/src/base/types.ts +2 -2
  16. package/src/grid/defaults.ts +128 -128
  17. package/src/grid/gridObservables.ts +543 -543
  18. package/src/grid/index.ts +15 -15
  19. package/src/grid/plugins/BarStack.ts +43 -43
  20. package/src/grid/plugins/Bars.ts +44 -44
  21. package/src/grid/plugins/BarsPN.ts +41 -41
  22. package/src/grid/plugins/BarsTriangle.ts +42 -42
  23. package/src/grid/plugins/Dots.ts +37 -37
  24. package/src/grid/plugins/GridLegend.ts +59 -59
  25. package/src/grid/plugins/GroupAux.ts +1014 -991
  26. package/src/grid/plugins/GroupAxis.ts +36 -36
  27. package/src/grid/plugins/LineAreas.ts +40 -40
  28. package/src/grid/plugins/Lines.ts +40 -40
  29. package/src/grid/plugins/ScalingArea.ts +174 -174
  30. package/src/grid/plugins/ValueAxis.ts +36 -36
  31. package/src/grid/plugins/ValueStackAxis.ts +38 -38
  32. package/src/grid/types.ts +123 -123
  33. package/src/index.ts +9 -9
  34. package/src/multiGrid/defaults.ts +158 -158
  35. package/src/multiGrid/index.ts +13 -13
  36. package/src/multiGrid/multiGridObservables.ts +49 -49
  37. package/src/multiGrid/plugins/MultiBarStack.ts +78 -78
  38. package/src/multiGrid/plugins/MultiBars.ts +77 -77
  39. package/src/multiGrid/plugins/MultiBarsTriangle.ts +77 -77
  40. package/src/multiGrid/plugins/MultiDots.ts +65 -65
  41. package/src/multiGrid/plugins/MultiGridLegend.ts +89 -89
  42. package/src/multiGrid/plugins/MultiGroupAxis.ts +70 -70
  43. package/src/multiGrid/plugins/MultiLineAreas.ts +77 -77
  44. package/src/multiGrid/plugins/MultiLines.ts +77 -77
  45. package/src/multiGrid/plugins/MultiValueAxis.ts +69 -69
  46. package/src/multiGrid/plugins/MultiValueStackAxis.ts +69 -69
  47. package/src/multiGrid/plugins/OverlappingValueAxes.ts +170 -170
  48. package/src/multiGrid/plugins/OverlappingValueStackAxes.ts +169 -169
  49. package/src/multiGrid/types.ts +72 -72
  50. package/src/noneData/defaults.ts +102 -102
  51. package/src/noneData/index.ts +3 -3
  52. package/src/noneData/plugins/Container.ts +10 -10
  53. package/src/noneData/plugins/Tooltip.ts +327 -327
  54. package/src/noneData/types.ts +26 -26
  55. package/src/series/defaults.ts +149 -149
  56. package/src/series/index.ts +9 -9
  57. package/src/series/plugins/Bubbles.ts +545 -545
  58. package/src/series/plugins/Pie.ts +584 -584
  59. package/src/series/plugins/PieEventTexts.ts +262 -262
  60. package/src/series/plugins/PieLabels.ts +604 -598
  61. package/src/series/plugins/Rose.ts +481 -481
  62. package/src/series/plugins/RoseLabels.ts +571 -565
  63. package/src/series/plugins/SeriesLegend.ts +59 -59
  64. package/src/series/seriesObservables.ts +145 -145
  65. package/src/series/seriesUtils.ts +51 -51
  66. package/src/series/types.ts +87 -87
  67. package/src/tree/defaults.ts +23 -23
  68. package/src/tree/index.ts +3 -3
  69. package/src/tree/plugins/TreeLegend.ts +59 -59
  70. package/src/tree/plugins/TreeMap.ts +305 -305
  71. package/src/tree/types.ts +23 -23
  72. package/src/utils/commonUtils.ts +21 -21
  73. package/src/utils/d3Graphics.ts +174 -124
  74. package/src/utils/d3Utils.ts +73 -73
  75. package/src/utils/observables.ts +14 -14
  76. package/src/utils/orbchartsUtils.ts +100 -100
  77. package/tsconfig.base.json +13 -13
  78. package/tsconfig.json +2 -2
  79. package/vite.config.js +22 -22
@@ -1,629 +1,629 @@
1
- import * as d3 from 'd3'
2
- import {
3
- combineLatest,
4
- map,
5
- filter,
6
- switchMap,
7
- takeUntil,
8
- distinctUntilChanged,
9
- Observable,
10
- Subject } from 'rxjs'
11
- import type { BasePluginFn } from './types'
12
- import type {
13
- ComputedDatumGrid,
14
- ComputedDataGrid,
15
- ComputedLayoutDatumGrid,
16
- ComputedLayoutDataGrid,
17
- DataFormatterGrid,
18
- EventGrid,
19
- GridContainerPosition,
20
- ChartParams,
21
- Layout,
22
- TransformData } from '@orbcharts/core'
23
- import { createAxisLinearScale } from '@orbcharts/core'
24
- import { getD3TransitionEase } from '../utils/d3Utils'
25
- import { getClassName, getUniID, getMinAndMaxValue } from '../utils/orbchartsUtils'
26
- import { gridGroupPositionFnObservable } from '../grid/gridObservables'
27
- import { gridSelectionsObservable } from '../grid/gridObservables'
28
-
29
- export interface BaseLineAreasParams {
30
- lineCurve: string
31
- // lineWidth: number
32
- linearGradientOpacity: [number, number]
33
- }
34
-
35
- interface BaseLineAreasContext {
36
- selection: d3.Selection<any, unknown, any, unknown>
37
- computedData$: Observable<ComputedDataGrid>
38
- computedLayoutData$: Observable<ComputedLayoutDataGrid>
39
- visibleComputedData$: Observable<ComputedDatumGrid[][]>
40
- visibleComputedLayoutData$: Observable<ComputedLayoutDataGrid>
41
- seriesLabels$: Observable<string[]>
42
- SeriesDataMap$: Observable<Map<string, ComputedDatumGrid[]>>
43
- GroupDataMap$: Observable<Map<string, ComputedDatumGrid[]>>
44
- fullDataFormatter$: Observable<DataFormatterGrid>
45
- fullParams$: Observable<BaseLineAreasParams>
46
- fullChartParams$: Observable<ChartParams>
47
- gridAxesTransform$: Observable<TransformData>
48
- gridGraphicTransform$: Observable<TransformData>
49
- gridAxesSize$: Observable<{
50
- width: number;
51
- height: number;
52
- }>
53
- gridHighlight$: Observable<ComputedDatumGrid[]>
54
- gridContainerPosition$: Observable<GridContainerPosition[]>
55
- allContainerPosition$: Observable<GridContainerPosition[]>
56
- layout$: Observable<Layout>
57
- event$: Subject<EventGrid>
58
- }
59
-
60
- type ClipPathDatum = {
61
- id: string;
62
- // x: number;
63
- // y: number;
64
- width: number;
65
- height: number;
66
- }
67
-
68
- // const pluginName = 'Lines'
69
- // const pathClassName = getClassName(pluginName, 'path')
70
-
71
-
72
- function createAreaPath (lineCurve: string = 'curveLinear', valueAxisStart: number): d3.Line<ComputedLayoutDatumGrid> {
73
- return d3.area<ComputedLayoutDatumGrid>()
74
- .x((d) => d.axisX)
75
- .y0(d => valueAxisStart)
76
- .y1((d) => d.axisY)
77
- .curve((d3 as any)[lineCurve])
78
-
79
- }
80
-
81
- // 依無值的資料分段
82
- function makeSegmentData (data: ComputedLayoutDatumGrid[]): ComputedLayoutDatumGrid[][] {
83
- let segmentData: ComputedLayoutDatumGrid[][] = [[]]
84
-
85
- let currentIndex = 0
86
- for (let i in data) {
87
- if (data[i].visible == false || data[i].value === undefined || data[i].value === null) {
88
- // 換下一段的 index
89
- if (segmentData[currentIndex].length) {
90
- currentIndex ++
91
- segmentData[currentIndex] = []
92
- }
93
- continue
94
- }
95
- segmentData[currentIndex].push(data[i])
96
- }
97
-
98
- return segmentData
99
- }
100
-
101
-
102
- function renderLineAreas ({ selection, pathClassName, segmentData, areaPath, linearGradientIds, params }: {
103
- selection: d3.Selection<SVGGElement, unknown, any, unknown>
104
- pathClassName: string
105
- segmentData: ComputedLayoutDatumGrid[][]
106
- areaPath: d3.Line<ComputedLayoutDatumGrid>
107
- linearGradientIds: string[]
108
- params: BaseLineAreasParams
109
- }): d3.Selection<SVGPathElement, ComputedLayoutDatumGrid[], any, any> {
110
- // if (!data[0]) {
111
- // return undefined
112
- // }
113
-
114
- const lineAreas = selection
115
- .selectAll<SVGPathElement, ComputedLayoutDatumGrid[]>('path')
116
- .data(segmentData, (d, i) => d.length ? `${d[0].id}_${d[d.length - 1].id}` : i) // 以線段起迄id結合為線段id
117
- .join(
118
- enter => {
119
- return enter
120
- .append<SVGPathElement>('path')
121
- .classed(pathClassName, true)
122
- .attr("fill","none")
123
- // .attr('pointer-events', 'visibleStroke') // 只對線條產生事件
124
- .style('vector-effect', 'non-scaling-stroke')
125
- .style('cursor', 'pointer')
126
- },
127
- update => update,
128
- exit => exit.remove()
129
- )
130
- // .attr("stroke-width", params.lineWidth)
131
- // .attr("stroke", (d, i) => d[0] && d[0].color)
132
- .attr("fill", (d, i) => d[0] ? `url(#${linearGradientIds[d[0].seriesIndex]})` : '')
133
- .attr("d", (d) => {
134
- return areaPath(d)
135
- })
136
-
137
- return lineAreas
138
- }
139
-
140
- function highlightLineAreas ({ selection, seriesLabel, fullChartParams }: {
141
- selection: d3.Selection<any, string, any, any>
142
- seriesLabel: string | null
143
- fullChartParams: ChartParams
144
- }) {
145
- selection.interrupt('highlight')
146
- if (!seriesLabel) {
147
- // remove highlight
148
- selection
149
- .transition('highlight')
150
- .duration(200)
151
- .style('opacity', 1)
152
- return
153
- }
154
-
155
- selection
156
- .each((currentSeriesLabel, i, n) => {
157
- // const currentSeriesLabel = d[0] ? d[0].seriesLabel : ''
158
-
159
- if (currentSeriesLabel === seriesLabel) {
160
- d3.select(n[i])
161
- .style('opacity', 1)
162
- } else {
163
- d3.select(n[i])
164
- .style('opacity', fullChartParams.styles.unhighlightedOpacity)
165
- }
166
- })
167
- }
168
-
169
- function renderLinearGradient ({ defsSelection, computedData, linearGradientIds, params }: {
170
- defsSelection: d3.Selection<SVGDefsElement, any, any, any>
171
- computedData: ComputedDataGrid
172
- linearGradientIds: string[]
173
- params: BaseLineAreasParams
174
- }) {
175
- defsSelection!
176
- .selectAll<SVGLinearGradientElement, ComputedDatumGrid>('linearGradient')
177
- .data(computedData ?? [])
178
- .join(
179
- enter => {
180
- return enter
181
- .append('linearGradient')
182
- .attr('x1', '0%')
183
- .attr('x2', '0%')
184
- .attr('y1', '100%')
185
- .attr('y2', '0%')
186
- .attr('spreadMethod', 'pad')
187
- },
188
- update => update,
189
- exit => exit.remove()
190
- )
191
- .attr('id', (d, i) => {
192
- return d[0] ? linearGradientIds[d[0].seriesIndex] : ''
193
- })
194
- .html((d, i) => {
195
- const color = d[0] ? d[0].color : ''
196
- return `
197
- <stop offset="0%" stop-color="${color}" stop-opacity="${params.linearGradientOpacity[0]}"/>
198
- <stop offset="100%" stop-color="${color}" stop-opacity="${params.linearGradientOpacity[1]}"/>
199
- `
200
- })
201
-
202
- }
203
-
204
- function renderClipPath ({ defsSelection, clipPathData, transitionDuration, transitionEase }: {
205
- defsSelection: d3.Selection<SVGDefsElement, any, any, any>
206
- clipPathData: ClipPathDatum[]
207
- transitionDuration: number
208
- transitionEase: string
209
- }) {
210
- const clipPath = defsSelection
211
- .selectAll<SVGClipPathElement, Layout>('clipPath')
212
- .data(clipPathData)
213
- .join(
214
- enter => {
215
- return enter
216
- .append('clipPath')
217
- },
218
- update => update,
219
- exit => exit.remove()
220
- )
221
- .attr('id', d => d.id)
222
- .each((d, i, g) => {
223
- const rect = d3.select(g[i])
224
- .selectAll<SVGRectElement, typeof d>('rect')
225
- .data([d])
226
- .join(
227
- enter => {
228
- const enterSelection = enter
229
- .append('rect')
230
- enterSelection
231
- .transition()
232
- .duration(transitionDuration)
233
- .ease(getD3TransitionEase(transitionEase))
234
- // .delay(100) // @Q@ 不知為何如果沒加 delay位置會有點跑掉
235
- .tween('tween', (_d, _i, _g) => {
236
- return (t) => {
237
- const transitionWidth = _d.width * t
238
-
239
- enterSelection
240
- .attr('x', 0)
241
- .attr('y', 0)
242
- .attr('width', _d => transitionWidth)
243
- .attr('height', _d => _d.height)
244
- }
245
- })
246
- return enterSelection
247
- },
248
- update => {
249
- return update
250
- .attr('x', 0)
251
- .attr('y', 0)
252
- .attr('width', _d => _d.width)
253
- .attr('height', _d => _d.height)
254
- },
255
- exit => exit.remove()
256
- )
257
- })
258
-
259
- }
260
-
261
- export const createBaseLineAreas: BasePluginFn<BaseLineAreasContext> = (pluginName: string, {
262
- selection,
263
- computedData$,
264
- computedLayoutData$,
265
- visibleComputedData$,
266
- visibleComputedLayoutData$,
267
- seriesLabels$,
268
- SeriesDataMap$,
269
- GroupDataMap$,
270
- fullParams$,
271
- fullDataFormatter$,
272
- fullChartParams$,
273
- gridAxesTransform$,
274
- gridGraphicTransform$,
275
- gridAxesSize$,
276
- gridHighlight$,
277
- gridContainerPosition$,
278
- layout$,
279
- event$
280
- }) => {
281
-
282
- const destroy$ = new Subject()
283
-
284
- const clipPathID = getUniID(pluginName, 'clipPath-box')
285
- const pathClassName = getClassName(pluginName, 'path')
286
-
287
- const {
288
- seriesSelection$,
289
- axesSelection$,
290
- defsSelection$,
291
- graphicGSelection$
292
- } = gridSelectionsObservable({
293
- selection,
294
- pluginName,
295
- clipPathID,
296
- seriesLabels$,
297
- gridContainerPosition$,
298
- gridAxesTransform$,
299
- gridGraphicTransform$
300
- })
301
-
302
- // valueAxis 的起始座標
303
- const valueAxisStart$: Observable<number> = gridGraphicTransform$.pipe(
304
- takeUntil(destroy$),
305
- map(data => {
306
- // 抵消掉外層的變型
307
- return - data.translate[1] / data.scale[1]
308
- })
309
- )
310
-
311
- const areaPath$: Observable<d3.Line<ComputedLayoutDatumGrid>> = new Observable(subscriber => {
312
- const paramsSubscription = combineLatest({
313
- fullParams: fullParams$,
314
- valueAxisStart: valueAxisStart$
315
- }).pipe(
316
- takeUntil(destroy$)
317
- )
318
- .subscribe(d => {
319
- const areaPath = createAreaPath(d.fullParams.lineCurve, d.valueAxisStart)
320
- subscriber.next(areaPath)
321
- })
322
- return () => {
323
- paramsSubscription.unsubscribe()
324
- }
325
- })
326
-
327
- // // 顯示範圍內的series labels
328
- // const seriesLabels$: Observable<string[]> = new Observable(subscriber => {
329
- // computedData$.pipe(
330
- // takeUntil(destroy$),
331
- // switchMap(async (d) => d),
332
- // ).subscribe(data => {
333
- // const labels = data[0] && data[0][0]
334
- // ? data.map(d => d[0].seriesLabel)
335
- // : []
336
- // subscriber.next(labels)
337
- // })
338
- // })
339
-
340
- // const axisSize$ = gridAxisSizeObservable({
341
- // fullDataFormatter$,
342
- // computedLayout$
343
- // })
344
-
345
- const transitionDuration$ = fullChartParams$
346
- .pipe(
347
- map(d => d.transitionDuration),
348
- distinctUntilChanged()
349
- )
350
-
351
- const transitionEase$ = fullChartParams$
352
- .pipe(
353
- map(d => d.transitionEase),
354
- distinctUntilChanged()
355
- )
356
-
357
- const clipPathSubscription = combineLatest({
358
- defsSelection: defsSelection$,
359
- seriesLabels: seriesLabels$,
360
- axisSize: gridAxesSize$,
361
- transitionDuration: transitionDuration$,
362
- transitionEase: transitionEase$
363
- }).pipe(
364
- takeUntil(destroy$),
365
- switchMap(async (d) => d),
366
- ).subscribe(data => {
367
- // 外層的遮罩
368
- const clipPathBox = [{
369
- id: clipPathID,
370
- width: data.axisSize.width,
371
- height: data.axisSize.height
372
- }]
373
- // 各別線條的遮罩(各別動畫)
374
- const clipPathData = clipPathBox.concat(
375
- data.seriesLabels.map(d => {
376
- return {
377
- id: `orbcharts__clipPath_${d}`,
378
- width: data.axisSize.width,
379
- height: data.axisSize.height
380
- }
381
- })
382
- )
383
- renderClipPath({
384
- defsSelection: data.defsSelection,
385
- clipPathData,
386
- transitionDuration: data.transitionDuration,
387
- transitionEase: data.transitionEase
388
- })
389
- })
390
-
391
- // const SeriesDataMap$ = computedData$.pipe(
392
- // map(d => makeGridSeriesDataMap(d))
393
- // )
394
-
395
- // const GroupDataMap$ = computedData$.pipe(
396
- // map(d => makeGridGroupDataMap(d))
397
- // )
398
-
399
- const DataMap$ = computedData$.pipe(
400
- map(d => {
401
- const DataMap: Map<string, ComputedDatumGrid> = new Map()
402
- d.flat().forEach(_d => DataMap.set(_d.id, _d))
403
- return DataMap
404
- })
405
- )
406
-
407
- // 取得事件座標的group資料
408
- const gridGroupPositionFn$ = gridGroupPositionFnObservable({
409
- fullDataFormatter$,
410
- gridAxesSize$: gridAxesSize$,
411
- computedData$: computedData$,
412
- fullChartParams$: fullChartParams$,
413
- gridContainerPosition$: gridContainerPosition$,
414
- layout$: layout$
415
- })
416
-
417
- const highlightTarget$ = fullChartParams$.pipe(
418
- takeUntil(destroy$),
419
- map(d => d.highlightTarget),
420
- distinctUntilChanged()
421
- )
422
-
423
- const linearGradientIds$ = seriesLabels$.pipe(
424
- takeUntil(destroy$),
425
- map(d => d.map((d, i) => {
426
- return getUniID(pluginName, `lineargradient-${d}`)
427
- }))
428
- )
429
-
430
- const pathSelectionArr$ = combineLatest({
431
- graphicGSelection: graphicGSelection$,
432
- defsSelection: defsSelection$,
433
- visibleComputedLayoutData: visibleComputedLayoutData$,
434
- linearGradientIds: linearGradientIds$,
435
- areaPath: areaPath$,
436
- params: fullParams$,
437
- }).pipe(
438
- takeUntil(destroy$),
439
- switchMap(async (d) => d),
440
- map(data => {
441
- // const updateGraphic = data.graphicGSelection
442
- // .selectAll<SVGGElement, number>('g')
443
- // .data(data.seriesLabels, (d, i) => d)
444
- // const enterGraphic = updateGraphic.enter()
445
- // .append('g')
446
- // .classed(graphicClassName, true)
447
- // updateGraphic.exit().remove()
448
- // const graphicSelection = updateGraphic.merge(enterGraphic)
449
- // .attr('clip-path', (d, i) => `url(#orbcharts__clipPath_${d})`)
450
- let pathSelectionArr: d3.Selection<SVGPathElement, ComputedLayoutDatumGrid[], any, any>[] = []
451
-
452
- // 繪圖
453
- data.graphicGSelection.each((d, i, all) => {
454
- // 將資料分段
455
- const segmentData = makeSegmentData(data.visibleComputedLayoutData[i] ?? [])
456
-
457
- pathSelectionArr[i] = renderLineAreas({
458
- selection: d3.select(all[i]),
459
- pathClassName,
460
- areaPath: data.areaPath,
461
- segmentData: segmentData,
462
- linearGradientIds: data.linearGradientIds,
463
- params: data.params
464
- })
465
- renderLinearGradient({
466
- defsSelection: data.defsSelection,
467
- computedData: data.visibleComputedLayoutData,
468
- linearGradientIds: data.linearGradientIds,
469
- params: data.params
470
- })
471
- })
472
-
473
- return pathSelectionArr
474
- })
475
- )
476
-
477
- combineLatest({
478
- pathSelectionArr: pathSelectionArr$,
479
- computedData: computedData$,
480
- SeriesDataMap: SeriesDataMap$,
481
- GroupDataMap: GroupDataMap$,
482
- highlightTarget: highlightTarget$,
483
- gridGroupPositionFn: gridGroupPositionFn$,
484
- }).pipe(
485
- takeUntil(destroy$),
486
- switchMap(async (d) => d),
487
- ).subscribe(data => {
488
- data.pathSelectionArr.forEach(pathSelection => {
489
- pathSelection
490
- .on('mouseover', (event, datum) => {
491
- // event.stopPropagation()
492
-
493
- const seriesLabel = datum[0] ? datum[0].seriesLabel : ''
494
- const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
495
- const groupData = data.GroupDataMap.get(groupLabel)!
496
- const targetDatum = groupData.find(d => d.seriesLabel === seriesLabel)
497
- const _datum = targetDatum ?? datum[0]
498
-
499
- event$.next({
500
- type: 'grid',
501
- eventName: 'mouseover',
502
- pluginName,
503
- highlightTarget: data.highlightTarget,
504
- datum: _datum,
505
- gridIndex: _datum.gridIndex,
506
- series: data.SeriesDataMap.get(_datum.seriesLabel)!,
507
- seriesIndex: _datum.seriesIndex,
508
- seriesLabel: _datum.seriesLabel,
509
- groups: data.GroupDataMap.get(_datum.groupLabel)!,
510
- groupIndex: _datum.groupIndex,
511
- groupLabel: _datum.groupLabel,
512
- event,
513
- data: data.computedData
514
- })
515
- })
516
- .on('mousemove', (event, datum) => {
517
- // event.stopPropagation()
518
-
519
- const seriesLabel = datum[0] ? datum[0].seriesLabel : ''
520
- const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
521
- const groupData = data.GroupDataMap.get(groupLabel)!
522
- const targetDatum = groupData.find(d => d.seriesLabel === seriesLabel)
523
- const _datum = targetDatum ?? datum[0]
524
-
525
- event$.next({
526
- type: 'grid',
527
- eventName: 'mousemove',
528
- pluginName,
529
- highlightTarget: data.highlightTarget,
530
- datum: _datum,
531
- gridIndex: _datum.gridIndex,
532
- series: data.SeriesDataMap.get(_datum.seriesLabel)!,
533
- seriesIndex: _datum.seriesIndex,
534
- seriesLabel: _datum.seriesLabel,
535
- groups: data.GroupDataMap.get(_datum.groupLabel)!,
536
- groupIndex: _datum.groupIndex,
537
- groupLabel: _datum.groupLabel,
538
- event,
539
- data: data.computedData
540
- })
541
- })
542
- .on('mouseout', (event, datum) => {
543
- // event.stopPropagation()
544
-
545
- const seriesLabel = datum[0] ? datum[0].seriesLabel : ''
546
- const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
547
- const groupData = data.GroupDataMap.get(groupLabel)!
548
- const targetDatum = groupData.find(d => d.seriesLabel === seriesLabel)
549
- const _datum = targetDatum ?? datum[0]
550
-
551
- event$.next({
552
- type: 'grid',
553
- eventName: 'mouseout',
554
- pluginName,
555
- highlightTarget: data.highlightTarget,
556
- datum: _datum,
557
- gridIndex: _datum.gridIndex,
558
- series: data.SeriesDataMap.get(_datum.seriesLabel)!,
559
- seriesIndex: _datum.seriesIndex,
560
- seriesLabel: _datum.seriesLabel,
561
- groups: data.GroupDataMap.get(_datum.groupLabel)!,
562
- groupIndex: _datum.groupIndex,
563
- groupLabel: _datum.groupLabel,
564
- event,
565
- data: data.computedData
566
- })
567
- })
568
- .on('click', (event, datum) => {
569
- // event.stopPropagation()
570
-
571
- const seriesLabel = datum[0] ? datum[0].seriesLabel : ''
572
- const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
573
- const groupData = data.GroupDataMap.get(groupLabel)!
574
- const targetDatum = groupData.find(d => d.seriesLabel === seriesLabel)
575
- const _datum = targetDatum ?? datum[0]
576
-
577
- event$.next({
578
- type: 'grid',
579
- eventName: 'click',
580
- pluginName,
581
- highlightTarget: data.highlightTarget,
582
- datum: _datum,
583
- gridIndex: _datum.gridIndex,
584
- series: data.SeriesDataMap.get(_datum.seriesLabel)!,
585
- seriesIndex: _datum.seriesIndex,
586
- seriesLabel: _datum.seriesLabel,
587
- groups: data.GroupDataMap.get(_datum.groupLabel)!,
588
- groupIndex: _datum.groupIndex,
589
- groupLabel: _datum.groupLabel,
590
- event,
591
- data: data.computedData
592
- })
593
- })
594
- })
595
- })
596
-
597
- // const datumList$ = computedData$.pipe(
598
- // takeUntil(destroy$),
599
- // map(d => d.flat())
600
- // )
601
- // const highlight$ = highlightObservable({ datumList$, fullChartParams$, event$: store.event$ })
602
- // const highlightSubscription = gridHighlight$.subscribe()
603
-
604
- fullChartParams$.pipe(
605
- takeUntil(destroy$),
606
- filter(d => d.highlightTarget === 'series'),
607
- switchMap(d => combineLatest({
608
- graphicGSelection: graphicGSelection$,
609
- gridHighlight: gridHighlight$,
610
- DataMap: DataMap$,
611
- fullChartParams: fullChartParams$
612
- }).pipe(
613
- takeUntil(destroy$),
614
- switchMap(async d => d)
615
- ))
616
- ).subscribe(data => {
617
- const seriesLabel = data.gridHighlight[0] ? data.gridHighlight[0].seriesLabel : null
618
- highlightLineAreas({
619
- selection: data.graphicGSelection,
620
- seriesLabel,
621
- fullChartParams: data.fullChartParams
622
- })
623
- })
624
-
625
- return () => {
626
- destroy$.next(undefined)
627
- // highlightSubscription.unsubscribe()
628
- }
1
+ import * as d3 from 'd3'
2
+ import {
3
+ combineLatest,
4
+ map,
5
+ filter,
6
+ switchMap,
7
+ takeUntil,
8
+ distinctUntilChanged,
9
+ Observable,
10
+ Subject } from 'rxjs'
11
+ import type { BasePluginFn } from './types'
12
+ import type {
13
+ ComputedDatumGrid,
14
+ ComputedDataGrid,
15
+ ComputedLayoutDatumGrid,
16
+ ComputedLayoutDataGrid,
17
+ DataFormatterGrid,
18
+ EventGrid,
19
+ GridContainerPosition,
20
+ ChartParams,
21
+ Layout,
22
+ TransformData } from '@orbcharts/core'
23
+ import { createAxisLinearScale } from '@orbcharts/core'
24
+ import { getD3TransitionEase } from '../utils/d3Utils'
25
+ import { getClassName, getUniID, getMinAndMaxValue } from '../utils/orbchartsUtils'
26
+ import { gridGroupPositionFnObservable } from '../grid/gridObservables'
27
+ import { gridSelectionsObservable } from '../grid/gridObservables'
28
+
29
+ export interface BaseLineAreasParams {
30
+ lineCurve: string
31
+ // lineWidth: number
32
+ linearGradientOpacity: [number, number]
33
+ }
34
+
35
+ interface BaseLineAreasContext {
36
+ selection: d3.Selection<any, unknown, any, unknown>
37
+ computedData$: Observable<ComputedDataGrid>
38
+ computedLayoutData$: Observable<ComputedLayoutDataGrid>
39
+ visibleComputedData$: Observable<ComputedDatumGrid[][]>
40
+ visibleComputedLayoutData$: Observable<ComputedLayoutDataGrid>
41
+ seriesLabels$: Observable<string[]>
42
+ SeriesDataMap$: Observable<Map<string, ComputedDatumGrid[]>>
43
+ GroupDataMap$: Observable<Map<string, ComputedDatumGrid[]>>
44
+ fullDataFormatter$: Observable<DataFormatterGrid>
45
+ fullParams$: Observable<BaseLineAreasParams>
46
+ fullChartParams$: Observable<ChartParams>
47
+ gridAxesTransform$: Observable<TransformData>
48
+ gridGraphicTransform$: Observable<TransformData>
49
+ gridAxesSize$: Observable<{
50
+ width: number;
51
+ height: number;
52
+ }>
53
+ gridHighlight$: Observable<ComputedDatumGrid[]>
54
+ gridContainerPosition$: Observable<GridContainerPosition[]>
55
+ allContainerPosition$: Observable<GridContainerPosition[]>
56
+ layout$: Observable<Layout>
57
+ event$: Subject<EventGrid>
58
+ }
59
+
60
+ type ClipPathDatum = {
61
+ id: string;
62
+ // x: number;
63
+ // y: number;
64
+ width: number;
65
+ height: number;
66
+ }
67
+
68
+ // const pluginName = 'Lines'
69
+ // const pathClassName = getClassName(pluginName, 'path')
70
+
71
+
72
+ function createAreaPath (lineCurve: string = 'curveLinear', valueAxisStart: number): d3.Line<ComputedLayoutDatumGrid> {
73
+ return d3.area<ComputedLayoutDatumGrid>()
74
+ .x((d) => d.axisX)
75
+ .y0(d => valueAxisStart)
76
+ .y1((d) => d.axisY)
77
+ .curve((d3 as any)[lineCurve])
78
+
79
+ }
80
+
81
+ // 依無值的資料分段
82
+ function makeSegmentData (data: ComputedLayoutDatumGrid[]): ComputedLayoutDatumGrid[][] {
83
+ let segmentData: ComputedLayoutDatumGrid[][] = [[]]
84
+
85
+ let currentIndex = 0
86
+ for (let i in data) {
87
+ if (data[i].visible == false || data[i].value === undefined || data[i].value === null) {
88
+ // 換下一段的 index
89
+ if (segmentData[currentIndex].length) {
90
+ currentIndex ++
91
+ segmentData[currentIndex] = []
92
+ }
93
+ continue
94
+ }
95
+ segmentData[currentIndex].push(data[i])
96
+ }
97
+
98
+ return segmentData
99
+ }
100
+
101
+
102
+ function renderLineAreas ({ selection, pathClassName, segmentData, areaPath, linearGradientIds, params }: {
103
+ selection: d3.Selection<SVGGElement, unknown, any, unknown>
104
+ pathClassName: string
105
+ segmentData: ComputedLayoutDatumGrid[][]
106
+ areaPath: d3.Line<ComputedLayoutDatumGrid>
107
+ linearGradientIds: string[]
108
+ params: BaseLineAreasParams
109
+ }): d3.Selection<SVGPathElement, ComputedLayoutDatumGrid[], any, any> {
110
+ // if (!data[0]) {
111
+ // return undefined
112
+ // }
113
+
114
+ const lineAreas = selection
115
+ .selectAll<SVGPathElement, ComputedLayoutDatumGrid[]>('path')
116
+ .data(segmentData, (d, i) => d.length ? `${d[0].id}_${d[d.length - 1].id}` : i) // 以線段起迄id結合為線段id
117
+ .join(
118
+ enter => {
119
+ return enter
120
+ .append<SVGPathElement>('path')
121
+ .classed(pathClassName, true)
122
+ .attr("fill","none")
123
+ // .attr('pointer-events', 'visibleStroke') // 只對線條產生事件
124
+ .style('vector-effect', 'non-scaling-stroke')
125
+ .style('cursor', 'pointer')
126
+ },
127
+ update => update,
128
+ exit => exit.remove()
129
+ )
130
+ // .attr("stroke-width", params.lineWidth)
131
+ // .attr("stroke", (d, i) => d[0] && d[0].color)
132
+ .attr("fill", (d, i) => d[0] ? `url(#${linearGradientIds[d[0].seriesIndex]})` : '')
133
+ .attr("d", (d) => {
134
+ return areaPath(d)
135
+ })
136
+
137
+ return lineAreas
138
+ }
139
+
140
+ function highlightLineAreas ({ selection, seriesLabel, fullChartParams }: {
141
+ selection: d3.Selection<any, string, any, any>
142
+ seriesLabel: string | null
143
+ fullChartParams: ChartParams
144
+ }) {
145
+ selection.interrupt('highlight')
146
+ if (!seriesLabel) {
147
+ // remove highlight
148
+ selection
149
+ .transition('highlight')
150
+ .duration(200)
151
+ .style('opacity', 1)
152
+ return
153
+ }
154
+
155
+ selection
156
+ .each((currentSeriesLabel, i, n) => {
157
+ // const currentSeriesLabel = d[0] ? d[0].seriesLabel : ''
158
+
159
+ if (currentSeriesLabel === seriesLabel) {
160
+ d3.select(n[i])
161
+ .style('opacity', 1)
162
+ } else {
163
+ d3.select(n[i])
164
+ .style('opacity', fullChartParams.styles.unhighlightedOpacity)
165
+ }
166
+ })
167
+ }
168
+
169
+ function renderLinearGradient ({ defsSelection, computedData, linearGradientIds, params }: {
170
+ defsSelection: d3.Selection<SVGDefsElement, any, any, any>
171
+ computedData: ComputedDataGrid
172
+ linearGradientIds: string[]
173
+ params: BaseLineAreasParams
174
+ }) {
175
+ defsSelection!
176
+ .selectAll<SVGLinearGradientElement, ComputedDatumGrid>('linearGradient')
177
+ .data(computedData ?? [])
178
+ .join(
179
+ enter => {
180
+ return enter
181
+ .append('linearGradient')
182
+ .attr('x1', '0%')
183
+ .attr('x2', '0%')
184
+ .attr('y1', '100%')
185
+ .attr('y2', '0%')
186
+ .attr('spreadMethod', 'pad')
187
+ },
188
+ update => update,
189
+ exit => exit.remove()
190
+ )
191
+ .attr('id', (d, i) => {
192
+ return d[0] ? linearGradientIds[d[0].seriesIndex] : ''
193
+ })
194
+ .html((d, i) => {
195
+ const color = d[0] ? d[0].color : ''
196
+ return `
197
+ <stop offset="0%" stop-color="${color}" stop-opacity="${params.linearGradientOpacity[0]}"/>
198
+ <stop offset="100%" stop-color="${color}" stop-opacity="${params.linearGradientOpacity[1]}"/>
199
+ `
200
+ })
201
+
202
+ }
203
+
204
+ function renderClipPath ({ defsSelection, clipPathData, transitionDuration, transitionEase }: {
205
+ defsSelection: d3.Selection<SVGDefsElement, any, any, any>
206
+ clipPathData: ClipPathDatum[]
207
+ transitionDuration: number
208
+ transitionEase: string
209
+ }) {
210
+ const clipPath = defsSelection
211
+ .selectAll<SVGClipPathElement, Layout>('clipPath')
212
+ .data(clipPathData)
213
+ .join(
214
+ enter => {
215
+ return enter
216
+ .append('clipPath')
217
+ },
218
+ update => update,
219
+ exit => exit.remove()
220
+ )
221
+ .attr('id', d => d.id)
222
+ .each((d, i, g) => {
223
+ const rect = d3.select(g[i])
224
+ .selectAll<SVGRectElement, typeof d>('rect')
225
+ .data([d])
226
+ .join(
227
+ enter => {
228
+ const enterSelection = enter
229
+ .append('rect')
230
+ enterSelection
231
+ .transition()
232
+ .duration(transitionDuration)
233
+ .ease(getD3TransitionEase(transitionEase))
234
+ // .delay(100) // @Q@ 不知為何如果沒加 delay位置會有點跑掉
235
+ .tween('tween', (_d, _i, _g) => {
236
+ return (t) => {
237
+ const transitionWidth = _d.width * t
238
+
239
+ enterSelection
240
+ .attr('x', 0)
241
+ .attr('y', 0)
242
+ .attr('width', _d => transitionWidth)
243
+ .attr('height', _d => _d.height)
244
+ }
245
+ })
246
+ return enterSelection
247
+ },
248
+ update => {
249
+ return update
250
+ .attr('x', 0)
251
+ .attr('y', 0)
252
+ .attr('width', _d => _d.width)
253
+ .attr('height', _d => _d.height)
254
+ },
255
+ exit => exit.remove()
256
+ )
257
+ })
258
+
259
+ }
260
+
261
+ export const createBaseLineAreas: BasePluginFn<BaseLineAreasContext> = (pluginName: string, {
262
+ selection,
263
+ computedData$,
264
+ computedLayoutData$,
265
+ visibleComputedData$,
266
+ visibleComputedLayoutData$,
267
+ seriesLabels$,
268
+ SeriesDataMap$,
269
+ GroupDataMap$,
270
+ fullParams$,
271
+ fullDataFormatter$,
272
+ fullChartParams$,
273
+ gridAxesTransform$,
274
+ gridGraphicTransform$,
275
+ gridAxesSize$,
276
+ gridHighlight$,
277
+ gridContainerPosition$,
278
+ layout$,
279
+ event$
280
+ }) => {
281
+
282
+ const destroy$ = new Subject()
283
+
284
+ const clipPathID = getUniID(pluginName, 'clipPath-box')
285
+ const pathClassName = getClassName(pluginName, 'path')
286
+
287
+ const {
288
+ seriesSelection$,
289
+ axesSelection$,
290
+ defsSelection$,
291
+ graphicGSelection$
292
+ } = gridSelectionsObservable({
293
+ selection,
294
+ pluginName,
295
+ clipPathID,
296
+ seriesLabels$,
297
+ gridContainerPosition$,
298
+ gridAxesTransform$,
299
+ gridGraphicTransform$
300
+ })
301
+
302
+ // valueAxis 的起始座標
303
+ const valueAxisStart$: Observable<number> = gridGraphicTransform$.pipe(
304
+ takeUntil(destroy$),
305
+ map(data => {
306
+ // 抵消掉外層的變型
307
+ return - data.translate[1] / data.scale[1]
308
+ })
309
+ )
310
+
311
+ const areaPath$: Observable<d3.Line<ComputedLayoutDatumGrid>> = new Observable(subscriber => {
312
+ const paramsSubscription = combineLatest({
313
+ fullParams: fullParams$,
314
+ valueAxisStart: valueAxisStart$
315
+ }).pipe(
316
+ takeUntil(destroy$)
317
+ )
318
+ .subscribe(d => {
319
+ const areaPath = createAreaPath(d.fullParams.lineCurve, d.valueAxisStart)
320
+ subscriber.next(areaPath)
321
+ })
322
+ return () => {
323
+ paramsSubscription.unsubscribe()
324
+ }
325
+ })
326
+
327
+ // // 顯示範圍內的series labels
328
+ // const seriesLabels$: Observable<string[]> = new Observable(subscriber => {
329
+ // computedData$.pipe(
330
+ // takeUntil(destroy$),
331
+ // switchMap(async (d) => d),
332
+ // ).subscribe(data => {
333
+ // const labels = data[0] && data[0][0]
334
+ // ? data.map(d => d[0].seriesLabel)
335
+ // : []
336
+ // subscriber.next(labels)
337
+ // })
338
+ // })
339
+
340
+ // const axisSize$ = gridAxisSizeObservable({
341
+ // fullDataFormatter$,
342
+ // computedLayout$
343
+ // })
344
+
345
+ const transitionDuration$ = fullChartParams$
346
+ .pipe(
347
+ map(d => d.transitionDuration),
348
+ distinctUntilChanged()
349
+ )
350
+
351
+ const transitionEase$ = fullChartParams$
352
+ .pipe(
353
+ map(d => d.transitionEase),
354
+ distinctUntilChanged()
355
+ )
356
+
357
+ const clipPathSubscription = combineLatest({
358
+ defsSelection: defsSelection$,
359
+ seriesLabels: seriesLabels$,
360
+ axisSize: gridAxesSize$,
361
+ transitionDuration: transitionDuration$,
362
+ transitionEase: transitionEase$
363
+ }).pipe(
364
+ takeUntil(destroy$),
365
+ switchMap(async (d) => d),
366
+ ).subscribe(data => {
367
+ // 外層的遮罩
368
+ const clipPathBox = [{
369
+ id: clipPathID,
370
+ width: data.axisSize.width,
371
+ height: data.axisSize.height
372
+ }]
373
+ // 各別線條的遮罩(各別動畫)
374
+ const clipPathData = clipPathBox.concat(
375
+ data.seriesLabels.map(d => {
376
+ return {
377
+ id: `orbcharts__clipPath_${d}`,
378
+ width: data.axisSize.width,
379
+ height: data.axisSize.height
380
+ }
381
+ })
382
+ )
383
+ renderClipPath({
384
+ defsSelection: data.defsSelection,
385
+ clipPathData,
386
+ transitionDuration: data.transitionDuration,
387
+ transitionEase: data.transitionEase
388
+ })
389
+ })
390
+
391
+ // const SeriesDataMap$ = computedData$.pipe(
392
+ // map(d => makeGridSeriesDataMap(d))
393
+ // )
394
+
395
+ // const GroupDataMap$ = computedData$.pipe(
396
+ // map(d => makeGridGroupDataMap(d))
397
+ // )
398
+
399
+ const DataMap$ = computedData$.pipe(
400
+ map(d => {
401
+ const DataMap: Map<string, ComputedDatumGrid> = new Map()
402
+ d.flat().forEach(_d => DataMap.set(_d.id, _d))
403
+ return DataMap
404
+ })
405
+ )
406
+
407
+ // 取得事件座標的group資料
408
+ const gridGroupPositionFn$ = gridGroupPositionFnObservable({
409
+ fullDataFormatter$,
410
+ gridAxesSize$: gridAxesSize$,
411
+ computedData$: computedData$,
412
+ fullChartParams$: fullChartParams$,
413
+ gridContainerPosition$: gridContainerPosition$,
414
+ layout$: layout$
415
+ })
416
+
417
+ const highlightTarget$ = fullChartParams$.pipe(
418
+ takeUntil(destroy$),
419
+ map(d => d.highlightTarget),
420
+ distinctUntilChanged()
421
+ )
422
+
423
+ const linearGradientIds$ = seriesLabels$.pipe(
424
+ takeUntil(destroy$),
425
+ map(d => d.map((d, i) => {
426
+ return getUniID(pluginName, `lineargradient-${d}`)
427
+ }))
428
+ )
429
+
430
+ const pathSelectionArr$ = combineLatest({
431
+ graphicGSelection: graphicGSelection$,
432
+ defsSelection: defsSelection$,
433
+ visibleComputedLayoutData: visibleComputedLayoutData$,
434
+ linearGradientIds: linearGradientIds$,
435
+ areaPath: areaPath$,
436
+ params: fullParams$,
437
+ }).pipe(
438
+ takeUntil(destroy$),
439
+ switchMap(async (d) => d),
440
+ map(data => {
441
+ // const updateGraphic = data.graphicGSelection
442
+ // .selectAll<SVGGElement, number>('g')
443
+ // .data(data.seriesLabels, (d, i) => d)
444
+ // const enterGraphic = updateGraphic.enter()
445
+ // .append('g')
446
+ // .classed(graphicClassName, true)
447
+ // updateGraphic.exit().remove()
448
+ // const graphicSelection = updateGraphic.merge(enterGraphic)
449
+ // .attr('clip-path', (d, i) => `url(#orbcharts__clipPath_${d})`)
450
+ let pathSelectionArr: d3.Selection<SVGPathElement, ComputedLayoutDatumGrid[], any, any>[] = []
451
+
452
+ // 繪圖
453
+ data.graphicGSelection.each((d, i, all) => {
454
+ // 將資料分段
455
+ const segmentData = makeSegmentData(data.visibleComputedLayoutData[i] ?? [])
456
+
457
+ pathSelectionArr[i] = renderLineAreas({
458
+ selection: d3.select(all[i]),
459
+ pathClassName,
460
+ areaPath: data.areaPath,
461
+ segmentData: segmentData,
462
+ linearGradientIds: data.linearGradientIds,
463
+ params: data.params
464
+ })
465
+ renderLinearGradient({
466
+ defsSelection: data.defsSelection,
467
+ computedData: data.visibleComputedLayoutData,
468
+ linearGradientIds: data.linearGradientIds,
469
+ params: data.params
470
+ })
471
+ })
472
+
473
+ return pathSelectionArr
474
+ })
475
+ )
476
+
477
+ combineLatest({
478
+ pathSelectionArr: pathSelectionArr$,
479
+ computedData: computedData$,
480
+ SeriesDataMap: SeriesDataMap$,
481
+ GroupDataMap: GroupDataMap$,
482
+ highlightTarget: highlightTarget$,
483
+ gridGroupPositionFn: gridGroupPositionFn$,
484
+ }).pipe(
485
+ takeUntil(destroy$),
486
+ switchMap(async (d) => d),
487
+ ).subscribe(data => {
488
+ data.pathSelectionArr.forEach(pathSelection => {
489
+ pathSelection
490
+ .on('mouseover', (event, datum) => {
491
+ // event.stopPropagation()
492
+
493
+ const seriesLabel = datum[0] ? datum[0].seriesLabel : ''
494
+ const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
495
+ const groupData = data.GroupDataMap.get(groupLabel)!
496
+ const targetDatum = groupData.find(d => d.seriesLabel === seriesLabel)
497
+ const _datum = targetDatum ?? datum[0]
498
+
499
+ event$.next({
500
+ type: 'grid',
501
+ eventName: 'mouseover',
502
+ pluginName,
503
+ highlightTarget: data.highlightTarget,
504
+ datum: _datum,
505
+ gridIndex: _datum.gridIndex,
506
+ series: data.SeriesDataMap.get(_datum.seriesLabel)!,
507
+ seriesIndex: _datum.seriesIndex,
508
+ seriesLabel: _datum.seriesLabel,
509
+ groups: data.GroupDataMap.get(_datum.groupLabel)!,
510
+ groupIndex: _datum.groupIndex,
511
+ groupLabel: _datum.groupLabel,
512
+ event,
513
+ data: data.computedData
514
+ })
515
+ })
516
+ .on('mousemove', (event, datum) => {
517
+ // event.stopPropagation()
518
+
519
+ const seriesLabel = datum[0] ? datum[0].seriesLabel : ''
520
+ const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
521
+ const groupData = data.GroupDataMap.get(groupLabel)!
522
+ const targetDatum = groupData.find(d => d.seriesLabel === seriesLabel)
523
+ const _datum = targetDatum ?? datum[0]
524
+
525
+ event$.next({
526
+ type: 'grid',
527
+ eventName: 'mousemove',
528
+ pluginName,
529
+ highlightTarget: data.highlightTarget,
530
+ datum: _datum,
531
+ gridIndex: _datum.gridIndex,
532
+ series: data.SeriesDataMap.get(_datum.seriesLabel)!,
533
+ seriesIndex: _datum.seriesIndex,
534
+ seriesLabel: _datum.seriesLabel,
535
+ groups: data.GroupDataMap.get(_datum.groupLabel)!,
536
+ groupIndex: _datum.groupIndex,
537
+ groupLabel: _datum.groupLabel,
538
+ event,
539
+ data: data.computedData
540
+ })
541
+ })
542
+ .on('mouseout', (event, datum) => {
543
+ // event.stopPropagation()
544
+
545
+ const seriesLabel = datum[0] ? datum[0].seriesLabel : ''
546
+ const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
547
+ const groupData = data.GroupDataMap.get(groupLabel)!
548
+ const targetDatum = groupData.find(d => d.seriesLabel === seriesLabel)
549
+ const _datum = targetDatum ?? datum[0]
550
+
551
+ event$.next({
552
+ type: 'grid',
553
+ eventName: 'mouseout',
554
+ pluginName,
555
+ highlightTarget: data.highlightTarget,
556
+ datum: _datum,
557
+ gridIndex: _datum.gridIndex,
558
+ series: data.SeriesDataMap.get(_datum.seriesLabel)!,
559
+ seriesIndex: _datum.seriesIndex,
560
+ seriesLabel: _datum.seriesLabel,
561
+ groups: data.GroupDataMap.get(_datum.groupLabel)!,
562
+ groupIndex: _datum.groupIndex,
563
+ groupLabel: _datum.groupLabel,
564
+ event,
565
+ data: data.computedData
566
+ })
567
+ })
568
+ .on('click', (event, datum) => {
569
+ // event.stopPropagation()
570
+
571
+ const seriesLabel = datum[0] ? datum[0].seriesLabel : ''
572
+ const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
573
+ const groupData = data.GroupDataMap.get(groupLabel)!
574
+ const targetDatum = groupData.find(d => d.seriesLabel === seriesLabel)
575
+ const _datum = targetDatum ?? datum[0]
576
+
577
+ event$.next({
578
+ type: 'grid',
579
+ eventName: 'click',
580
+ pluginName,
581
+ highlightTarget: data.highlightTarget,
582
+ datum: _datum,
583
+ gridIndex: _datum.gridIndex,
584
+ series: data.SeriesDataMap.get(_datum.seriesLabel)!,
585
+ seriesIndex: _datum.seriesIndex,
586
+ seriesLabel: _datum.seriesLabel,
587
+ groups: data.GroupDataMap.get(_datum.groupLabel)!,
588
+ groupIndex: _datum.groupIndex,
589
+ groupLabel: _datum.groupLabel,
590
+ event,
591
+ data: data.computedData
592
+ })
593
+ })
594
+ })
595
+ })
596
+
597
+ // const datumList$ = computedData$.pipe(
598
+ // takeUntil(destroy$),
599
+ // map(d => d.flat())
600
+ // )
601
+ // const highlight$ = highlightObservable({ datumList$, fullChartParams$, event$: store.event$ })
602
+ // const highlightSubscription = gridHighlight$.subscribe()
603
+
604
+ fullChartParams$.pipe(
605
+ takeUntil(destroy$),
606
+ filter(d => d.highlightTarget === 'series'),
607
+ switchMap(d => combineLatest({
608
+ graphicGSelection: graphicGSelection$,
609
+ gridHighlight: gridHighlight$,
610
+ DataMap: DataMap$,
611
+ fullChartParams: fullChartParams$
612
+ }).pipe(
613
+ takeUntil(destroy$),
614
+ switchMap(async d => d)
615
+ ))
616
+ ).subscribe(data => {
617
+ const seriesLabel = data.gridHighlight[0] ? data.gridHighlight[0].seriesLabel : null
618
+ highlightLineAreas({
619
+ selection: data.graphicGSelection,
620
+ seriesLabel,
621
+ fullChartParams: data.fullChartParams
622
+ })
623
+ })
624
+
625
+ return () => {
626
+ destroy$.next(undefined)
627
+ // highlightSubscription.unsubscribe()
628
+ }
629
629
  }