@orbcharts/plugins-basic 3.0.0-alpha.68 → 3.0.0-alpha.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/LICENSE +200 -200
  2. package/dist/orbcharts-plugins-basic.es.js +4838 -4760
  3. package/dist/orbcharts-plugins-basic.umd.js +15 -14
  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 +176 -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
  })