@orbcharts/plugins-basic 3.0.0-alpha.61 → 3.0.0-alpha.62

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. package/LICENSE +200 -200
  2. package/dist/orbcharts-plugins-basic.es.js +1 -1
  3. package/dist/orbcharts-plugins-basic.umd.js +1 -1
  4. package/package.json +42 -42
  5. package/src/base/BaseBarStack.ts +778 -778
  6. package/src/base/BaseBars.ts +764 -764
  7. package/src/base/BaseBarsTriangle.ts +672 -672
  8. package/src/base/BaseDots.ts +513 -513
  9. package/src/base/BaseGroupAxis.ts +558 -558
  10. package/src/base/BaseLegend.ts +641 -641
  11. package/src/base/BaseLineAreas.ts +628 -628
  12. package/src/base/BaseLines.ts +704 -704
  13. package/src/base/BaseValueAxis.ts +480 -478
  14. package/src/base/types.ts +2 -2
  15. package/src/grid/defaults.ts +128 -128
  16. package/src/grid/gridObservables.ts +541 -541
  17. package/src/grid/index.ts +15 -15
  18. package/src/grid/plugins/BarStack.ts +43 -43
  19. package/src/grid/plugins/Bars.ts +44 -44
  20. package/src/grid/plugins/BarsPN.ts +41 -41
  21. package/src/grid/plugins/BarsTriangle.ts +42 -42
  22. package/src/grid/plugins/Dots.ts +37 -37
  23. package/src/grid/plugins/GridLegend.ts +59 -59
  24. package/src/grid/plugins/GroupAux.ts +976 -976
  25. package/src/grid/plugins/GroupAxis.ts +35 -35
  26. package/src/grid/plugins/LineAreas.ts +40 -40
  27. package/src/grid/plugins/Lines.ts +40 -40
  28. package/src/grid/plugins/ScalingArea.ts +173 -173
  29. package/src/grid/plugins/ValueAxis.ts +36 -36
  30. package/src/grid/plugins/ValueStackAxis.ts +38 -38
  31. package/src/grid/types.ts +123 -123
  32. package/src/index.ts +9 -9
  33. package/src/multiGrid/defaults.ts +158 -158
  34. package/src/multiGrid/index.ts +13 -13
  35. package/src/multiGrid/multiGridObservables.ts +49 -49
  36. package/src/multiGrid/plugins/MultiBarStack.ts +78 -78
  37. package/src/multiGrid/plugins/MultiBars.ts +77 -77
  38. package/src/multiGrid/plugins/MultiBarsTriangle.ts +77 -77
  39. package/src/multiGrid/plugins/MultiDots.ts +65 -65
  40. package/src/multiGrid/plugins/MultiGridLegend.ts +89 -89
  41. package/src/multiGrid/plugins/MultiGroupAxis.ts +69 -69
  42. package/src/multiGrid/plugins/MultiLineAreas.ts +77 -77
  43. package/src/multiGrid/plugins/MultiLines.ts +77 -77
  44. package/src/multiGrid/plugins/MultiValueAxis.ts +69 -69
  45. package/src/multiGrid/plugins/MultiValueStackAxis.ts +69 -69
  46. package/src/multiGrid/plugins/OverlappingValueAxes.ts +167 -167
  47. package/src/multiGrid/plugins/OverlappingValueStackAxes.ts +168 -168
  48. package/src/multiGrid/types.ts +72 -72
  49. package/src/noneData/defaults.ts +102 -102
  50. package/src/noneData/index.ts +3 -3
  51. package/src/noneData/plugins/Container.ts +10 -10
  52. package/src/noneData/plugins/Tooltip.ts +310 -310
  53. package/src/noneData/types.ts +26 -26
  54. package/src/series/defaults.ts +144 -144
  55. package/src/series/index.ts +9 -9
  56. package/src/series/plugins/Bubbles.ts +545 -545
  57. package/src/series/plugins/Pie.ts +576 -576
  58. package/src/series/plugins/PieEventTexts.ts +262 -262
  59. package/src/series/plugins/PieLabels.ts +304 -304
  60. package/src/series/plugins/Rose.ts +472 -472
  61. package/src/series/plugins/RoseLabels.ts +362 -362
  62. package/src/series/plugins/SeriesLegend.ts +59 -59
  63. package/src/series/seriesObservables.ts +145 -145
  64. package/src/series/seriesUtils.ts +51 -51
  65. package/src/series/types.ts +83 -83
  66. package/src/tree/defaults.ts +23 -23
  67. package/src/tree/index.ts +3 -3
  68. package/src/tree/plugins/TreeLegend.ts +59 -59
  69. package/src/tree/plugins/TreeMap.ts +305 -305
  70. package/src/tree/types.ts +23 -23
  71. package/src/utils/commonUtils.ts +21 -21
  72. package/src/utils/d3Graphics.ts +124 -124
  73. package/src/utils/d3Utils.ts +73 -73
  74. package/src/utils/observables.ts +14 -14
  75. package/src/utils/orbchartsUtils.ts +100 -100
  76. package/tsconfig.base.json +13 -13
  77. package/tsconfig.json +2 -2
  78. package/vite.config.js +22 -22
