@orbcharts/plugins-basic 3.0.2 → 3.0.3

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