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

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 (113) hide show
  1. package/LICENSE +200 -200
  2. package/dist/orbcharts-plugins-basic.es.js +2220 -2216
  3. package/dist/orbcharts-plugins-basic.umd.js +36 -34
  4. package/dist/src/grid/defaults.d.ts +2 -2
  5. package/dist/src/multiGrid/defaults.d.ts +1 -1
  6. package/dist/src/multiValue/defaults.d.ts +1 -1
  7. package/lib/core-types.ts +7 -7
  8. package/lib/core.ts +6 -6
  9. package/lib/plugins-basic-types.ts +6 -6
  10. package/package.json +44 -44
  11. package/src/base/BaseBars.ts +765 -765
  12. package/src/base/BaseBarsTriangle.ts +676 -676
  13. package/src/base/BaseDots.ts +464 -464
  14. package/src/base/BaseGroupAxis.ts +691 -691
  15. package/src/base/BaseLegend.ts +684 -684
  16. package/src/base/BaseLineAreas.ts +629 -629
  17. package/src/base/BaseLines.ts +706 -706
  18. package/src/base/BaseRacingBars.ts +551 -551
  19. package/src/base/BaseRacingLabels.ts +396 -396
  20. package/src/base/BaseRacingValueLabels.ts +403 -403
  21. package/src/base/BaseStackedBars.ts +782 -782
  22. package/src/base/BaseTooltip.ts +386 -386
  23. package/src/base/BaseValueAxis.ts +600 -600
  24. package/src/base/BaseXAxis.ts +427 -427
  25. package/src/base/BaseYAxis.ts +389 -389
  26. package/src/base/types.ts +2 -2
  27. package/src/const.ts +30 -30
  28. package/src/grid/defaults.ts +213 -213
  29. package/src/grid/gridObservables.ts +612 -612
  30. package/src/grid/index.ts +16 -16
  31. package/src/grid/plugins/Bars.ts +69 -69
  32. package/src/grid/plugins/BarsPN.ts +66 -66
  33. package/src/grid/plugins/BarsTriangle.ts +73 -73
  34. package/src/grid/plugins/Dots.ts +68 -68
  35. package/src/grid/plugins/GridLegend.ts +107 -107
  36. package/src/grid/plugins/GridTooltip.ts +66 -66
  37. package/src/grid/plugins/GroupAux.ts +1120 -1120
  38. package/src/grid/plugins/GroupAxis.ts +73 -73
  39. package/src/grid/plugins/GroupZoom.ts +218 -218
  40. package/src/grid/plugins/LineAreas.ts +65 -65
  41. package/src/grid/plugins/Lines.ts +59 -59
  42. package/src/grid/plugins/StackedBars.ts +64 -64
  43. package/src/grid/plugins/StackedValueAxis.ts +96 -96
  44. package/src/grid/plugins/ValueAxis.ts +94 -94
  45. package/src/index.ts +6 -6
  46. package/src/multiGrid/defaults.ts +244 -244
  47. package/src/multiGrid/index.ts +14 -14
  48. package/src/multiGrid/multiGridObservables.ts +50 -50
  49. package/src/multiGrid/plugins/MultiBars.ts +108 -108
  50. package/src/multiGrid/plugins/MultiBarsTriangle.ts +114 -114
  51. package/src/multiGrid/plugins/MultiDots.ts +102 -102
  52. package/src/multiGrid/plugins/MultiGridLegend.ts +169 -169
  53. package/src/multiGrid/plugins/MultiGridTooltip.ts +66 -66
  54. package/src/multiGrid/plugins/MultiGroupAxis.ts +137 -137
  55. package/src/multiGrid/plugins/MultiLineAreas.ts +107 -107
  56. package/src/multiGrid/plugins/MultiLines.ts +101 -101
  57. package/src/multiGrid/plugins/MultiStackedBars.ts +106 -106
  58. package/src/multiGrid/plugins/MultiStackedValueAxis.ts +134 -134
  59. package/src/multiGrid/plugins/MultiValueAxis.ts +134 -134
  60. package/src/multiGrid/plugins/OverlappingStackedValueAxes.ts +300 -300
  61. package/src/multiGrid/plugins/OverlappingValueAxes.ts +300 -300
  62. package/src/multiValue/defaults.ts +391 -388
  63. package/src/multiValue/index.ts +12 -12
  64. package/src/multiValue/multiValueObservables.ts +666 -666
  65. package/src/multiValue/plugins/MultiValueLegend.ts +107 -107
  66. package/src/multiValue/plugins/MultiValueTooltip.ts +66 -66
  67. package/src/multiValue/plugins/RacingBars.ts +373 -373
  68. package/src/multiValue/plugins/RacingCounterTexts.ts +300 -300
  69. package/src/multiValue/plugins/RacingValueAxis.ts +114 -114
  70. package/src/multiValue/plugins/RankingAxis_legacy.ts +109 -109
  71. package/src/multiValue/plugins/Scatter.ts +426 -426
  72. package/src/multiValue/plugins/ScatterBubbles.ts +554 -554
  73. package/src/multiValue/plugins/XAxis.ts +107 -107
  74. package/src/multiValue/plugins/XYAux.ts +682 -682
  75. package/src/multiValue/plugins/XYAxes.ts +194 -194
  76. package/src/multiValue/plugins/XYAxes_legacy.ts +683 -683
  77. package/src/multiValue/plugins/XZoom.ts +299 -299
  78. package/src/noneData/defaults.ts +102 -102
  79. package/src/noneData/index.ts +3 -3
  80. package/src/noneData/plugins/Container.ts +27 -27
  81. package/src/noneData/plugins/Tooltip.ts +373 -373
  82. package/src/relationship/defaults.ts +221 -221
  83. package/src/relationship/index.ts +5 -5
  84. package/src/relationship/plugins/ForceDirected.ts +1173 -1173
  85. package/src/relationship/plugins/ForceDirectedBubbles.ts +1411 -1411
  86. package/src/relationship/plugins/RelationshipLegend.ts +100 -100
  87. package/src/relationship/plugins/RelationshipTooltip.ts +66 -66
  88. package/src/relationship/relationshipObservables.ts +49 -49
  89. package/src/series/defaults.ts +221 -221
  90. package/src/series/index.ts +9 -9
  91. package/src/series/plugins/Bubbles.ts +636 -636
  92. package/src/series/plugins/Pie.ts +623 -623
  93. package/src/series/plugins/PieEventTexts.ts +284 -284
  94. package/src/series/plugins/PieLabels.ts +640 -640
  95. package/src/series/plugins/Rose.ts +516 -516
  96. package/src/series/plugins/RoseLabels.ts +600 -600
  97. package/src/series/plugins/SeriesLegend.ts +107 -107
  98. package/src/series/plugins/SeriesTooltip.ts +66 -66
  99. package/src/series/seriesObservables.ts +145 -145
  100. package/src/series/seriesUtils.ts +51 -51
  101. package/src/tree/defaults.ts +102 -102
  102. package/src/tree/index.ts +4 -4
  103. package/src/tree/plugins/TreeLegend.ts +100 -100
  104. package/src/tree/plugins/TreeMap.ts +341 -341
  105. package/src/tree/plugins/TreeTooltip.ts +66 -66
  106. package/src/utils/commonUtils.ts +31 -31
  107. package/src/utils/d3Graphics.ts +176 -176
  108. package/src/utils/d3Utils.ts +92 -92
  109. package/src/utils/observables.ts +14 -14
  110. package/src/utils/orbchartsUtils.ts +129 -129
  111. package/tsconfig.base.json +13 -13
  112. package/tsconfig.json +2 -2
  113. 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
  })