@@ -1,977 +1,977 @@
1
- import * as d3 from 'd3'
2
- import {
3
- // of,
4
- iif,
5
- EMPTY,
6
- combineLatest,
7
- switchMap,
8
- map,
9
- filter,
10
- first,
11
- takeUntil,
12
- distinctUntilChanged,
13
- shareReplay,
14
- Subject,
15
- Observable } from 'rxjs'
16
- import {
17
- defineGridPlugin } from '@orbcharts/core'
18
- import type {
19
- TransformData,
20
- DataFormatterGrid,
21
- ChartParams } from '@orbcharts/core'
22
- import { DEFAULT_GROUP_AREA_PARAMS } from '../defaults'
23
- import { parseTickFormatValue } from '../../utils/d3Utils'
24
- import { measureTextWidth } from '../../utils/commonUtils'
25
- import { getColor, getClassName, getUniID } from '../../utils/orbchartsUtils'
26
- import { d3EventObservable } from '../../utils/observables'
27
- import { gridGroupPosition } from '../gridObservables'
28
- import { createAxisPointScale } from '@orbcharts/core'
29
- import type { GroupAuxParams } from '../types'
30
- import { gridSelectionsObservable } from '../gridObservables'
31
-
32
- interface LineDatum {
33
- id: string
34
- x1: number
35
- x2: number
36
- y1: number
37
- y2: number
38
- }
39
-
40
- interface LabelDatum {
41
- id: string
42
- text: string
43
- x: number
44
- y: number
45
- }
46
-
47
- const pluginName = 'GroupAux'
48
- const labelClassName = getClassName(pluginName, 'label-box')
49
-
50
- function createLineData ({ groupLabel, axisX, axisHeight, fullParams }: {
51
- groupLabel: string
52
- axisX: number
53
- axisHeight: number
54
- fullParams: GroupAuxParams
55
- }): LineDatum[] {
56
- return fullParams.showLine && groupLabel
57
- ? [{
58
- id: groupLabel,
59
- x1: axisX,
60
- x2: axisX,
61
- y1: 0,
62
- y2: axisHeight
63
- }]
64
- : []
65
- }
66
-
67
- function renderLine ({ selection, pluginName, lineData, fullParams, fullChartParams }: {
68
- selection: d3.Selection<any, string, any, unknown>
69
- pluginName: string
70
- lineData: LineDatum[]
71
- fullParams: GroupAuxParams
72
- fullChartParams: ChartParams
73
- }) {
74
- const gClassName = getClassName(pluginName, 'auxline')
75
- const update = selection
76
- .selectAll<SVGLineElement, LineDatum>(`line.${gClassName}`)
77
- .data(lineData)
78
- const enter = update
79
- .enter()
80
- .append('line')
81
- .classed(gClassName, true)
82
- // .style('stroke', '#E4E7ED')
83
- .style('stroke', d => getColor(fullParams.lineColorType, fullChartParams))
84
- .style('stroke-width', 1)
85
- .style('stroke-dasharray', fullParams.lineDashArray ?? 'none')
86
- .style('pointer-events', 'none')
87
- // .attr('opacity', 0)
88
- const auxLineSelection = update.merge(enter)
89
- // .attr('opacity', (d) => {
90
- // return d.active == true ? 1 : 0
91
- // })
92
- update.exit().remove()
93
- enter
94
- .attr('x1', d => d.x1)
95
- .attr('y1', d => d.y1)
96
- .attr('x2', d => d.x2)
97
- .attr('y2', d => d.y2)
98
- update
99
- .transition()
100
- .duration(50)
101
- .attr('x1', d => d.x1)
102
- .attr('y1', d => d.y1)
103
- .attr('x2', d => d.x2)
104
- .attr('y2', d => d.y2)
105
-
106
- return auxLineSelection
107
- }
108
-
109
- function removeLine (selection: d3.Selection<any, string, any, unknown>) {
110
- const update = selection
111
- .selectAll<SVGLineElement, LineDatum>('line')
112
- .data([])
113
-
114
- update.exit().remove()
115
- }
116
-
117
- function createLabelData ({ groupLabel, axisX, fullParams }: {
118
- groupLabel: string
119
- axisX: number
120
- fullParams: GroupAuxParams
121
- }) {
122
- return fullParams.showLabel && groupLabel
123
- ? [{
124
- id: groupLabel,
125
- x: axisX,
126
- y: - fullParams.labelPadding,
127
- text: parseTickFormatValue(groupLabel, fullParams.labelTextFormat)
128
- }]
129
- : []
130
- }
131
-
132
- function renderLabel ({ selection, labelData, fullParams, fullDataFormatter, fullChartParams, labelTransform, textSizePx }: {
133
- selection: d3.Selection<any, string, any, unknown>
134
- labelData: LabelDatum[]
135
- fullParams: GroupAuxParams
136
- fullDataFormatter: DataFormatterGrid
137
- fullChartParams: ChartParams
138
- // gridAxesReverseTransformValue: string
139
- labelTransform: string
140
- textSizePx: number
141
- }) {
142
- const rectHeight = textSizePx + 4
143
-
144
- const gUpdate = selection
145
- .selectAll<SVGGElement, LabelDatum>(`g.${labelClassName}`)
146
- .data(labelData)
147
- const gEnter = gUpdate
148
- .enter()
149
- .append('g')
150
- .classed(labelClassName, true)
151
- .style('cursor', 'pointer')
152
- const axisLabelSelection = gEnter.merge(gUpdate)
153
- gEnter
154
- .attr("transform", (d, i) => {
155
- return `translate(${d.x}, ${d.y})`
156
- })
157
- gUpdate
158
- .transition()
159
- .duration(50)
160
- .attr("transform", (d, i) => {
161
- return `translate(${d.x}, ${d.y})`
162
- })
163
- gUpdate.exit().remove()
164
-
165
- axisLabelSelection.each((datum, i, n) => {
166
- const rectWidth = measureTextWidth(datum.text, textSizePx) + 12
167
- // -- label偏移位置 --
168
- let rectX = - rectWidth / 2
169
- let rectY = -2
170
- if (fullDataFormatter.grid.groupAxis.position === 'bottom') {
171
- rectX = fullParams.labelRotate
172
- ? - rectWidth
173
- : - rectWidth / 2
174
- rectY = 2
175
- } else if (fullDataFormatter.grid.groupAxis.position === 'left') {
176
- rectX = - rectWidth + 2
177
- rectY = - rectHeight / 2
178
- } else if (fullDataFormatter.grid.groupAxis.position === 'right') {
179
- rectX = - 2
180
- rectY = - rectHeight / 2
181
- } else if (fullDataFormatter.grid.groupAxis.position === 'top') {
182
- rectX = fullParams.labelRotate
183
- ? - rectWidth
184
- : - rectWidth / 2
185
- rectY = - rectHeight + 2
186
- }
187
-
188
- const rectUpdate = d3.select(n[i])
189
- .selectAll<SVGRectElement, LabelDatum>('rect')
190
- .data([datum])
191
- const rectEnter = rectUpdate
192
- .enter()
193
- .append('rect')
194
- .attr('height', `${rectHeight}px`)
195
- .attr('fill', d => getColor(fullParams.labelColorType, fullChartParams))
196
- .attr('x', rectX)
197
- .attr('y', rectY)
198
- .attr('rx', 5)
199
- .attr('ry', 5)
200
- .style('cursor', 'pointer')
201
- // .style('pointer-events', 'none')
202
- const rect = rectUpdate.merge(rectEnter)
203
- .attr('width', d => `${rectWidth}px`)
204
- .style('transform', labelTransform)
205
- rectUpdate.exit().remove()
206
-
207
- const textUpdate = d3.select(n[i])
208
- .selectAll<SVGTextElement, LabelDatum>('text')
209
- .data([datum])
210
- const textEnter = textUpdate
211
- .enter()
212
- .append('text')
213
- .style('dominant-baseline', 'hanging')
214
- .style('cursor', 'pointer')
215
- // .style('pointer-events', 'none')
216
- const text = textUpdate.merge(textEnter)
217
- .text(d => d.text)
218
- .style('transform', labelTransform)
219
- .attr('fill', d => getColor(fullParams.labelTextColorType, fullChartParams))
220
- .attr('font-size', fullChartParams.styles.textSize)
221
- .attr('x', rectX + 6)
222
- .attr('y', rectY)
223
- textUpdate.exit().remove()
224
- })
225
-
226
- return axisLabelSelection
227
- }
228
-
229
- function removeLabel (selection: d3.Selection<any, string, any, unknown>) {
230
- const gUpdate = selection
231
- .selectAll<SVGGElement, LabelDatum>(`g.${labelClassName}`)
232
- .data([])
233
-
234
- gUpdate.exit().remove()
235
- }
236
-
237
- export const GroupAux = defineGridPlugin(pluginName, DEFAULT_GROUP_AREA_PARAMS)(({ selection, rootSelection, name, subject, observer }) => {
238
- const destroy$ = new Subject()
239
-
240
- let isLabelMouseover = false
241
-
242
- const rootRectSelection: d3.Selection<SVGRectElement, any, any, any> = rootSelection
243
- .insert('rect', 'g')
244
- .classed(getClassName(pluginName, 'rect'), true)
245
- .attr('opacity', 0)
246
-
247
- // const axisSelection: d3.Selection<SVGGElement, any, any, any> = selection
248
- // .append('g')
249
-
250
- const {
251
- seriesSelection$,
252
- axesSelection$,
253
- defsSelection$,
254
- graphicGSelection$
255
- } = gridSelectionsObservable({
256
- selection,
257
- pluginName,
258
- clipPathID: 'test',
259
- seriesLabels$: observer.seriesLabels$,
260
- gridContainerPosition$: observer.gridContainerPosition$,
261
- gridAxesTransform$: observer.gridAxesTransform$,
262
- gridGraphicTransform$: observer.gridGraphicTransform$
263
- })
264
-
265
- observer.layout$.pipe(
266
- takeUntil(destroy$),
267
- ).subscribe(d => {
268
- rootRectSelection
269
- .attr('width', d.rootWidth)
270
- .attr('height', d.rootHeight)
271
- })
272
-
273
- // observer.gridAxesTransform$
274
- // .pipe(
275
- // takeUntil(destroy$),
276
- // map(d => d.value),
277
- // distinctUntilChanged()
278
- // ).subscribe(d => {
279
- // axisSelection
280
- // .style('transform', d)
281
- // })
282
-
283
- // const visibleComputedData$ = observer.computedData$.pipe(
284
- // takeUntil(destroy$),
285
- // map(data => {
286
- // const visibleComputedData = data
287
- // .map(d => {
288
- // return d.filter(_d => {
289
- // return _d.visible == true
290
- // })
291
- // })
292
- // .filter(d => d.length)
293
- // // console.log('visibleComputedData', visibleComputedData)
294
- // return visibleComputedData
295
- // })
296
- // )
297
-
298
- // const SeriesDataMap$ = visibleComputedData$.pipe(
299
- // map(d => makeGridSeriesDataMap(d))
300
- // )
301
-
302
- // const GroupDataMap$ = visibleComputedData$.pipe(
303
- // map(d => makeGridGroupDataMap(d))
304
- // )
305
-
306
- // const contentTransform$: Observable<string> = new Observable(subscriber => {
307
- // combineLatest({
308
- // fullParams: observer.fullParams$,
309
- // gridAxesTransform: observer.gridAxesTransform$
310
- // }).pipe(
311
- // takeUntil(destroy$),
312
- // // 轉換後會退訂前一個未完成的訂閱事件,因此可以取到「同時間」最後一次的訂閱事件
313
- // switchMap(async (d) => d),
314
- // ).subscribe(data => {
315
-
316
- // const transformData = Object.assign({}, data.gridAxesTransform)
317
-
318
- // // const value = getAxesTransformValue({
319
- // // translate: [0, 0],
320
- // // scale: [transformData.scale[0] * -1, transformData.scale[1] * -1],
321
- // // rotate: transformData.rotate * -1,
322
- // // rotateX: transformData.rotateX * -1,
323
- // // rotateY: transformData.rotateY * -1
324
- // // })
325
-
326
- // subscriber.next(transformData.value)
327
- // })
328
- // })
329
- // const reverseTransform$: Observable<TransformData> = observer.gridAxesTransform$.pipe(
330
- // takeUntil(destroy$),
331
- // map(d => {
332
- // const translate: [number, number] = [d.translate[0] * -1, d.translate[1] * -1]
333
- // const scale: [number, number] = [d.scale[0] * -1, d.scale[1] * -1]
334
- // const rotate = d.rotate * -1
335
- // const rotateX = d.rotateX * -1
336
- // const rotateY = d.rotateY * -1
337
- // return {
338
- // translate,
339
- // scale,
340
- // rotate,
341
- // rotateX,
342
- // rotateY,
343
- // value: ''
344
- // }
345
- // }),
346
- // )
347
- // const contentTransform$ = combineLatest({
348
- // fullParams: observer.fullParams$,
349
- // reverseTransform: reverseTransform$
350
- // }).pipe(
351
- // takeUntil(destroy$),
352
- // switchMap(async data => {
353
- // const translate = [0, 0]
354
- // return `translate(${translate[0]}px, ${translate[1]}px) rotate(${data.reverseTransform.rotate}deg) rotateX(${data.reverseTransform.rotateX}deg) rotateY(${data.reverseTransform.rotateY}deg)`
355
- // }),
356
- // distinctUntilChanged()
357
- // )
358
-
359
- // const groupScale$: Observable<d3.ScalePoint<string>> = new Observable(subscriber => {
360
- // combineLatest({
361
- // fullDataFormatter: observer.fullDataFormatter$,
362
- // gridAxesSize: observer.gridAxesSize$,
363
- // computedData: observer.computedData$
364
- // }).pipe(
365
- // takeUntil(destroy$),
366
- // switchMap(async (d) => d),
367
- // ).subscribe(data => {
368
- // const groupMin = 0
369
- // const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
370
- // const groupScaleDomainMin = data.fullDataFormatter.grid.groupAxis.scaleDomain[0] === 'auto'
371
- // ? groupMin - data.fullDataFormatter.grid.groupAxis.scalePadding
372
- // : data.fullDataFormatter.grid.groupAxis.scaleDomain[0] as number - data.fullDataFormatter.grid.groupAxis.scalePadding
373
- // const groupScaleDomainMax = data.fullDataFormatter.grid.groupAxis.scaleDomain[1] === 'auto'
374
- // ? groupMax + data.fullDataFormatter.grid.groupAxis.scalePadding
375
- // : data.fullDataFormatter.grid.groupAxis.scaleDomain[1] as number + data.fullDataFormatter.grid.groupAxis.scalePadding
376
-
377
- // const groupingLength = data.computedData[0]
378
- // ? data.computedData[0].length
379
- // : 0
380
-
381
- // let _labels = data.fullDataFormatter.grid.seriesDirection === 'row'
382
- // // ? data.fullDataFormatter.grid.columnLabels
383
- // // : data.fullDataFormatter.grid.rowLabels
384
- // ? (data.computedData[0] ?? []).map(d => d.groupLabel)
385
- // : data.computedData.map(d => d[0].groupLabel)
386
-
387
- // const axisLabels = new Array(groupingLength).fill(0)
388
- // .map((d, i) => {
389
- // return _labels[i] != null
390
- // ? _labels[i]
391
- // : String(i) // 沒有label則用序列號填充
392
- // })
393
- // .filter((d, i) => {
394
- // return i >= groupScaleDomainMin && i <= groupScaleDomainMax
395
- // })
396
-
397
-
398
- // const padding = data.fullDataFormatter.grid.groupAxis.scalePadding
399
-
400
- // const groupScale = createAxisPointScale({
401
- // axisLabels,
402
- // axisWidth: data.gridAxesSize.width,
403
- // padding
404
- // })
405
-
406
- // subscriber.next(groupScale)
407
- // })
408
- // })
409
-
410
- const groupScaleDomain$ = combineLatest({
411
- fullDataFormatter: observer.fullDataFormatter$,
412
- gridAxesSize: observer.gridAxesSize$,
413
- computedData: observer.computedData$
414
- }).pipe(
415
- takeUntil(destroy$),
416
- switchMap(async (d) => d),
417
- map(data => {
418
- const groupMin = 0
419
- const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
420
- const groupScaleDomainMin = data.fullDataFormatter.grid.groupAxis.scaleDomain[0] === 'auto'
421
- ? groupMin - data.fullDataFormatter.grid.groupAxis.scalePadding
422
- : data.fullDataFormatter.grid.groupAxis.scaleDomain[0] as number - data.fullDataFormatter.grid.groupAxis.scalePadding
423
- const groupScaleDomainMax = data.fullDataFormatter.grid.groupAxis.scaleDomain[1] === 'auto'
424
- ? groupMax + data.fullDataFormatter.grid.groupAxis.scalePadding
425
- : data.fullDataFormatter.grid.groupAxis.scaleDomain[1] as number + data.fullDataFormatter.grid.groupAxis.scalePadding
426
-
427
- return [groupScaleDomainMin, groupScaleDomainMax]
428
- }),
429
- shareReplay(1)
430
- )
431
-
432
- const groupScale$ = combineLatest({
433
- groupScaleDomain: groupScaleDomain$,
434
- gridAxesSize: observer.gridAxesSize$
435
- }).pipe(
436
- takeUntil(destroy$),
437
- switchMap(async (d) => d),
438
- map(data => {
439
- const groupScale: d3.ScaleLinear<number, number> = d3.scaleLinear()
440
- .domain(data.groupScaleDomain)
441
- .range([0, data.gridAxesSize.width])
442
- return groupScale
443
- })
444
- )
445
-
446
- // // 取得事件座標的group資料
447
- // const gridGroupPositionFn$ = gridGroupPositionFnObservable({
448
- // fullDataFormatter$: observer.fullDataFormatter$,
449
- // gridAxesSize$: observer.gridAxesSize$,
450
- // computedData$: observer.computedData$,
451
- // fullChartParams$: observer.fullChartParams$,
452
- // })
453
-
454
- const highlightTarget$ = observer.fullChartParams$.pipe(
455
- takeUntil(destroy$),
456
- map(d => d.highlightTarget),
457
- distinctUntilChanged()
458
- )
459
-
460
- // combineLatest({
461
- // computedData: observer.computedData$,
462
- // gridAxesSize: observer.gridAxesSize$,
463
- // fullParams: observer.fullParams$,
464
- // fullChartParams: observer.fullChartParams$,
465
- // highlightTarget: highlightTarget$,
466
- // SeriesDataMap: observer.SeriesDataMap$,
467
- // GroupDataMap: observer.GroupDataMap$,
468
- // gridGroupPositionFn: gridGroupPositionFn$,
469
- // groupScale: groupScale$,
470
- // }).pipe(
471
- // takeUntil(destroy$),
472
- // switchMap(async (d) => d),
473
- // ).subscribe(data => {
474
-
475
- // // store.selection
476
- // rootSelection
477
- // .on('mouseover', (event, datum) => {
478
- // // event.stopPropagation()
479
-
480
- // const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
481
-
482
- // subject.event$.next({
483
- // type: 'grid',
484
- // pluginName: name,
485
- // eventName: 'mouseover',
486
- // highlightTarget: data.highlightTarget,
487
- // datum: null,
488
- // gridIndex: 0, // @Q@ 暫不處理
489
- // series: [],
490
- // seriesIndex: -1,
491
- // seriesLabel: '',
492
- // groups: data.GroupDataMap.get(groupLabel) ?? [],
493
- // // groups: [],
494
- // groupIndex,
495
- // groupLabel,
496
- // event,
497
- // data: data.computedData
498
- // })
499
- // })
500
- // .on('mousemove', (event, datum) => {
501
- // // event.stopPropagation()
502
-
503
- // const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
504
-
505
- // subject.event$.next({
506
- // type: 'grid',
507
- // pluginName: name,
508
- // eventName: 'mousemove',
509
- // highlightTarget: data.highlightTarget,
510
- // datum: null,
511
- // gridIndex: 0, // @Q@ 暫不處理
512
- // series: [],
513
- // seriesIndex: -1,
514
- // seriesLabel: '',
515
- // groups: data.GroupDataMap.get(groupLabel) ?? [],
516
- // // groups: [],
517
- // groupIndex,
518
- // groupLabel,
519
- // event,
520
- // data: data.computedData
521
- // })
522
- // })
523
- // .on('mouseout', (event, datum) => {
524
- // // event.stopPropagation()
525
-
526
- // const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
527
-
528
- // subject.event$.next({
529
- // type: 'grid',
530
- // pluginName: name,
531
- // eventName: 'mouseout',
532
- // highlightTarget: data.highlightTarget,
533
- // datum: null,
534
- // gridIndex: 0, // @Q@ 暫不處理
535
- // series: [],
536
- // seriesIndex: -1,
537
- // seriesLabel: '',
538
- // groups: data.GroupDataMap.get(groupLabel) ?? [],
539
- // // groups: [],
540
- // groupIndex,
541
- // groupLabel,
542
- // event,
543
- // data: data.computedData
544
- // })
545
- // })
546
- // .on('click', (event, datum) => {
547
- // event.stopPropagation()
548
-
549
- // const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
550
-
551
- // subject.event$.next({
552
- // type: 'grid',
553
- // pluginName: name,
554
- // eventName: 'click',
555
- // highlightTarget: data.highlightTarget,
556
- // datum: null,
557
- // gridIndex: 0, // @Q@ 暫不處理
558
- // series: [],
559
- // seriesIndex: -1,
560
- // seriesLabel: '',
561
- // // groups: data.GroupDataMap.get(groupLabel) ?? [],
562
- // groups: [],
563
- // groupIndex,
564
- // groupLabel,
565
- // event,
566
- // data: data.computedData
567
- // })
568
- // })
569
-
570
- // // barSelection$.next(barSelection!)
571
- // })
572
-
573
- const rootMousemove$: Observable<any> = d3EventObservable(rootSelection, 'mousemove').pipe(
574
- takeUntil(destroy$),
575
- )
576
-
577
-
578
- // const mousemoveGroupLabel$ = combineLatest({
579
- // rootMousemove: rootMousemove$,
580
- // gridGroupPositionFn: gridGroupPositionFn$,
581
- // }).pipe(
582
- // takeUntil(destroy$),
583
- // switchMap(async d => d),
584
- // map(data => {
585
- // const { groupIndex, groupLabel } = data.gridGroupPositionFn(data.rootMousemove)
586
- // return { groupIndex, groupLabel }
587
- // }),
588
- // shareReplay(1)
589
- // )
590
-
591
- const labelTransform$ = combineLatest({
592
- fullParams: observer.fullParams$,
593
- fullDataFormatter: observer.fullDataFormatter$,
594
- gridAxesReverseTransform: observer.gridAxesReverseTransform$,
595
- gridContainerPosition: observer.gridContainerPosition$
596
- }).pipe(
597
- takeUntil(destroy$),
598
- switchMap(async d => d),
599
- map(data => {
600
- const axisReverseTranslateValue = `translate(${data.gridAxesReverseTransform.translate[0]}px, ${data.gridAxesReverseTransform.translate[1]}px)`
601
- const axisReverseRotateValue = `rotate(${data.gridAxesReverseTransform.rotate}deg) rotateX(${data.gridAxesReverseTransform.rotateX}deg) rotateY(${data.gridAxesReverseTransform.rotateY}deg)`
602
- const containerScaleReverseScaleValue = `scale(${1 / data.gridContainerPosition[0].scale[0]}, ${1 / data.gridContainerPosition[0].scale[1]})`
603
- const tickTextRotateDeg = (data.fullDataFormatter.grid.groupAxis.position === 'left' && data.fullDataFormatter.grid.valueAxis.position === 'top')
604
- || (data.fullDataFormatter.grid.groupAxis.position === 'right' && data.fullDataFormatter.grid.valueAxis.position === 'bottom')
605
- ? data.fullParams.labelRotate + 180 // 修正文字倒轉
606
- : data.fullParams.labelRotate
607
- // ? 180 // 修正文字倒轉
608
- // : 0
609
-
610
- const textRotateValue = `rotate(${tickTextRotateDeg}deg)`
611
-
612
- // 必須按照順序(先抵消外層rotate,再抵消最外層scale,最後再做本身的rotate)
613
- return `${axisReverseTranslateValue} ${axisReverseRotateValue} ${containerScaleReverseScaleValue} ${textRotateValue}`
614
- }),
615
- distinctUntilChanged()
616
- )
617
-
618
- const columnAmount$ = observer.gridContainerPosition$.pipe(
619
- takeUntil(destroy$),
620
- map(gridContainerPosition => {
621
- const maxColumnIndex = gridContainerPosition.reduce((acc, current) => {
622
- return current.columnIndex > acc ? current.columnIndex : acc
623
- }, 0)
624
- return maxColumnIndex + 1
625
- }),
626
- distinctUntilChanged()
627
- )
628
-
629
- const rowAmount$ = observer.gridContainerPosition$.pipe(
630
- takeUntil(destroy$),
631
- map(gridContainerPosition => {
632
- const maxRowIndex = gridContainerPosition.reduce((acc, current) => {
633
- return current.rowIndex > acc ? current.rowIndex : acc
634
- }, 0)
635
- return maxRowIndex + 1
636
- }),
637
- distinctUntilChanged()
638
- )
639
-
640
- const gridGroupPosition$ = gridGroupPosition({
641
- rootSelection,
642
- fullDataFormatter$: observer.fullDataFormatter$,
643
- gridAxesSize$: observer.gridAxesSize$,
644
- computedData$: observer.computedData$,
645
- fullChartParams$: observer.fullChartParams$,
646
- gridContainerPosition$: observer.gridContainerPosition$,
647
- layout$: observer.layout$
648
- }).pipe(
649
- takeUntil(destroy$)
650
- )
651
-
652
- combineLatest({
653
- axesSelection: axesSelection$,
654
- columnAmount: columnAmount$,
655
- rowAmount: rowAmount$,
656
- layout: observer.layout$,
657
- rootMousemove: rootMousemove$,
658
- // gridGroupPositionFn: gridGroupPositionFn$,
659
- gridGroupPosition: gridGroupPosition$,
660
- computedData: observer.computedData$,
661
- groupScale: groupScale$,
662
- gridAxesSize: observer.gridAxesSize$,
663
- fullParams: observer.fullParams$,
664
- fullDataFormatter: observer.fullDataFormatter$,
665
- fullChartParams: observer.fullChartParams$,
666
- highlightTarget: highlightTarget$,
667
- // gridAxesReverseTransform: observer.gridAxesReverseTransform$,
668
- labelTransform: labelTransform$,
669
- GroupDataMap: observer.GroupDataMap$,
670
- textSizePx: observer.textSizePx$
671
- }).subscribe(data => {
672
- // // 由於event座標是基於底層的,但是container會有多欄,所以要重新計算
673
- // const eventData = {
674
- // offsetX: data.rootMousemove.offsetX * data.columnAmount % data.layout.rootWidth,
675
- // offsetY: data.rootMousemove.offsetY * data.rowAmount % data.layout.rootHeight
676
- // }
677
- // 依event的座標取得group資料
678
- const { groupIndex, groupLabel } = data.gridGroupPosition
679
- // console.log('gridGroupPosition', groupIndex, groupLabel)
680
- const axisX = data.groupScale(groupIndex) ?? 0
681
- // console.log('axisX', axisX)
682
- const lineData = createLineData({
683
- groupLabel: groupLabel,
684
- axisX,
685
- axisHeight: data.gridAxesSize.height,
686
- fullParams: data.fullParams,
687
- })
688
- // console.log('lineData', lineData)
689
- renderLine({
690
- // selection: axisSelection,
691
- selection: data.axesSelection,
692
- pluginName: name,
693
- lineData,
694
- fullParams: data.fullParams,
695
- fullChartParams: data.fullChartParams
696
- })
697
- const labelData = createLabelData({
698
- groupLabel: groupLabel,
699
- axisX,
700
- fullParams: data.fullParams
701
- })
702
- const labelSelection = renderLabel({
703
- // selection: axisSelection,
704
- selection: data.axesSelection,
705
- labelData,
706
- fullParams: data.fullParams,
707
- fullDataFormatter: data.fullDataFormatter,
708
- fullChartParams: data.fullChartParams,
709
- // gridAxesReverseTransformValue: data.gridAxesReverseTransform.value,
710
- labelTransform: data.labelTransform,
711
- textSizePx: data.textSizePx
712
- })
713
-
714
- // label的事件
715
- labelSelection
716
- .on('mouseover', (event, datum) => {
717
- event.stopPropagation()
718
- // const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
719
-
720
- isLabelMouseover = true
721
-
722
- subject.event$.next({
723
- type: 'grid',
724
- pluginName: name,
725
- eventName: 'mouseover',
726
- highlightTarget: data.highlightTarget,
727
- datum: null,
728
- gridIndex: 0, // @Q@ 暫不處理
729
- series: [],
730
- seriesIndex: -1,
731
- seriesLabel: '',
732
- groups: data.GroupDataMap.get(groupLabel) ?? [],
733
- groupIndex,
734
- groupLabel,
735
- event,
736
- data: data.computedData
737
- })
738
- })
739
- .on('mousemove', (event, datum) => {
740
- event.stopPropagation()
741
- // const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
742
-
743
- subject.event$.next({
744
- type: 'grid',
745
- pluginName: name,
746
- eventName: 'mousemove',
747
- highlightTarget: data.highlightTarget,
748
- datum: null,
749
- gridIndex: 0, // @Q@ 暫不處理
750
- series: [],
751
- seriesIndex: -1,
752
- seriesLabel: '',
753
- groups: data.GroupDataMap.get(groupLabel) ?? [],
754
- groupIndex,
755
- groupLabel,
756
- event,
757
- data: data.computedData
758
- })
759
- })
760
- // .on('mouseout', (event, datum) => {
761
- // event.stopPropagation()
762
- // // const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
763
-
764
- // isLabelMouseover = false
765
-
766
- // subject.event$.next({
767
- // type: 'grid',
768
- // pluginName: name,
769
- // eventName: 'mouseout',
770
- // highlightTarget: data.highlightTarget,
771
- // datum: null,
772
- // gridIndex: 0, // @Q@ 暫不處理
773
- // series: [],
774
- // seriesIndex: -1,
775
- // seriesLabel: '',
776
- // groups: data.GroupDataMap.get(groupLabel) ?? [],
777
- // groupIndex,
778
- // groupLabel,
779
- // event,
780
- // data: data.computedData
781
- // })
782
- // })
783
- .on('click', (event, datum) => {
784
- event.stopPropagation()
785
- // const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
786
-
787
- subject.event$.next({
788
- type: 'grid',
789
- pluginName: name,
790
- eventName: 'click',
791
- highlightTarget: data.highlightTarget,
792
- datum: null,
793
- gridIndex: 0, // @Q@ 暫不處理
794
- series: [],
795
- seriesIndex: -1,
796
- seriesLabel: '',
797
- groups: data.GroupDataMap.get(groupLabel) ?? [],
798
- groupIndex,
799
- groupLabel,
800
- event,
801
- data: data.computedData
802
- })
803
- })
804
-
805
- })
806
-
807
- // // -- highlight(無論highlightTarget設定為何,一律依從groupLabel來顯示) --
808
- // combineLatest({
809
- // event: subject.event$.pipe(
810
- // filter(d => d.eventName === 'mouseover' || d.eventName === 'mousemove')
811
- // ),
812
- // computedData: observer.computedData$,
813
- // groupScale: groupScale$,
814
- // gridAxesSize: observer.gridAxesSize$,
815
- // fullParams: observer.fullParams$,
816
- // fullChartParams: observer.fullChartParams$,
817
- // highlightTarget: highlightTarget$,
818
- // gridAxesReverseTransform: observer.gridAxesReverseTransform$,
819
- // GroupDataMap: observer.GroupDataMap$,
820
- // gridGroupPositionFn: gridGroupPositionFn$,
821
- // textSizePx: observer.textSizePx$
822
- // }).pipe(
823
- // takeUntil(destroy$),
824
- // switchMap(async d => d)
825
- // ).subscribe(data => {
826
- // // const groups = data.event.eventName === 'mouseover' || data.event.eventName === 'mousemove'
827
- // // ? data.event.groups
828
- // // : []
829
-
830
- // // const groupLabel = data.event.eventName === 'mouseover' || data.event.eventName === 'mousemove'
831
- // // ? data.event.groupLabel
832
- // // : ''
833
- // const axisX = data.groupScale(data.event.groupLabel) ?? 0
834
-
835
- // const lineData = createLineData({
836
- // groupLabel: data.event.groupLabel,
837
- // axisX,
838
- // axisHeight: data.gridAxesSize.height,
839
- // fullParams: data.fullParams,
840
- // })
841
- // renderLine({
842
- // selection: axisSelection,
843
- // pluginName: name,
844
- // lineData,
845
- // fullParams: data.fullParams,
846
- // fullChartParams: data.fullChartParams
847
- // })
848
- // const labelData = createLabelData({
849
- // groupLabel: data.event.groupLabel,
850
- // axisX,
851
- // fullParams: data.fullParams
852
- // })
853
- // const labelSelection = renderLabel({
854
- // selection: axisSelection,
855
- // labelData,
856
- // fullParams: data.fullParams,
857
- // fullChartParams: data.fullChartParams,
858
- // gridAxesReverseTransformValue: data.gridAxesReverseTransform.value,
859
- // textSizePx: data.textSizePx
860
- // })
861
-
862
- // // label的事件
863
- // labelSelection
864
- // .on('mouseover', (event, datum) => {
865
-
866
- // const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
867
-
868
- // subject.event$.next({
869
- // type: 'grid',
870
- // pluginName: name,
871
- // eventName: 'mouseover',
872
- // highlightTarget: data.highlightTarget,
873
- // datum: null,
874
- // gridIndex: 0, // @Q@ 暫不處理
875
- // series: [],
876
- // seriesIndex: -1,
877
- // seriesLabel: '',
878
- // groups: data.event.groups,
879
- // groupIndex,
880
- // groupLabel,
881
- // event,
882
- // data: data.computedData
883
- // })
884
- // })
885
- // .on('mousemove', (event, datum) => {
886
- // const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
887
-
888
- // subject.event$.next({
889
- // type: 'grid',
890
- // pluginName: name,
891
- // eventName: 'mousemove',
892
- // highlightTarget: data.highlightTarget,
893
- // datum: null,
894
- // gridIndex: 0, // @Q@ 暫不處理
895
- // series: [],
896
- // seriesIndex: -1,
897
- // seriesLabel: '',
898
- // groups: data.event.groups,
899
- // groupIndex,
900
- // groupLabel,
901
- // event,
902
- // data: data.computedData
903
- // })
904
- // })
905
- // .on('mouseout', (event, datum) => {
906
- // const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
907
-
908
- // subject.event$.next({
909
- // type: 'grid',
910
- // pluginName: name,
911
- // eventName: 'mouseout',
912
- // highlightTarget: data.highlightTarget,
913
- // datum: null,
914
- // gridIndex: 0, // @Q@ 暫不處理
915
- // series: [],
916
- // seriesIndex: -1,
917
- // seriesLabel: '',
918
- // groups: data.event.groups,
919
- // groupIndex,
920
- // groupLabel,
921
- // event,
922
- // data: data.computedData
923
- // })
924
- // })
925
- // .on('click', (event, datum) => {
926
- // const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
927
-
928
- // subject.event$.next({
929
- // type: 'grid',
930
- // pluginName: name,
931
- // eventName: 'click',
932
- // highlightTarget: data.highlightTarget,
933
- // datum: null,
934
- // gridIndex: 0, // @Q@ 暫不處理
935
- // series: [],
936
- // seriesIndex: -1,
937
- // seriesLabel: '',
938
- // groups: data.event.groups,
939
- // groupIndex,
940
- // groupLabel,
941
- // event,
942
- // data: data.computedData
943
- // })
944
- // })
945
- // })
946
-
947
-
948
-
949
- const rootRectMouseout$ = d3EventObservable(rootRectSelection, 'mouseout').pipe(
950
- takeUntil(destroy$),
951
- )
952
-
953
-
954
- combineLatest({
955
- rootRectMouseout: rootRectMouseout$,
956
- axesSelection: axesSelection$,
957
- }).pipe(
958
- takeUntil(destroy$),
959
- switchMap(async d => d)
960
- ).subscribe(data => {
961
-
962
- setTimeout(() => {
963
- // @Q@ workaround - 不知為何和 label 會有衝突,當滑鼠移動到 label 上時,會觸發 mouseout 事件
964
- if (isLabelMouseover == true) {
965
- return
966
- }
967
-
968
- removeLine(data.axesSelection)
969
- removeLabel(data.axesSelection)
970
- })
971
- })
972
-
973
- return () => {
974
- destroy$.next(undefined)
975
- rootRectSelection.remove()
976
- }
1
+ import * as d3 from 'd3'
2
+ import {
3
+ // of,
4
+ iif,
5
+ EMPTY,
6
+ combineLatest,
7
+ switchMap,
8
+ map,
9
+ filter,
10
+ first,
11
+ takeUntil,
12
+ distinctUntilChanged,
13
+ shareReplay,
14
+ Subject,
15
+ Observable } from 'rxjs'
16
+ import {
17
+ defineGridPlugin } from '@orbcharts/core'
18
+ import type {
19
+ TransformData,
20
+ DataFormatterGrid,
21
+ ChartParams } from '@orbcharts/core'
22
+ import { DEFAULT_GROUP_AREA_PARAMS } from '../defaults'
23
+ import { parseTickFormatValue } from '../../utils/d3Utils'
24
+ import { measureTextWidth } from '../../utils/commonUtils'
25
+ import { getColor, getClassName, getUniID } from '../../utils/orbchartsUtils'
26
+ import { d3EventObservable } from '../../utils/observables'
27
+ import { gridGroupPosition } from '../gridObservables'
28
+ import { createAxisPointScale } from '@orbcharts/core'
29
+ import type { GroupAuxParams } from '../types'
30
+ import { gridSelectionsObservable } from '../gridObservables'
31
+
32
+ interface LineDatum {
33
+ id: string
34
+ x1: number
35
+ x2: number
36
+ y1: number
37
+ y2: number
38
+ }
39
+
40
+ interface LabelDatum {
41
+ id: string
42
+ text: string
43
+ x: number
44
+ y: number
45
+ }
46
+
47
+ const pluginName = 'GroupAux'
48
+ const labelClassName = getClassName(pluginName, 'label-box')
49
+
50
+ function createLineData ({ groupLabel, axisX, axisHeight, fullParams }: {
51
+ groupLabel: string
52
+ axisX: number
53
+ axisHeight: number
54
+ fullParams: GroupAuxParams
55
+ }): LineDatum[] {
56
+ return fullParams.showLine && groupLabel
57
+ ? [{
58
+ id: groupLabel,
59
+ x1: axisX,
60
+ x2: axisX,
61
+ y1: 0,
62
+ y2: axisHeight
63
+ }]
64
+ : []
65
+ }
66
+
67
+ function renderLine ({ selection, pluginName, lineData, fullParams, fullChartParams }: {
68
+ selection: d3.Selection<any, string, any, unknown>
69
+ pluginName: string
70
+ lineData: LineDatum[]
71
+ fullParams: GroupAuxParams
72
+ fullChartParams: ChartParams
73
+ }) {
74
+ const gClassName = getClassName(pluginName, 'auxline')
75
+ const update = selection
76
+ .selectAll<SVGLineElement, LineDatum>(`line.${gClassName}`)
77
+ .data(lineData)
78
+ const enter = update
79
+ .enter()
80
+ .append('line')
81
+ .classed(gClassName, true)
82
+ // .style('stroke', '#E4E7ED')
83
+ .style('stroke', d => getColor(fullParams.lineColorType, fullChartParams))
84
+ .style('stroke-width', 1)
85
+ .style('stroke-dasharray', fullParams.lineDashArray ?? 'none')
86
+ .style('pointer-events', 'none')
87
+ // .attr('opacity', 0)
88
+ const auxLineSelection = update.merge(enter)
89
+ // .attr('opacity', (d) => {
90
+ // return d.active == true ? 1 : 0
91
+ // })
92
+ update.exit().remove()
93
+ enter
94
+ .attr('x1', d => d.x1)
95
+ .attr('y1', d => d.y1)
96
+ .attr('x2', d => d.x2)
97
+ .attr('y2', d => d.y2)
98
+ update
99
+ .transition()
100
+ .duration(50)
101
+ .attr('x1', d => d.x1)
102
+ .attr('y1', d => d.y1)
103
+ .attr('x2', d => d.x2)
104
+ .attr('y2', d => d.y2)
105
+
106
+ return auxLineSelection
107
+ }
108
+
109
+ function removeLine (selection: d3.Selection<any, string, any, unknown>) {
110
+ const update = selection
111
+ .selectAll<SVGLineElement, LineDatum>('line')
112
+ .data([])
113
+
114
+ update.exit().remove()
115
+ }
116
+
117
+ function createLabelData ({ groupLabel, axisX, fullParams }: {
118
+ groupLabel: string
119
+ axisX: number
120
+ fullParams: GroupAuxParams
121
+ }) {
122
+ return fullParams.showLabel && groupLabel
123
+ ? [{
124
+ id: groupLabel,
125
+ x: axisX,
126
+ y: - fullParams.labelPadding,
127
+ text: parseTickFormatValue(groupLabel, fullParams.labelTextFormat)
128
+ }]
129
+ : []
130
+ }
131
+
132
+ function renderLabel ({ selection, labelData, fullParams, fullDataFormatter, fullChartParams, labelTransform, textSizePx }: {
133
+ selection: d3.Selection<any, string, any, unknown>
134
+ labelData: LabelDatum[]
135
+ fullParams: GroupAuxParams
136
+ fullDataFormatter: DataFormatterGrid
137
+ fullChartParams: ChartParams
138
+ // gridAxesReverseTransformValue: string
139
+ labelTransform: string
140
+ textSizePx: number
141
+ }) {
142
+ const rectHeight = textSizePx + 4
143
+
144
+ const gUpdate = selection
145
+ .selectAll<SVGGElement, LabelDatum>(`g.${labelClassName}`)
146
+ .data(labelData)
147
+ const gEnter = gUpdate
148
+ .enter()
149
+ .append('g')
150
+ .classed(labelClassName, true)
151
+ .style('cursor', 'pointer')
152
+ const axisLabelSelection = gEnter.merge(gUpdate)
153
+ gEnter
154
+ .attr("transform", (d, i) => {
155
+ return `translate(${d.x}, ${d.y})`
156
+ })
157
+ gUpdate
158
+ .transition()
159
+ .duration(50)
160
+ .attr("transform", (d, i) => {
161
+ return `translate(${d.x}, ${d.y})`
162
+ })
163
+ gUpdate.exit().remove()
164
+
165
+ axisLabelSelection.each((datum, i, n) => {
166
+ const rectWidth = measureTextWidth(datum.text, textSizePx) + 12
167
+ // -- label偏移位置 --
168
+ let rectX = - rectWidth / 2
169
+ let rectY = -2
170
+ if (fullDataFormatter.grid.groupAxis.position === 'bottom') {
171
+ rectX = fullParams.labelRotate
172
+ ? - rectWidth
173
+ : - rectWidth / 2
174
+ rectY = 2
175
+ } else if (fullDataFormatter.grid.groupAxis.position === 'left') {
176
+ rectX = - rectWidth + 2
177
+ rectY = - rectHeight / 2
178
+ } else if (fullDataFormatter.grid.groupAxis.position === 'right') {
179
+ rectX = - 2
180
+ rectY = - rectHeight / 2
181
+ } else if (fullDataFormatter.grid.groupAxis.position === 'top') {
182
+ rectX = fullParams.labelRotate
183
+ ? - rectWidth
184
+ : - rectWidth / 2
185
+ rectY = - rectHeight + 2
186
+ }
187
+
188
+ const rectUpdate = d3.select(n[i])
189
+ .selectAll<SVGRectElement, LabelDatum>('rect')
190
+ .data([datum])
191
+ const rectEnter = rectUpdate
192
+ .enter()
193
+ .append('rect')
194
+ .attr('height', `${rectHeight}px`)
195
+ .attr('fill', d => getColor(fullParams.labelColorType, fullChartParams))
196
+ .attr('x', rectX)
197
+ .attr('y', rectY)
198
+ .attr('rx', 5)
199
+ .attr('ry', 5)
200
+ .style('cursor', 'pointer')
201
+ // .style('pointer-events', 'none')
202
+ const rect = rectUpdate.merge(rectEnter)
203
+ .attr('width', d => `${rectWidth}px`)
204
+ .style('transform', labelTransform)
205
+ rectUpdate.exit().remove()
206
+
207
+ const textUpdate = d3.select(n[i])
208
+ .selectAll<SVGTextElement, LabelDatum>('text')
209
+ .data([datum])
210
+ const textEnter = textUpdate
211
+ .enter()
212
+ .append('text')
213
+ .style('dominant-baseline', 'hanging')
214
+ .style('cursor', 'pointer')
215
+ // .style('pointer-events', 'none')
216
+ const text = textUpdate.merge(textEnter)
217
+ .text(d => d.text)
218
+ .style('transform', labelTransform)
219
+ .attr('fill', d => getColor(fullParams.labelTextColorType, fullChartParams))
220
+ .attr('font-size', fullChartParams.styles.textSize)
221
+ .attr('x', rectX + 6)
222
+ .attr('y', rectY)
223
+ textUpdate.exit().remove()
224
+ })
225
+
226
+ return axisLabelSelection
227
+ }
228
+
229
+ function removeLabel (selection: d3.Selection<any, string, any, unknown>) {
230
+ const gUpdate = selection
231
+ .selectAll<SVGGElement, LabelDatum>(`g.${labelClassName}`)
232
+ .data([])
233
+
234
+ gUpdate.exit().remove()
235
+ }
236
+
237
+ export const GroupAux = defineGridPlugin(pluginName, DEFAULT_GROUP_AREA_PARAMS)(({ selection, rootSelection, name, subject, observer }) => {
238
+ const destroy$ = new Subject()
239
+
240
+ let isLabelMouseover = false
241
+
242
+ const rootRectSelection: d3.Selection<SVGRectElement, any, any, any> = rootSelection
243
+ .insert('rect', 'g')
244
+ .classed(getClassName(pluginName, 'rect'), true)
245
+ .attr('opacity', 0)
246
+
247
+ // const axisSelection: d3.Selection<SVGGElement, any, any, any> = selection
248
+ // .append('g')
249
+
250
+ const {
251
+ seriesSelection$,
252
+ axesSelection$,
253
+ defsSelection$,
254
+ graphicGSelection$
255
+ } = gridSelectionsObservable({
256
+ selection,
257
+ pluginName,
258
+ clipPathID: 'test',
259
+ seriesLabels$: observer.seriesLabels$,
260
+ gridContainerPosition$: observer.gridContainerPosition$,
261
+ gridAxesTransform$: observer.gridAxesTransform$,
262
+ gridGraphicTransform$: observer.gridGraphicTransform$
263
+ })
264
+
265
+ observer.layout$.pipe(
266
+ takeUntil(destroy$),
267
+ ).subscribe(d => {
268
+ rootRectSelection
269
+ .attr('width', d.rootWidth)
270
+ .attr('height', d.rootHeight)
271
+ })
272
+
273
+ // observer.gridAxesTransform$
274
+ // .pipe(
275
+ // takeUntil(destroy$),
276
+ // map(d => d.value),
277
+ // distinctUntilChanged()
278
+ // ).subscribe(d => {
279
+ // axisSelection
280
+ // .style('transform', d)
281
+ // })
282
+
283
+ // const visibleComputedData$ = observer.computedData$.pipe(
284
+ // takeUntil(destroy$),
285
+ // map(data => {
286
+ // const visibleComputedData = data
287
+ // .map(d => {
288
+ // return d.filter(_d => {
289
+ // return _d.visible == true
290
+ // })
291
+ // })
292
+ // .filter(d => d.length)
293
+ // // console.log('visibleComputedData', visibleComputedData)
294
+ // return visibleComputedData
295
+ // })
296
+ // )
297
+
298
+ // const SeriesDataMap$ = visibleComputedData$.pipe(
299
+ // map(d => makeGridSeriesDataMap(d))
300
+ // )
301
+
302
+ // const GroupDataMap$ = visibleComputedData$.pipe(
303
+ // map(d => makeGridGroupDataMap(d))
304
+ // )
305
+
306
+ // const contentTransform$: Observable<string> = new Observable(subscriber => {
307
+ // combineLatest({
308
+ // fullParams: observer.fullParams$,
309
+ // gridAxesTransform: observer.gridAxesTransform$
310
+ // }).pipe(
311
+ // takeUntil(destroy$),
312
+ // // 轉換後會退訂前一個未完成的訂閱事件,因此可以取到「同時間」最後一次的訂閱事件
313
+ // switchMap(async (d) => d),
314
+ // ).subscribe(data => {
315
+
316
+ // const transformData = Object.assign({}, data.gridAxesTransform)
317
+
318
+ // // const value = getAxesTransformValue({
319
+ // // translate: [0, 0],
320
+ // // scale: [transformData.scale[0] * -1, transformData.scale[1] * -1],
321
+ // // rotate: transformData.rotate * -1,
322
+ // // rotateX: transformData.rotateX * -1,
323
+ // // rotateY: transformData.rotateY * -1
324
+ // // })
325
+
326
+ // subscriber.next(transformData.value)
327
+ // })
328
+ // })
329
+ // const reverseTransform$: Observable<TransformData> = observer.gridAxesTransform$.pipe(
330
+ // takeUntil(destroy$),
331
+ // map(d => {
332
+ // const translate: [number, number] = [d.translate[0] * -1, d.translate[1] * -1]
333
+ // const scale: [number, number] = [d.scale[0] * -1, d.scale[1] * -1]
334
+ // const rotate = d.rotate * -1
335
+ // const rotateX = d.rotateX * -1
336
+ // const rotateY = d.rotateY * -1
337
+ // return {
338
+ // translate,
339
+ // scale,
340
+ // rotate,
341
+ // rotateX,
342
+ // rotateY,
343
+ // value: ''
344
+ // }
345
+ // }),
346
+ // )
347
+ // const contentTransform$ = combineLatest({
348
+ // fullParams: observer.fullParams$,
349
+ // reverseTransform: reverseTransform$
350
+ // }).pipe(
351
+ // takeUntil(destroy$),
352
+ // switchMap(async data => {
353
+ // const translate = [0, 0]
354
+ // return `translate(${translate[0]}px, ${translate[1]}px) rotate(${data.reverseTransform.rotate}deg) rotateX(${data.reverseTransform.rotateX}deg) rotateY(${data.reverseTransform.rotateY}deg)`
355
+ // }),
356
+ // distinctUntilChanged()
357
+ // )
358
+
359
+ // const groupScale$: Observable<d3.ScalePoint<string>> = new Observable(subscriber => {
360
+ // combineLatest({
361
+ // fullDataFormatter: observer.fullDataFormatter$,
362
+ // gridAxesSize: observer.gridAxesSize$,
363
+ // computedData: observer.computedData$
364
+ // }).pipe(
365
+ // takeUntil(destroy$),
366
+ // switchMap(async (d) => d),
367
+ // ).subscribe(data => {
368
+ // const groupMin = 0
369
+ // const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
370
+ // const groupScaleDomainMin = data.fullDataFormatter.grid.groupAxis.scaleDomain[0] === 'auto'
371
+ // ? groupMin - data.fullDataFormatter.grid.groupAxis.scalePadding
372
+ // : data.fullDataFormatter.grid.groupAxis.scaleDomain[0] as number - data.fullDataFormatter.grid.groupAxis.scalePadding
373
+ // const groupScaleDomainMax = data.fullDataFormatter.grid.groupAxis.scaleDomain[1] === 'auto'
374
+ // ? groupMax + data.fullDataFormatter.grid.groupAxis.scalePadding
375
+ // : data.fullDataFormatter.grid.groupAxis.scaleDomain[1] as number + data.fullDataFormatter.grid.groupAxis.scalePadding
376
+
377
+ // const groupingLength = data.computedData[0]
378
+ // ? data.computedData[0].length
379
+ // : 0
380
+
381
+ // let _labels = data.fullDataFormatter.grid.seriesDirection === 'row'
382
+ // // ? data.fullDataFormatter.grid.columnLabels
383
+ // // : data.fullDataFormatter.grid.rowLabels
384
+ // ? (data.computedData[0] ?? []).map(d => d.groupLabel)
385
+ // : data.computedData.map(d => d[0].groupLabel)
386
+
387
+ // const axisLabels = new Array(groupingLength).fill(0)
388
+ // .map((d, i) => {
389
+ // return _labels[i] != null
390
+ // ? _labels[i]
391
+ // : String(i) // 沒有label則用序列號填充
392
+ // })
393
+ // .filter((d, i) => {
394
+ // return i >= groupScaleDomainMin && i <= groupScaleDomainMax
395
+ // })
396
+
397
+
398
+ // const padding = data.fullDataFormatter.grid.groupAxis.scalePadding
399
+
400
+ // const groupScale = createAxisPointScale({
401
+ // axisLabels,
402
+ // axisWidth: data.gridAxesSize.width,
403
+ // padding
404
+ // })
405
+
406
+ // subscriber.next(groupScale)
407
+ // })
408
+ // })
409
+
410
+ const groupScaleDomain$ = combineLatest({
411
+ fullDataFormatter: observer.fullDataFormatter$,
412
+ gridAxesSize: observer.gridAxesSize$,
413
+ computedData: observer.computedData$
414
+ }).pipe(
415
+ takeUntil(destroy$),
416
+ switchMap(async (d) => d),
417
+ map(data => {
418
+ const groupMin = 0
419
+ const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
420
+ const groupScaleDomainMin = data.fullDataFormatter.grid.groupAxis.scaleDomain[0] === 'auto'
421
+ ? groupMin - data.fullDataFormatter.grid.groupAxis.scalePadding
422
+ : data.fullDataFormatter.grid.groupAxis.scaleDomain[0] as number - data.fullDataFormatter.grid.groupAxis.scalePadding
423
+ const groupScaleDomainMax = data.fullDataFormatter.grid.groupAxis.scaleDomain[1] === 'auto'
424
+ ? groupMax + data.fullDataFormatter.grid.groupAxis.scalePadding
425
+ : data.fullDataFormatter.grid.groupAxis.scaleDomain[1] as number + data.fullDataFormatter.grid.groupAxis.scalePadding
426
+
427
+ return [groupScaleDomainMin, groupScaleDomainMax]
428
+ }),
429
+ shareReplay(1)
430
+ )
431
+
432
+ const groupScale$ = combineLatest({
433
+ groupScaleDomain: groupScaleDomain$,
434
+ gridAxesSize: observer.gridAxesSize$
435
+ }).pipe(
436
+ takeUntil(destroy$),
437
+ switchMap(async (d) => d),
438
+ map(data => {
439
+ const groupScale: d3.ScaleLinear<number, number> = d3.scaleLinear()
440
+ .domain(data.groupScaleDomain)
441
+ .range([0, data.gridAxesSize.width])
442
+ return groupScale
443
+ })
444
+ )
445
+
446
+ // // 取得事件座標的group資料
447
+ // const gridGroupPositionFn$ = gridGroupPositionFnObservable({
448
+ // fullDataFormatter$: observer.fullDataFormatter$,
449
+ // gridAxesSize$: observer.gridAxesSize$,
450
+ // computedData$: observer.computedData$,
451
+ // fullChartParams$: observer.fullChartParams$,
452
+ // })
453
+
454
+ const highlightTarget$ = observer.fullChartParams$.pipe(
455
+ takeUntil(destroy$),
456
+ map(d => d.highlightTarget),
457
+ distinctUntilChanged()
458
+ )
459
+
460
+ // combineLatest({
461
+ // computedData: observer.computedData$,
462
+ // gridAxesSize: observer.gridAxesSize$,
463
+ // fullParams: observer.fullParams$,
464
+ // fullChartParams: observer.fullChartParams$,
465
+ // highlightTarget: highlightTarget$,
466
+ // SeriesDataMap: observer.SeriesDataMap$,
467
+ // GroupDataMap: observer.GroupDataMap$,
468
+ // gridGroupPositionFn: gridGroupPositionFn$,
469
+ // groupScale: groupScale$,
470
+ // }).pipe(
471
+ // takeUntil(destroy$),
472
+ // switchMap(async (d) => d),
473
+ // ).subscribe(data => {
474
+
475
+ // // store.selection
476
+ // rootSelection
477
+ // .on('mouseover', (event, datum) => {
478
+ // // event.stopPropagation()
479
+
480
+ // const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
481
+
482
+ // subject.event$.next({
483
+ // type: 'grid',
484
+ // pluginName: name,
485
+ // eventName: 'mouseover',
486
+ // highlightTarget: data.highlightTarget,
487
+ // datum: null,
488
+ // gridIndex: 0, // @Q@ 暫不處理
489
+ // series: [],
490
+ // seriesIndex: -1,
491
+ // seriesLabel: '',
492
+ // groups: data.GroupDataMap.get(groupLabel) ?? [],
493
+ // // groups: [],
494
+ // groupIndex,
495
+ // groupLabel,
496
+ // event,
497
+ // data: data.computedData
498
+ // })
499
+ // })
500
+ // .on('mousemove', (event, datum) => {
501
+ // // event.stopPropagation()
502
+
503
+ // const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
504
+
505
+ // subject.event$.next({
506
+ // type: 'grid',
507
+ // pluginName: name,
508
+ // eventName: 'mousemove',
509
+ // highlightTarget: data.highlightTarget,
510
+ // datum: null,
511
+ // gridIndex: 0, // @Q@ 暫不處理
512
+ // series: [],
513
+ // seriesIndex: -1,
514
+ // seriesLabel: '',
515
+ // groups: data.GroupDataMap.get(groupLabel) ?? [],
516
+ // // groups: [],
517
+ // groupIndex,
518
+ // groupLabel,
519
+ // event,
520
+ // data: data.computedData
521
+ // })
522
+ // })
523
+ // .on('mouseout', (event, datum) => {
524
+ // // event.stopPropagation()
525
+
526
+ // const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
527
+
528
+ // subject.event$.next({
529
+ // type: 'grid',
530
+ // pluginName: name,
531
+ // eventName: 'mouseout',
532
+ // highlightTarget: data.highlightTarget,
533
+ // datum: null,
534
+ // gridIndex: 0, // @Q@ 暫不處理
535
+ // series: [],
536
+ // seriesIndex: -1,
537
+ // seriesLabel: '',
538
+ // groups: data.GroupDataMap.get(groupLabel) ?? [],
539
+ // // groups: [],
540
+ // groupIndex,
541
+ // groupLabel,
542
+ // event,
543
+ // data: data.computedData
544
+ // })
545
+ // })
546
+ // .on('click', (event, datum) => {
547
+ // event.stopPropagation()
548
+
549
+ // const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
550
+
551
+ // subject.event$.next({
552
+ // type: 'grid',
553
+ // pluginName: name,
554
+ // eventName: 'click',
555
+ // highlightTarget: data.highlightTarget,
556
+ // datum: null,
557
+ // gridIndex: 0, // @Q@ 暫不處理
558
+ // series: [],
559
+ // seriesIndex: -1,
560
+ // seriesLabel: '',
561
+ // // groups: data.GroupDataMap.get(groupLabel) ?? [],
562
+ // groups: [],
563
+ // groupIndex,
564
+ // groupLabel,
565
+ // event,
566
+ // data: data.computedData
567
+ // })
568
+ // })
569
+
570
+ // // barSelection$.next(barSelection!)
571
+ // })
572
+
573
+ const rootMousemove$: Observable<any> = d3EventObservable(rootSelection, 'mousemove').pipe(
574
+ takeUntil(destroy$),
575
+ )
576
+
577
+
578
+ // const mousemoveGroupLabel$ = combineLatest({
579
+ // rootMousemove: rootMousemove$,
580
+ // gridGroupPositionFn: gridGroupPositionFn$,
581
+ // }).pipe(
582
+ // takeUntil(destroy$),
583
+ // switchMap(async d => d),
584
+ // map(data => {
585
+ // const { groupIndex, groupLabel } = data.gridGroupPositionFn(data.rootMousemove)
586
+ // return { groupIndex, groupLabel }
587
+ // }),
588
+ // shareReplay(1)
589
+ // )
590
+
591
+ const labelTransform$ = combineLatest({
592
+ fullParams: observer.fullParams$,
593
+ fullDataFormatter: observer.fullDataFormatter$,
594
+ gridAxesReverseTransform: observer.gridAxesReverseTransform$,
595
+ gridContainerPosition: observer.gridContainerPosition$
596
+ }).pipe(
597
+ takeUntil(destroy$),
598
+ switchMap(async d => d),
599
+ map(data => {
600
+ const axisReverseTranslateValue = `translate(${data.gridAxesReverseTransform.translate[0]}px, ${data.gridAxesReverseTransform.translate[1]}px)`
601
+ const axisReverseRotateValue = `rotate(${data.gridAxesReverseTransform.rotate}deg) rotateX(${data.gridAxesReverseTransform.rotateX}deg) rotateY(${data.gridAxesReverseTransform.rotateY}deg)`
602
+ const containerScaleReverseScaleValue = `scale(${1 / data.gridContainerPosition[0].scale[0]}, ${1 / data.gridContainerPosition[0].scale[1]})`
603
+ const tickTextRotateDeg = (data.fullDataFormatter.grid.groupAxis.position === 'left' && data.fullDataFormatter.grid.valueAxis.position === 'top')
604
+ || (data.fullDataFormatter.grid.groupAxis.position === 'right' && data.fullDataFormatter.grid.valueAxis.position === 'bottom')
605
+ ? data.fullParams.labelRotate + 180 // 修正文字倒轉
606
+ : data.fullParams.labelRotate
607
+ // ? 180 // 修正文字倒轉
608
+ // : 0
609
+
610
+ const textRotateValue = `rotate(${tickTextRotateDeg}deg)`
611
+
612
+ // 必須按照順序(先抵消外層rotate,再抵消最外層scale,最後再做本身的rotate)
613
+ return `${axisReverseTranslateValue} ${axisReverseRotateValue} ${containerScaleReverseScaleValue} ${textRotateValue}`
614
+ }),
615
+ distinctUntilChanged()
616
+ )
617
+
618
+ const columnAmount$ = observer.gridContainerPosition$.pipe(
619
+ takeUntil(destroy$),
620
+ map(gridContainerPosition => {
621
+ const maxColumnIndex = gridContainerPosition.reduce((acc, current) => {
622
+ return current.columnIndex > acc ? current.columnIndex : acc
623
+ }, 0)
624
+ return maxColumnIndex + 1
625
+ }),
626
+ distinctUntilChanged()
627
+ )
628
+
629
+ const rowAmount$ = observer.gridContainerPosition$.pipe(
630
+ takeUntil(destroy$),
631
+ map(gridContainerPosition => {
632
+ const maxRowIndex = gridContainerPosition.reduce((acc, current) => {
633
+ return current.rowIndex > acc ? current.rowIndex : acc
634
+ }, 0)
635
+ return maxRowIndex + 1
636
+ }),
637
+ distinctUntilChanged()
638
+ )
639
+
640
+ const gridGroupPosition$ = gridGroupPosition({
641
+ rootSelection,
642
+ fullDataFormatter$: observer.fullDataFormatter$,
643
+ gridAxesSize$: observer.gridAxesSize$,
644
+ computedData$: observer.computedData$,
645
+ fullChartParams$: observer.fullChartParams$,
646
+ gridContainerPosition$: observer.gridContainerPosition$,
647
+ layout$: observer.layout$
648
+ }).pipe(
649
+ takeUntil(destroy$)
650
+ )
651
+
652
+ combineLatest({
653
+ axesSelection: axesSelection$,
654
+ columnAmount: columnAmount$,
655
+ rowAmount: rowAmount$,
656
+ layout: observer.layout$,
657
+ rootMousemove: rootMousemove$,
658
+ // gridGroupPositionFn: gridGroupPositionFn$,
659
+ gridGroupPosition: gridGroupPosition$,
660
+ computedData: observer.computedData$,
661
+ groupScale: groupScale$,
662
+ gridAxesSize: observer.gridAxesSize$,
663
+ fullParams: observer.fullParams$,
664
+ fullDataFormatter: observer.fullDataFormatter$,
665
+ fullChartParams: observer.fullChartParams$,
666
+ highlightTarget: highlightTarget$,
667
+ // gridAxesReverseTransform: observer.gridAxesReverseTransform$,
668
+ labelTransform: labelTransform$,
669
+ GroupDataMap: observer.GroupDataMap$,
670
+ textSizePx: observer.textSizePx$
671
+ }).subscribe(data => {
672
+ // // 由於event座標是基於底層的,但是container會有多欄,所以要重新計算
673
+ // const eventData = {
674
+ // offsetX: data.rootMousemove.offsetX * data.columnAmount % data.layout.rootWidth,
675
+ // offsetY: data.rootMousemove.offsetY * data.rowAmount % data.layout.rootHeight
676
+ // }
677
+ // 依event的座標取得group資料
678
+ const { groupIndex, groupLabel } = data.gridGroupPosition
679
+ // console.log('gridGroupPosition', groupIndex, groupLabel)
680
+ const axisX = data.groupScale(groupIndex) ?? 0
681
+ // console.log('axisX', axisX)
682
+ const lineData = createLineData({
683
+ groupLabel: groupLabel,
684
+ axisX,
685
+ axisHeight: data.gridAxesSize.height,
686
+ fullParams: data.fullParams,
687
+ })
688
+ // console.log('lineData', lineData)
689
+ renderLine({
690
+ // selection: axisSelection,
691
+ selection: data.axesSelection,
692
+ pluginName: name,
693
+ lineData,
694
+ fullParams: data.fullParams,
695
+ fullChartParams: data.fullChartParams
696
+ })
697
+ const labelData = createLabelData({
698
+ groupLabel: groupLabel,
699
+ axisX,
700
+ fullParams: data.fullParams
701
+ })
702
+ const labelSelection = renderLabel({
703
+ // selection: axisSelection,
704
+ selection: data.axesSelection,
705
+ labelData,
706
+ fullParams: data.fullParams,
707
+ fullDataFormatter: data.fullDataFormatter,
708
+ fullChartParams: data.fullChartParams,
709
+ // gridAxesReverseTransformValue: data.gridAxesReverseTransform.value,
710
+ labelTransform: data.labelTransform,
711
+ textSizePx: data.textSizePx
712
+ })
713
+
714
+ // label的事件
715
+ labelSelection
716
+ .on('mouseover', (event, datum) => {
717
+ event.stopPropagation()
718
+ // const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
719
+
720
+ isLabelMouseover = true
721
+
722
+ subject.event$.next({
723
+ type: 'grid',
724
+ pluginName: name,
725
+ eventName: 'mouseover',
726
+ highlightTarget: data.highlightTarget,
727
+ datum: null,
728
+ gridIndex: 0, // @Q@ 暫不處理
729
+ series: [],
730
+ seriesIndex: -1,
731
+ seriesLabel: '',
732
+ groups: data.GroupDataMap.get(groupLabel) ?? [],
733
+ groupIndex,
734
+ groupLabel,
735
+ event,
736
+ data: data.computedData
737
+ })
738
+ })
739
+ .on('mousemove', (event, datum) => {
740
+ event.stopPropagation()
741
+ // const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
742
+
743
+ subject.event$.next({
744
+ type: 'grid',
745
+ pluginName: name,
746
+ eventName: 'mousemove',
747
+ highlightTarget: data.highlightTarget,
748
+ datum: null,
749
+ gridIndex: 0, // @Q@ 暫不處理
750
+ series: [],
751
+ seriesIndex: -1,
752
+ seriesLabel: '',
753
+ groups: data.GroupDataMap.get(groupLabel) ?? [],
754
+ groupIndex,
755
+ groupLabel,
756
+ event,
757
+ data: data.computedData
758
+ })
759
+ })
760
+ // .on('mouseout', (event, datum) => {
761
+ // event.stopPropagation()
762
+ // // const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
763
+
764
+ // isLabelMouseover = false
765
+
766
+ // subject.event$.next({
767
+ // type: 'grid',
768
+ // pluginName: name,
769
+ // eventName: 'mouseout',
770
+ // highlightTarget: data.highlightTarget,
771
+ // datum: null,
772
+ // gridIndex: 0, // @Q@ 暫不處理
773
+ // series: [],
774
+ // seriesIndex: -1,
775
+ // seriesLabel: '',
776
+ // groups: data.GroupDataMap.get(groupLabel) ?? [],
777
+ // groupIndex,
778
+ // groupLabel,
779
+ // event,
780
+ // data: data.computedData
781
+ // })
782
+ // })
783
+ .on('click', (event, datum) => {
784
+ event.stopPropagation()
785
+ // const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
786
+
787
+ subject.event$.next({
788
+ type: 'grid',
789
+ pluginName: name,
790
+ eventName: 'click',
791
+ highlightTarget: data.highlightTarget,
792
+ datum: null,
793
+ gridIndex: 0, // @Q@ 暫不處理
794
+ series: [],
795
+ seriesIndex: -1,
796
+ seriesLabel: '',
797
+ groups: data.GroupDataMap.get(groupLabel) ?? [],
798
+ groupIndex,
799
+ groupLabel,
800
+ event,
801
+ data: data.computedData
802
+ })
803
+ })
804
+
805
+ })
806
+
807
+ // // -- highlight(無論highlightTarget設定為何,一律依從groupLabel來顯示) --
808
+ // combineLatest({
809
+ // event: subject.event$.pipe(
810
+ // filter(d => d.eventName === 'mouseover' || d.eventName === 'mousemove')
811
+ // ),
812
+ // computedData: observer.computedData$,
813
+ // groupScale: groupScale$,
814
+ // gridAxesSize: observer.gridAxesSize$,
815
+ // fullParams: observer.fullParams$,
816
+ // fullChartParams: observer.fullChartParams$,
817
+ // highlightTarget: highlightTarget$,
818
+ // gridAxesReverseTransform: observer.gridAxesReverseTransform$,
819
+ // GroupDataMap: observer.GroupDataMap$,
820
+ // gridGroupPositionFn: gridGroupPositionFn$,
821
+ // textSizePx: observer.textSizePx$
822
+ // }).pipe(
823
+ // takeUntil(destroy$),
824
+ // switchMap(async d => d)
825
+ // ).subscribe(data => {
826
+ // // const groups = data.event.eventName === 'mouseover' || data.event.eventName === 'mousemove'
827
+ // // ? data.event.groups
828
+ // // : []
829
+
830
+ // // const groupLabel = data.event.eventName === 'mouseover' || data.event.eventName === 'mousemove'
831
+ // // ? data.event.groupLabel
832
+ // // : ''
833
+ // const axisX = data.groupScale(data.event.groupLabel) ?? 0
834
+
835
+ // const lineData = createLineData({
836
+ // groupLabel: data.event.groupLabel,
837
+ // axisX,
838
+ // axisHeight: data.gridAxesSize.height,
839
+ // fullParams: data.fullParams,
840
+ // })
841
+ // renderLine({
842
+ // selection: axisSelection,
843
+ // pluginName: name,
844
+ // lineData,
845
+ // fullParams: data.fullParams,
846
+ // fullChartParams: data.fullChartParams
847
+ // })
848
+ // const labelData = createLabelData({
849
+ // groupLabel: data.event.groupLabel,
850
+ // axisX,
851
+ // fullParams: data.fullParams
852
+ // })
853
+ // const labelSelection = renderLabel({
854
+ // selection: axisSelection,
855
+ // labelData,
856
+ // fullParams: data.fullParams,
857
+ // fullChartParams: data.fullChartParams,
858
+ // gridAxesReverseTransformValue: data.gridAxesReverseTransform.value,
859
+ // textSizePx: data.textSizePx
860
+ // })
861
+
862
+ // // label的事件
863
+ // labelSelection
864
+ // .on('mouseover', (event, datum) => {
865
+
866
+ // const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
867
+
868
+ // subject.event$.next({
869
+ // type: 'grid',
870
+ // pluginName: name,
871
+ // eventName: 'mouseover',
872
+ // highlightTarget: data.highlightTarget,
873
+ // datum: null,
874
+ // gridIndex: 0, // @Q@ 暫不處理
875
+ // series: [],
876
+ // seriesIndex: -1,
877
+ // seriesLabel: '',
878
+ // groups: data.event.groups,
879
+ // groupIndex,
880
+ // groupLabel,
881
+ // event,
882
+ // data: data.computedData
883
+ // })
884
+ // })
885
+ // .on('mousemove', (event, datum) => {
886
+ // const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
887
+
888
+ // subject.event$.next({
889
+ // type: 'grid',
890
+ // pluginName: name,
891
+ // eventName: 'mousemove',
892
+ // highlightTarget: data.highlightTarget,
893
+ // datum: null,
894
+ // gridIndex: 0, // @Q@ 暫不處理
895
+ // series: [],
896
+ // seriesIndex: -1,
897
+ // seriesLabel: '',
898
+ // groups: data.event.groups,
899
+ // groupIndex,
900
+ // groupLabel,
901
+ // event,
902
+ // data: data.computedData
903
+ // })
904
+ // })
905
+ // .on('mouseout', (event, datum) => {
906
+ // const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
907
+
908
+ // subject.event$.next({
909
+ // type: 'grid',
910
+ // pluginName: name,
911
+ // eventName: 'mouseout',
912
+ // highlightTarget: data.highlightTarget,
913
+ // datum: null,
914
+ // gridIndex: 0, // @Q@ 暫不處理
915
+ // series: [],
916
+ // seriesIndex: -1,
917
+ // seriesLabel: '',
918
+ // groups: data.event.groups,
919
+ // groupIndex,
920
+ // groupLabel,
921
+ // event,
922
+ // data: data.computedData
923
+ // })
924
+ // })
925
+ // .on('click', (event, datum) => {
926
+ // const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
927
+
928
+ // subject.event$.next({
929
+ // type: 'grid',
930
+ // pluginName: name,
931
+ // eventName: 'click',
932
+ // highlightTarget: data.highlightTarget,
933
+ // datum: null,
934
+ // gridIndex: 0, // @Q@ 暫不處理
935
+ // series: [],
936
+ // seriesIndex: -1,
937
+ // seriesLabel: '',
938
+ // groups: data.event.groups,
939
+ // groupIndex,
940
+ // groupLabel,
941
+ // event,
942
+ // data: data.computedData
943
+ // })
944
+ // })
945
+ // })
946
+
947
+
948
+
949
+ const rootRectMouseout$ = d3EventObservable(rootRectSelection, 'mouseout').pipe(
950
+ takeUntil(destroy$),
951
+ )
952
+
953
+
954
+ combineLatest({
955
+ rootRectMouseout: rootRectMouseout$,
956
+ axesSelection: axesSelection$,
957
+ }).pipe(
958
+ takeUntil(destroy$),
959
+ switchMap(async d => d)
960
+ ).subscribe(data => {
961
+
962
+ setTimeout(() => {
963
+ // @Q@ workaround - 不知為何和 label 會有衝突,當滑鼠移動到 label 上時,會觸發 mouseout 事件
964
+ if (isLabelMouseover == true) {
965
+ return
966
+ }
967
+
968
+ removeLine(data.axesSelection)
969
+ removeLabel(data.axesSelection)
970
+ })
971
+ })
972
+
973
+ return () => {
974
+ destroy$.next(undefined)
975
+ rootRectSelection.remove()
976
+ }
977
977
  })