@orbcharts/plugins-basic 3.0.0-beta.19 → 3.0.0-beta.20

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