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