@orbcharts/plugins-basic 3.0.0-alpha.50 → 3.0.0-alpha.52

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