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

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 (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
  })