@orbcharts/plugins-basic 3.0.0-alpha.44 → 3.0.0-alpha.45

Sign up to get free protection for your applications and to get access to all the features.
Files changed (155) hide show
  1. package/LICENSE +200 -200
  2. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseBarStack.d.ts +6 -4
  3. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseBars.d.ts +6 -4
  4. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseBarsTriangle.d.ts +7 -4
  5. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseDots.d.ts +5 -3
  6. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseGroupAxis.d.ts +3 -3
  7. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseLineAreas.d.ts +6 -3
  8. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseLines.d.ts +6 -3
  9. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseValueAxis.d.ts +3 -3
  10. package/dist/{src → orbcharts-plugins-basic/src}/grid/gridObservables.d.ts +4 -4
  11. package/dist/orbcharts-plugins-basic/src/index.d.ts +5 -0
  12. package/dist/orbcharts-plugins-basic/src/multiGrid/multiGridObservables.d.ts +8 -0
  13. package/dist/orbcharts-plugins-basic/src/series/plugins/PieEventTexts.d.ts +3 -0
  14. package/dist/orbcharts-plugins-basic/src/series/seriesObservables.d.ts +21 -0
  15. package/dist/{src → orbcharts-plugins-basic/src}/series/seriesUtils.d.ts +3 -3
  16. package/dist/orbcharts-plugins-basic.es.js +7696 -7529
  17. package/dist/orbcharts-plugins-basic.umd.js +8 -8
  18. package/dist/src/index.d.ts +1 -5
  19. package/package.json +42 -42
  20. package/src/base/BaseBarStack.ts +778 -881
  21. package/src/base/BaseBars.ts +764 -750
  22. package/src/base/BaseBarsTriangle.ts +672 -659
  23. package/src/base/BaseDots.ts +502 -639
  24. package/src/base/BaseGroupAxis.ts +496 -496
  25. package/src/base/BaseLegend.ts +641 -641
  26. package/src/base/BaseLineAreas.ts +625 -621
  27. package/src/base/BaseLines.ts +699 -692
  28. package/src/base/BaseValueAxis.ts +478 -479
  29. package/src/base/types.ts +2 -2
  30. package/src/grid/defaults.ts +121 -121
  31. package/src/grid/gridObservables.ts +247 -263
  32. package/src/grid/index.ts +15 -15
  33. package/src/grid/plugins/BarStack.ts +50 -37
  34. package/src/grid/plugins/Bars.ts +51 -37
  35. package/src/grid/plugins/BarsDiverging.ts +41 -39
  36. package/src/grid/plugins/BarsTriangle.ts +50 -34
  37. package/src/grid/plugins/Dots.ts +37 -35
  38. package/src/grid/plugins/GridLegend.ts +59 -59
  39. package/src/grid/plugins/GroupAux.ts +645 -646
  40. package/src/grid/plugins/GroupAxis.ts +42 -30
  41. package/src/grid/plugins/LineAreas.ts +39 -36
  42. package/src/grid/plugins/Lines.ts +38 -35
  43. package/src/grid/plugins/ScalingArea.ts +173 -174
  44. package/src/grid/plugins/ValueAxis.ts +43 -31
  45. package/src/grid/plugins/ValueStackAxis.ts +79 -70
  46. package/src/grid/types.ts +120 -120
  47. package/src/index.ts +9 -9
  48. package/src/multiGrid/defaults.ts +147 -147
  49. package/src/multiGrid/index.ts +11 -11
  50. package/src/multiGrid/multiGridObservables.ts +42 -289
  51. package/src/multiGrid/plugins/MultiBarStack.ts +74 -60
  52. package/src/multiGrid/plugins/MultiBars.ts +73 -59
  53. package/src/multiGrid/plugins/MultiBarsTriangle.ts +73 -58
  54. package/src/multiGrid/plugins/MultiDots.ts +60 -58
  55. package/src/multiGrid/plugins/MultiGridLegend.ts +89 -89
  56. package/src/multiGrid/plugins/MultiGroupAxis.ts +65 -53
  57. package/src/multiGrid/plugins/MultiLineAreas.ts +62 -59
  58. package/src/multiGrid/plugins/MultiLines.ts +61 -58
  59. package/src/multiGrid/plugins/MultiValueAxis.ts +65 -53
  60. package/src/multiGrid/plugins/OverlappingValueAxes.ts +169 -164
  61. package/src/multiGrid/types.ts +67 -67
  62. package/src/noneData/defaults.ts +64 -61
  63. package/src/noneData/index.ts +3 -3
  64. package/src/noneData/plugins/Container.ts +10 -10
  65. package/src/noneData/plugins/Tooltip.ts +310 -310
  66. package/src/noneData/types.ts +26 -26
  67. package/src/series/defaults.ts +109 -99
  68. package/src/series/index.ts +6 -6
  69. package/src/series/plugins/Bubbles.ts +571 -551
  70. package/src/series/plugins/Pie.ts +548 -600
  71. package/src/series/plugins/PieEventTexts.ts +258 -194
  72. package/src/series/plugins/PieLabels.ts +335 -288
  73. package/src/series/plugins/SeriesLegend.ts +59 -59
  74. package/src/series/seriesObservables.ts +145 -0
  75. package/src/series/seriesUtils.ts +50 -50
  76. package/src/series/types.ts +67 -67
  77. package/src/tree/defaults.ts +22 -22
  78. package/src/tree/index.ts +3 -3
  79. package/src/tree/plugins/TreeLegend.ts +59 -59
  80. package/src/tree/plugins/TreeMap.ts +305 -305
  81. package/src/tree/types.ts +23 -23
  82. package/src/utils/commonUtils.ts +21 -21
  83. package/src/utils/d3Graphics.ts +124 -124
  84. package/src/utils/d3Utils.ts +73 -73
  85. package/src/utils/observables.ts +14 -14
  86. package/src/utils/orbchartsUtils.ts +100 -100
  87. package/tsconfig.dev.json +16 -16
  88. package/tsconfig.json +16 -13
  89. package/tsconfig.prod.json +13 -13
  90. package/vite.config.js +49 -49
  91. package/dist/src/multiGrid/multiGridObservables.d.ts +0 -12
  92. package/dist/src/series/plugins/PieEventTexts.d.ts +0 -1
  93. /package/dist/{src → orbcharts-plugins-basic/src}/base/BaseGroupArea.d.ts +0 -0
  94. /package/dist/{src → orbcharts-plugins-basic/src}/base/BaseLegend.d.ts +0 -0
  95. /package/dist/{src → orbcharts-plugins-basic/src}/base/types.d.ts +0 -0
  96. /package/dist/{src → orbcharts-plugins-basic/src}/grid/defaults.d.ts +0 -0
  97. /package/dist/{src → orbcharts-plugins-basic/src}/grid/index.d.ts +0 -0
  98. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/BarStack.d.ts +0 -0
  99. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/Bars.d.ts +0 -0
  100. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/BarsDiverging.d.ts +0 -0
  101. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/BarsTriangle.d.ts +0 -0
  102. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/Dots.d.ts +0 -0
  103. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/GridLegend.d.ts +0 -0
  104. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/GroupAux.d.ts +0 -0
  105. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/GroupAxis.d.ts +0 -0
  106. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/LineAreas.d.ts +0 -0
  107. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/Lines.d.ts +0 -0
  108. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/Ranking.d.ts +0 -0
  109. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/RankingAxis.d.ts +0 -0
  110. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/ScalingArea.d.ts +0 -0
  111. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/ValueAxis.d.ts +0 -0
  112. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/ValueStackAxis.d.ts +0 -0
  113. /package/dist/{src → orbcharts-plugins-basic/src}/grid/types.d.ts +0 -0
  114. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/defaults.d.ts +0 -0
  115. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/index.d.ts +0 -0
  116. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiBarStack.d.ts +0 -0
  117. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiBars.d.ts +0 -0
  118. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiBarsTriangle.d.ts +0 -0
  119. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiDots.d.ts +0 -0
  120. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiGridLegend.d.ts +0 -0
  121. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiGroupAxis.d.ts +0 -0
  122. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiLineAreas.d.ts +0 -0
  123. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiLines.d.ts +0 -0
  124. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiValueAxis.d.ts +0 -0
  125. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/OverlappingValueAxes.d.ts +0 -0
  126. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/types.d.ts +0 -0
  127. /package/dist/{src → orbcharts-plugins-basic/src}/multiValue/index.d.ts +0 -0
  128. /package/dist/{src → orbcharts-plugins-basic/src}/multiValue/plugins/Scatter.d.ts +0 -0
  129. /package/dist/{src → orbcharts-plugins-basic/src}/multiValue/plugins/ScatterAxes.d.ts +0 -0
  130. /package/dist/{src → orbcharts-plugins-basic/src}/noneData/defaults.d.ts +0 -0
  131. /package/dist/{src → orbcharts-plugins-basic/src}/noneData/index.d.ts +0 -0
  132. /package/dist/{src → orbcharts-plugins-basic/src}/noneData/plugins/Container.d.ts +0 -0
  133. /package/dist/{src → orbcharts-plugins-basic/src}/noneData/plugins/Tooltip.d.ts +0 -0
  134. /package/dist/{src → orbcharts-plugins-basic/src}/noneData/types.d.ts +0 -0
  135. /package/dist/{src → orbcharts-plugins-basic/src}/relationship/index.d.ts +0 -0
  136. /package/dist/{src → orbcharts-plugins-basic/src}/relationship/plugins/Relationship.d.ts +0 -0
  137. /package/dist/{src → orbcharts-plugins-basic/src}/series/defaults.d.ts +0 -0
  138. /package/dist/{src → orbcharts-plugins-basic/src}/series/index.d.ts +0 -0
  139. /package/dist/{src → orbcharts-plugins-basic/src}/series/plugins/Bubbles.d.ts +0 -0
  140. /package/dist/{src → orbcharts-plugins-basic/src}/series/plugins/Pie.d.ts +0 -0
  141. /package/dist/{src → orbcharts-plugins-basic/src}/series/plugins/PieLabels.d.ts +0 -0
  142. /package/dist/{src → orbcharts-plugins-basic/src}/series/plugins/SeriesLegend.d.ts +0 -0
  143. /package/dist/{src → orbcharts-plugins-basic/src}/series/plugins/Waffle.d.ts +0 -0
  144. /package/dist/{src → orbcharts-plugins-basic/src}/series/types.d.ts +0 -0
  145. /package/dist/{src → orbcharts-plugins-basic/src}/tree/defaults.d.ts +0 -0
  146. /package/dist/{src → orbcharts-plugins-basic/src}/tree/index.d.ts +0 -0
  147. /package/dist/{src → orbcharts-plugins-basic/src}/tree/plugins/TreeLegend.d.ts +0 -0
  148. /package/dist/{src → orbcharts-plugins-basic/src}/tree/plugins/TreeMap.d.ts +0 -0
  149. /package/dist/{src → orbcharts-plugins-basic/src}/tree/types.d.ts +0 -0
  150. /package/dist/{src → orbcharts-plugins-basic/src}/utils/commonUtils.d.ts +0 -0
  151. /package/dist/{src → orbcharts-plugins-basic/src}/utils/d3Graphics.d.ts +0 -0
  152. /package/dist/{src → orbcharts-plugins-basic/src}/utils/d3Utils.d.ts +0 -0
  153. /package/dist/{src → orbcharts-plugins-basic/src}/utils/observables.d.ts +0 -0
  154. /package/dist/{src → orbcharts-plugins-basic/src}/utils/orbchartsUtils.d.ts +0 -0
  155. /package/dist/{vite.config.d.ts → orbcharts-plugins-basic/vite.config.d.ts} +0 -0
@@ -1,622 +1,626 @@
1
- import * as d3 from 'd3'
2
- import {
3
- combineLatest,
4
- map,
5
- filter,
6
- switchMap,
7
- takeUntil,
8
- distinctUntilChanged,
9
- Observable,
10
- Subject } from 'rxjs'
11
- import type { BasePluginFn } from './types'
12
- import type {
13
- ComputedDatumGrid,
14
- ComputedDataGrid,
15
- DataFormatterGrid,
16
- EventGrid,
17
- ContainerPosition,
18
- ChartParams,
19
- Layout,
20
- TransformData } from '@orbcharts/core'
21
- import { DATA_FORMATTER_VALUE_AXIS } from '@orbcharts/core/src/defaults'
22
- import { createAxisLinearScale } from '@orbcharts/core'
23
- import { getD3TransitionEase } from '../utils/d3Utils'
24
- import { getClassName, getUniID, getMinAndMaxValue } from '../utils/orbchartsUtils'
25
- import { gridGroupPositionFnObservable } from '../grid/gridObservables'
26
- import { gridSelectionsObservable } from '../grid/gridObservables'
27
-
28
- export interface BaseLineAreasParams {
29
- lineCurve: string
30
- // lineWidth: number
31
- linearGradientOpacity: [number, number]
32
- }
33
-
34
- interface BaseLineAreasContext {
35
- selection: d3.Selection<any, unknown, any, unknown>
36
- computedData$: Observable<ComputedDataGrid>
37
- existSeriesLabels$: Observable<string[]>
38
- SeriesDataMap$: Observable<Map<string, ComputedDatumGrid[]>>
39
- GroupDataMap$: Observable<Map<string, ComputedDatumGrid[]>>
40
- fullDataFormatter$: Observable<DataFormatterGrid>
41
- fullParams$: Observable<BaseLineAreasParams>
42
- fullChartParams$: Observable<ChartParams>
43
- gridAxesTransform$: Observable<TransformData>
44
- gridGraphicTransform$: Observable<TransformData>
45
- gridAxesSize$: Observable<{
46
- width: number;
47
- height: number;
48
- }>
49
- gridHighlight$: Observable<ComputedDatumGrid[]>
50
- gridContainer$: Observable<ContainerPosition[]>
51
- layout$: Observable<Layout>
52
- event$: Subject<EventGrid>
53
- }
54
-
55
- type ClipPathDatum = {
56
- id: string;
57
- // x: number;
58
- // y: number;
59
- width: number;
60
- height: number;
61
- }
62
-
63
- // const pluginName = 'Lines'
64
- // const pathClassName = getClassName(pluginName, 'path')
65
-
66
-
67
- function createAreaPath (lineCurve: string = 'curveLinear', valueAxisStart: number): d3.Line<ComputedDatumGrid> {
68
- return d3.area<ComputedDatumGrid>()
69
- .x((d) => d.axisX)
70
- .y0(d => valueAxisStart)
71
- .y1((d) => d.axisY)
72
- .curve((d3 as any)[lineCurve])
73
-
74
- }
75
-
76
- // 依無值的資料分段
77
- function makeSegmentData (data: ComputedDatumGrid[]): ComputedDatumGrid[][] {
78
- let segmentData: ComputedDatumGrid[][] = [[]]
79
-
80
- let currentIndex = 0
81
- for (let i in data) {
82
- if (data[i].visible == false || data[i].value === undefined || data[i].value === null) {
83
- // 換下一段的 index
84
- if (segmentData[currentIndex].length) {
85
- currentIndex ++
86
- segmentData[currentIndex] = []
87
- }
88
- continue
89
- }
90
- segmentData[currentIndex].push(data[i])
91
- }
92
-
93
- return segmentData
94
- }
95
-
96
-
97
- function renderLineAreas ({ selection, pathClassName, segmentData, areaPath, linearGradientIds, params }: {
98
- selection: d3.Selection<SVGGElement, unknown, any, unknown>
99
- pathClassName: string
100
- segmentData: ComputedDatumGrid[][]
101
- areaPath: d3.Line<ComputedDatumGrid>
102
- linearGradientIds: string[]
103
- params: BaseLineAreasParams
104
- }): d3.Selection<SVGPathElement, ComputedDatumGrid[], any, any> {
105
- // if (!data[0]) {
106
- // return undefined
107
- // }
108
-
109
- const lineAreas = selection
110
- .selectAll<SVGPathElement, ComputedDatumGrid[]>('path')
111
- .data(segmentData, (d, i) => d.length ? `${d[0].id}_${d[d.length - 1].id}` : i) // 以線段起迄id結合為線段id
112
- .join(
113
- enter => {
114
- return enter
115
- .append<SVGPathElement>('path')
116
- .classed(pathClassName, true)
117
- .attr("fill","none")
118
- // .attr('pointer-events', 'visibleStroke') // 只對線條產生事件
119
- .style('vector-effect', 'non-scaling-stroke')
120
- .style('cursor', 'pointer')
121
- },
122
- update => update,
123
- exit => exit.remove()
124
- )
125
- // .attr("stroke-width", params.lineWidth)
126
- // .attr("stroke", (d, i) => d[0] && d[0].color)
127
- .attr("fill", (d, i) => d[0] ? `url(#${linearGradientIds[d[0].seriesIndex]})` : '')
128
- .attr("d", (d) => {
129
- return areaPath(d)
130
- })
131
-
132
- return lineAreas
133
- }
134
-
135
- function highlightLineAreas ({ selection, seriesLabel, fullChartParams }: {
136
- selection: d3.Selection<any, string, any, any>
137
- seriesLabel: string | null
138
- fullChartParams: ChartParams
139
- }) {
140
- selection.interrupt('highlight')
141
- if (!seriesLabel) {
142
- // remove highlight
143
- selection
144
- .transition('highlight')
145
- .duration(200)
146
- .style('opacity', 1)
147
- return
148
- }
149
-
150
- selection
151
- .each((currentSeriesLabel, i, n) => {
152
- // const currentSeriesLabel = d[0] ? d[0].seriesLabel : ''
153
-
154
- if (currentSeriesLabel === seriesLabel) {
155
- d3.select(n[i])
156
- .style('opacity', 1)
157
- } else {
158
- d3.select(n[i])
159
- .style('opacity', fullChartParams.styles.unhighlightedOpacity)
160
- }
161
- })
162
- }
163
-
164
- function renderLinearGradient ({ defsSelection, computedData, linearGradientIds, params }: {
165
- defsSelection: d3.Selection<SVGDefsElement, any, any, any>
166
- computedData: ComputedDataGrid
167
- linearGradientIds: string[]
168
- params: BaseLineAreasParams
169
- }) {
170
- defsSelection!
171
- .selectAll<SVGLinearGradientElement, ComputedDatumGrid>('linearGradient')
172
- .data(computedData ?? [])
173
- .join(
174
- enter => {
175
- return enter
176
- .append('linearGradient')
177
- .attr('x1', '0%')
178
- .attr('x2', '0%')
179
- .attr('y1', '100%')
180
- .attr('y2', '0%')
181
- .attr('spreadMethod', 'pad')
182
- },
183
- update => update,
184
- exit => exit.remove()
185
- )
186
- .attr('id', (d, i) => {
187
- return d[0] ? linearGradientIds[d[0].seriesIndex] : ''
188
- })
189
- .html((d, i) => {
190
- const color = d[0] ? d[0].color : ''
191
- return `
192
- <stop offset="0%" stop-color="${color}" stop-opacity="${params.linearGradientOpacity[0]}"/>
193
- <stop offset="100%" stop-color="${color}" stop-opacity="${params.linearGradientOpacity[1]}"/>
194
- `
195
- })
196
-
197
- }
198
-
199
- function renderClipPath ({ defsSelection, clipPathData, transitionDuration, transitionEase }: {
200
- defsSelection: d3.Selection<SVGDefsElement, any, any, any>
201
- clipPathData: ClipPathDatum[]
202
- transitionDuration: number
203
- transitionEase: string
204
- }) {
205
- const clipPath = defsSelection
206
- .selectAll<SVGClipPathElement, Layout>('clipPath')
207
- .data(clipPathData)
208
- .join(
209
- enter => {
210
- return enter
211
- .append('clipPath')
212
- },
213
- update => update,
214
- exit => exit.remove()
215
- )
216
- .attr('id', d => d.id)
217
- .each((d, i, g) => {
218
- const rect = d3.select(g[i])
219
- .selectAll<SVGRectElement, typeof d>('rect')
220
- .data([d])
221
- .join(
222
- enter => {
223
- const enterSelection = enter
224
- .append('rect')
225
- enterSelection
226
- .transition()
227
- .duration(transitionDuration)
228
- .ease(getD3TransitionEase(transitionEase))
229
- // .delay(100) // @Q@ 不知為何如果沒加 delay位置會有點跑掉
230
- .tween('tween', (_d, _i, _g) => {
231
- return (t) => {
232
- const transitionWidth = _d.width * t
233
-
234
- enterSelection
235
- .attr('x', 0)
236
- .attr('y', 0)
237
- .attr('width', _d => transitionWidth)
238
- .attr('height', _d => _d.height)
239
- }
240
- })
241
- return enterSelection
242
- },
243
- update => {
244
- return update
245
- .attr('x', 0)
246
- .attr('y', 0)
247
- .attr('width', _d => _d.width)
248
- .attr('height', _d => _d.height)
249
- },
250
- exit => exit.remove()
251
- )
252
- })
253
-
254
- }
255
-
256
- export const createBaseLineAreas: BasePluginFn<BaseLineAreasContext> = (pluginName: string, {
257
- selection,
258
- computedData$,
259
- existSeriesLabels$,
260
- SeriesDataMap$,
261
- GroupDataMap$,
262
- fullParams$,
263
- fullDataFormatter$,
264
- fullChartParams$,
265
- gridAxesTransform$,
266
- gridGraphicTransform$,
267
- gridAxesSize$,
268
- gridHighlight$,
269
- gridContainer$,
270
- layout$,
271
- event$
272
- }) => {
273
-
274
- const destroy$ = new Subject()
275
-
276
- const clipPathID = getUniID(pluginName, 'clipPath-box')
277
- const pathClassName = getClassName(pluginName, 'path')
278
-
279
- const {
280
- seriesSelection$,
281
- axesSelection$,
282
- defsSelection$,
283
- graphicGSelection$
284
- } = gridSelectionsObservable({
285
- selection,
286
- pluginName,
287
- clipPathID,
288
- existSeriesLabels$,
289
- gridContainer$,
290
- gridAxesTransform$,
291
- gridGraphicTransform$
292
- })
293
-
294
- // valueAxis 的起始座標
295
- const valueAxisStart$: Observable<number> = gridGraphicTransform$.pipe(
296
- takeUntil(destroy$),
297
- map(data => {
298
- // 抵消掉外層的變型
299
- return - data.translate[1] / data.scale[1]
300
- })
301
- )
302
-
303
- const areaPath$: Observable<d3.Line<ComputedDatumGrid>> = new Observable(subscriber => {
304
- const paramsSubscription = combineLatest({
305
- fullParams: fullParams$,
306
- valueAxisStart: valueAxisStart$
307
- }).pipe(
308
- takeUntil(destroy$)
309
- )
310
- .subscribe(d => {
311
- const areaPath = createAreaPath(d.fullParams.lineCurve, d.valueAxisStart)
312
- subscriber.next(areaPath)
313
- })
314
- return () => {
315
- paramsSubscription.unsubscribe()
316
- }
317
- })
318
-
319
- // 顯示範圍內的series labels
320
- const seriesLabels$: Observable<string[]> = new Observable(subscriber => {
321
- computedData$.pipe(
322
- takeUntil(destroy$),
323
- // 轉換後會退訂前一個未完成的訂閱事件,因此可以取到「同時間」最後一次的訂閱事件
324
- switchMap(async (d) => d),
325
- ).subscribe(data => {
326
- const labels = data[0] && data[0][0]
327
- ? data.map(d => d[0].seriesLabel)
328
- : []
329
- subscriber.next(labels)
330
- })
331
- })
332
-
333
- // const axisSize$ = gridAxisSizeObservable({
334
- // fullDataFormatter$,
335
- // computedLayout$
336
- // })
337
-
338
- const transitionDuration$ = fullChartParams$
339
- .pipe(
340
- map(d => d.transitionDuration),
341
- distinctUntilChanged()
342
- )
343
-
344
- const transitionEase$ = fullChartParams$
345
- .pipe(
346
- map(d => d.transitionEase),
347
- distinctUntilChanged()
348
- )
349
-
350
- const clipPathSubscription = combineLatest({
351
- defsSelection: defsSelection$,
352
- seriesLabels: seriesLabels$,
353
- axisSize: gridAxesSize$,
354
- transitionDuration: transitionDuration$,
355
- transitionEase: transitionEase$
356
- }).pipe(
357
- takeUntil(destroy$),
358
- // 轉換後會退訂前一個未完成的訂閱事件,因此可以取到「同時間」最後一次的訂閱事件
359
- switchMap(async (d) => d),
360
- ).subscribe(data => {
361
- // 外層的遮罩
362
- const clipPathBox = [{
363
- id: clipPathID,
364
- width: data.axisSize.width,
365
- height: data.axisSize.height
366
- }]
367
- // 各別線條的遮罩(各別動畫)
368
- const clipPathData = clipPathBox.concat(
369
- data.seriesLabels.map(d => {
370
- return {
371
- id: `orbcharts__clipPath_${d}`,
372
- width: data.axisSize.width,
373
- height: data.axisSize.height
374
- }
375
- })
376
- )
377
- renderClipPath({
378
- defsSelection: data.defsSelection,
379
- clipPathData,
380
- transitionDuration: data.transitionDuration,
381
- transitionEase: data.transitionEase
382
- })
383
- })
384
-
385
- // const SeriesDataMap$ = computedData$.pipe(
386
- // map(d => makeGridSeriesDataMap(d))
387
- // )
388
-
389
- // const GroupDataMap$ = computedData$.pipe(
390
- // map(d => makeGridGroupDataMap(d))
391
- // )
392
-
393
- const DataMap$ = computedData$.pipe(
394
- map(d => {
395
- const DataMap: Map<string, ComputedDatumGrid> = new Map()
396
- d.flat().forEach(_d => DataMap.set(_d.id, _d))
397
- return DataMap
398
- })
399
- )
400
-
401
- // 取得事件座標的group資料
402
- const gridGroupPositionFn$ = gridGroupPositionFnObservable({
403
- fullDataFormatter$,
404
- gridAxesSize$: gridAxesSize$,
405
- computedData$: computedData$,
406
- fullChartParams$: fullChartParams$
407
- })
408
-
409
- const highlightTarget$ = fullChartParams$.pipe(
410
- takeUntil(destroy$),
411
- map(d => d.highlightTarget),
412
- distinctUntilChanged()
413
- )
414
-
415
- const linearGradientIds$ = seriesLabels$.pipe(
416
- takeUntil(destroy$),
417
- map(d => d.map((d, i) => {
418
- return getUniID(pluginName, `lineargradient-${d}`)
419
- }))
420
- )
421
-
422
- const graphSubscription = combineLatest({
423
- graphicGSelection: graphicGSelection$,
424
- defsSelection: defsSelection$,
425
- seriesLabels: seriesLabels$,
426
- computedData: computedData$,
427
- linearGradientIds: linearGradientIds$,
428
- SeriesDataMap: SeriesDataMap$,
429
- GroupDataMap: GroupDataMap$,
430
- areaPath: areaPath$,
431
- params: fullParams$,
432
- highlightTarget: highlightTarget$,
433
- gridGroupPositionFn: gridGroupPositionFn$,
434
- }).pipe(
435
- takeUntil(destroy$),
436
- // 轉換後會退訂前一個未完成的訂閱事件,因此可以取到「同時間」最後一次的訂閱事件
437
- switchMap(async (d) => d),
438
- ).subscribe(data => {
439
-
440
- // const updateGraphic = data.graphicGSelection
441
- // .selectAll<SVGGElement, number>('g')
442
- // .data(data.seriesLabels, (d, i) => d)
443
- // const enterGraphic = updateGraphic.enter()
444
- // .append('g')
445
- // .classed(graphicClassName, true)
446
- // updateGraphic.exit().remove()
447
- // const graphicSelection = updateGraphic.merge(enterGraphic)
448
- // .attr('clip-path', (d, i) => `url(#orbcharts__clipPath_${d})`)
449
-
450
- // 繪圖
451
- data.graphicGSelection.each((d, i, all) => {
452
- // 將資料分段
453
- const segmentData = makeSegmentData(data.computedData[i] ?? [])
454
-
455
- const pathSelection = renderLineAreas({
456
- selection: d3.select(all[i]),
457
- pathClassName,
458
- areaPath: data.areaPath,
459
- segmentData: segmentData,
460
- linearGradientIds: data.linearGradientIds,
461
- params: data.params
462
- })
463
- renderLinearGradient({
464
- defsSelection: data.defsSelection,
465
- computedData: data.computedData,
466
- linearGradientIds: data.linearGradientIds,
467
- params: data.params
468
- })
469
-
470
- pathSelection
471
- .on('mouseover', (event, datum) => {
472
- event.stopPropagation()
473
-
474
- const seriesLabel = datum[0] ? datum[0].seriesLabel : ''
475
- const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
476
- const groupData = data.GroupDataMap.get(groupLabel)!
477
- const targetDatum = groupData.find(d => d.seriesLabel === seriesLabel)
478
- const _datum = targetDatum ?? datum[0]
479
-
480
- event$.next({
481
- type: 'grid',
482
- eventName: 'mouseover',
483
- pluginName,
484
- highlightTarget: data.highlightTarget,
485
- datum: _datum,
486
- gridIndex: _datum.gridIndex,
487
- series: data.SeriesDataMap.get(_datum.seriesLabel)!,
488
- seriesIndex: _datum.seriesIndex,
489
- seriesLabel: _datum.seriesLabel,
490
- groups: data.GroupDataMap.get(_datum.groupLabel)!,
491
- groupIndex: _datum.groupIndex,
492
- groupLabel: _datum.groupLabel,
493
- event,
494
- data: data.computedData
495
- })
496
- })
497
- .on('mousemove', (event, datum) => {
498
- event.stopPropagation()
499
-
500
- const seriesLabel = datum[0] ? datum[0].seriesLabel : ''
501
- const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
502
- const groupData = data.GroupDataMap.get(groupLabel)!
503
- const targetDatum = groupData.find(d => d.seriesLabel === seriesLabel)
504
- const _datum = targetDatum ?? datum[0]
505
-
506
- event$.next({
507
- type: 'grid',
508
- eventName: 'mousemove',
509
- pluginName,
510
- highlightTarget: data.highlightTarget,
511
- datum: _datum,
512
- gridIndex: _datum.gridIndex,
513
- series: data.SeriesDataMap.get(_datum.seriesLabel)!,
514
- seriesIndex: _datum.seriesIndex,
515
- seriesLabel: _datum.seriesLabel,
516
- groups: data.GroupDataMap.get(_datum.groupLabel)!,
517
- groupIndex: _datum.groupIndex,
518
- groupLabel: _datum.groupLabel,
519
- event,
520
- data: data.computedData
521
- })
522
- })
523
- .on('mouseout', (event, datum) => {
524
- event.stopPropagation()
525
-
526
- const seriesLabel = datum[0] ? datum[0].seriesLabel : ''
527
- const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
528
- const groupData = data.GroupDataMap.get(groupLabel)!
529
- const targetDatum = groupData.find(d => d.seriesLabel === seriesLabel)
530
- const _datum = targetDatum ?? datum[0]
531
-
532
- event$.next({
533
- type: 'grid',
534
- eventName: 'mouseout',
535
- pluginName,
536
- highlightTarget: data.highlightTarget,
537
- datum: _datum,
538
- gridIndex: _datum.gridIndex,
539
- series: data.SeriesDataMap.get(_datum.seriesLabel)!,
540
- seriesIndex: _datum.seriesIndex,
541
- seriesLabel: _datum.seriesLabel,
542
- groups: data.GroupDataMap.get(_datum.groupLabel)!,
543
- groupIndex: _datum.groupIndex,
544
- groupLabel: _datum.groupLabel,
545
- event,
546
- data: data.computedData
547
- })
548
- })
549
- .on('click', (event, datum) => {
550
- event.stopPropagation()
551
-
552
- const seriesLabel = datum[0] ? datum[0].seriesLabel : ''
553
- const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
554
- const groupData = data.GroupDataMap.get(groupLabel)!
555
- const targetDatum = groupData.find(d => d.seriesLabel === seriesLabel)
556
- const _datum = targetDatum ?? datum[0]
557
-
558
- event$.next({
559
- type: 'grid',
560
- eventName: 'click',
561
- pluginName,
562
- highlightTarget: data.highlightTarget,
563
- datum: _datum,
564
- gridIndex: _datum.gridIndex,
565
- series: data.SeriesDataMap.get(_datum.seriesLabel)!,
566
- seriesIndex: _datum.seriesIndex,
567
- seriesLabel: _datum.seriesLabel,
568
- groups: data.GroupDataMap.get(_datum.groupLabel)!,
569
- groupIndex: _datum.groupIndex,
570
- groupLabel: _datum.groupLabel,
571
- event,
572
- data: data.computedData
573
- })
574
- })
575
-
576
- })
577
-
578
-
579
-
580
- // graphicSelection$.next(graphicSelection)
581
-
582
-
583
- // pathSelection = renderLineAreas({
584
- // selection: graphicSelection,
585
- // areaPath: d.areaPath,
586
- // data: d.computedData
587
- // })
588
- })
589
-
590
- // const datumList$ = computedData$.pipe(
591
- // takeUntil(destroy$),
592
- // map(d => d.flat())
593
- // )
594
- // const highlight$ = highlightObservable({ datumList$, fullChartParams$, event$: store.event$ })
595
- // const highlightSubscription = gridHighlight$.subscribe()
596
-
597
- fullChartParams$.pipe(
598
- takeUntil(destroy$),
599
- filter(d => d.highlightTarget === 'series'),
600
- switchMap(d => combineLatest({
601
- graphicGSelection: graphicGSelection$,
602
- gridHighlight: gridHighlight$,
603
- DataMap: DataMap$,
604
- fullChartParams: fullChartParams$
605
- }).pipe(
606
- takeUntil(destroy$),
607
- switchMap(async d => d)
608
- ))
609
- ).subscribe(data => {
610
- const seriesLabel = data.gridHighlight[0] ? data.gridHighlight[0].seriesLabel : null
611
- highlightLineAreas({
612
- selection: data.graphicGSelection,
613
- seriesLabel,
614
- fullChartParams: data.fullChartParams
615
- })
616
- })
617
-
618
- return () => {
619
- destroy$.next(undefined)
620
- // highlightSubscription.unsubscribe()
621
- }
1
+ import * as d3 from 'd3'
2
+ import {
3
+ combineLatest,
4
+ map,
5
+ filter,
6
+ switchMap,
7
+ takeUntil,
8
+ distinctUntilChanged,
9
+ Observable,
10
+ Subject } from 'rxjs'
11
+ import type { BasePluginFn } from './types'
12
+ import type {
13
+ ComputedDatumGrid,
14
+ ComputedDataGrid,
15
+ ComputedLayoutDatumGrid,
16
+ ComputedLayoutDataGrid,
17
+ DataFormatterGrid,
18
+ EventGrid,
19
+ GridContainerPosition,
20
+ ChartParams,
21
+ Layout,
22
+ TransformData } from '@orbcharts/core'
23
+ import { createAxisLinearScale } from '@orbcharts/core'
24
+ import { getD3TransitionEase } from '../utils/d3Utils'
25
+ import { getClassName, getUniID, getMinAndMaxValue } from '../utils/orbchartsUtils'
26
+ import { gridGroupPositionFnObservable } from '../grid/gridObservables'
27
+ import { gridSelectionsObservable } from '../grid/gridObservables'
28
+
29
+ export interface BaseLineAreasParams {
30
+ lineCurve: string
31
+ // lineWidth: number
32
+ linearGradientOpacity: [number, number]
33
+ }
34
+
35
+ interface BaseLineAreasContext {
36
+ selection: d3.Selection<any, unknown, any, unknown>
37
+ computedData$: Observable<ComputedDataGrid>
38
+ computedLayoutData$: Observable<ComputedLayoutDataGrid>
39
+ visibleComputedData$: Observable<ComputedDatumGrid[][]>
40
+ visibleComputedLayoutData$: Observable<ComputedLayoutDataGrid>
41
+ seriesLabels$: Observable<string[]>
42
+ SeriesDataMap$: Observable<Map<string, ComputedDatumGrid[]>>
43
+ GroupDataMap$: Observable<Map<string, ComputedDatumGrid[]>>
44
+ fullDataFormatter$: Observable<DataFormatterGrid>
45
+ fullParams$: Observable<BaseLineAreasParams>
46
+ fullChartParams$: Observable<ChartParams>
47
+ gridAxesTransform$: Observable<TransformData>
48
+ gridGraphicTransform$: Observable<TransformData>
49
+ gridAxesSize$: Observable<{
50
+ width: number;
51
+ height: number;
52
+ }>
53
+ gridHighlight$: Observable<ComputedDatumGrid[]>
54
+ gridContainerPosition$: Observable<GridContainerPosition[]>
55
+ layout$: Observable<Layout>
56
+ event$: Subject<EventGrid>
57
+ }
58
+
59
+ type ClipPathDatum = {
60
+ id: string;
61
+ // x: number;
62
+ // y: number;
63
+ width: number;
64
+ height: number;
65
+ }
66
+
67
+ // const pluginName = 'Lines'
68
+ // const pathClassName = getClassName(pluginName, 'path')
69
+
70
+
71
+ function createAreaPath (lineCurve: string = 'curveLinear', valueAxisStart: number): d3.Line<ComputedLayoutDatumGrid> {
72
+ return d3.area<ComputedLayoutDatumGrid>()
73
+ .x((d) => d.axisX)
74
+ .y0(d => valueAxisStart)
75
+ .y1((d) => d.axisY)
76
+ .curve((d3 as any)[lineCurve])
77
+
78
+ }
79
+
80
+ // 依無值的資料分段
81
+ function makeSegmentData (data: ComputedLayoutDatumGrid[]): ComputedLayoutDatumGrid[][] {
82
+ let segmentData: ComputedLayoutDatumGrid[][] = [[]]
83
+
84
+ let currentIndex = 0
85
+ for (let i in data) {
86
+ if (data[i].visible == false || data[i].value === undefined || data[i].value === null) {
87
+ // 換下一段的 index
88
+ if (segmentData[currentIndex].length) {
89
+ currentIndex ++
90
+ segmentData[currentIndex] = []
91
+ }
92
+ continue
93
+ }
94
+ segmentData[currentIndex].push(data[i])
95
+ }
96
+
97
+ return segmentData
98
+ }
99
+
100
+
101
+ function renderLineAreas ({ selection, pathClassName, segmentData, areaPath, linearGradientIds, params }: {
102
+ selection: d3.Selection<SVGGElement, unknown, any, unknown>
103
+ pathClassName: string
104
+ segmentData: ComputedLayoutDatumGrid[][]
105
+ areaPath: d3.Line<ComputedLayoutDatumGrid>
106
+ linearGradientIds: string[]
107
+ params: BaseLineAreasParams
108
+ }): d3.Selection<SVGPathElement, ComputedLayoutDatumGrid[], any, any> {
109
+ // if (!data[0]) {
110
+ // return undefined
111
+ // }
112
+
113
+ const lineAreas = selection
114
+ .selectAll<SVGPathElement, ComputedLayoutDatumGrid[]>('path')
115
+ .data(segmentData, (d, i) => d.length ? `${d[0].id}_${d[d.length - 1].id}` : i) // 以線段起迄id結合為線段id
116
+ .join(
117
+ enter => {
118
+ return enter
119
+ .append<SVGPathElement>('path')
120
+ .classed(pathClassName, true)
121
+ .attr("fill","none")
122
+ // .attr('pointer-events', 'visibleStroke') // 只對線條產生事件
123
+ .style('vector-effect', 'non-scaling-stroke')
124
+ .style('cursor', 'pointer')
125
+ },
126
+ update => update,
127
+ exit => exit.remove()
128
+ )
129
+ // .attr("stroke-width", params.lineWidth)
130
+ // .attr("stroke", (d, i) => d[0] && d[0].color)
131
+ .attr("fill", (d, i) => d[0] ? `url(#${linearGradientIds[d[0].seriesIndex]})` : '')
132
+ .attr("d", (d) => {
133
+ return areaPath(d)
134
+ })
135
+
136
+ return lineAreas
137
+ }
138
+
139
+ function highlightLineAreas ({ selection, seriesLabel, fullChartParams }: {
140
+ selection: d3.Selection<any, string, any, any>
141
+ seriesLabel: string | null
142
+ fullChartParams: ChartParams
143
+ }) {
144
+ selection.interrupt('highlight')
145
+ if (!seriesLabel) {
146
+ // remove highlight
147
+ selection
148
+ .transition('highlight')
149
+ .duration(200)
150
+ .style('opacity', 1)
151
+ return
152
+ }
153
+
154
+ selection
155
+ .each((currentSeriesLabel, i, n) => {
156
+ // const currentSeriesLabel = d[0] ? d[0].seriesLabel : ''
157
+
158
+ if (currentSeriesLabel === seriesLabel) {
159
+ d3.select(n[i])
160
+ .style('opacity', 1)
161
+ } else {
162
+ d3.select(n[i])
163
+ .style('opacity', fullChartParams.styles.unhighlightedOpacity)
164
+ }
165
+ })
166
+ }
167
+
168
+ function renderLinearGradient ({ defsSelection, computedData, linearGradientIds, params }: {
169
+ defsSelection: d3.Selection<SVGDefsElement, any, any, any>
170
+ computedData: ComputedDataGrid
171
+ linearGradientIds: string[]
172
+ params: BaseLineAreasParams
173
+ }) {
174
+ defsSelection!
175
+ .selectAll<SVGLinearGradientElement, ComputedDatumGrid>('linearGradient')
176
+ .data(computedData ?? [])
177
+ .join(
178
+ enter => {
179
+ return enter
180
+ .append('linearGradient')
181
+ .attr('x1', '0%')
182
+ .attr('x2', '0%')
183
+ .attr('y1', '100%')
184
+ .attr('y2', '0%')
185
+ .attr('spreadMethod', 'pad')
186
+ },
187
+ update => update,
188
+ exit => exit.remove()
189
+ )
190
+ .attr('id', (d, i) => {
191
+ return d[0] ? linearGradientIds[d[0].seriesIndex] : ''
192
+ })
193
+ .html((d, i) => {
194
+ const color = d[0] ? d[0].color : ''
195
+ return `
196
+ <stop offset="0%" stop-color="${color}" stop-opacity="${params.linearGradientOpacity[0]}"/>
197
+ <stop offset="100%" stop-color="${color}" stop-opacity="${params.linearGradientOpacity[1]}"/>
198
+ `
199
+ })
200
+
201
+ }
202
+
203
+ function renderClipPath ({ defsSelection, clipPathData, transitionDuration, transitionEase }: {
204
+ defsSelection: d3.Selection<SVGDefsElement, any, any, any>
205
+ clipPathData: ClipPathDatum[]
206
+ transitionDuration: number
207
+ transitionEase: string
208
+ }) {
209
+ const clipPath = defsSelection
210
+ .selectAll<SVGClipPathElement, Layout>('clipPath')
211
+ .data(clipPathData)
212
+ .join(
213
+ enter => {
214
+ return enter
215
+ .append('clipPath')
216
+ },
217
+ update => update,
218
+ exit => exit.remove()
219
+ )
220
+ .attr('id', d => d.id)
221
+ .each((d, i, g) => {
222
+ const rect = d3.select(g[i])
223
+ .selectAll<SVGRectElement, typeof d>('rect')
224
+ .data([d])
225
+ .join(
226
+ enter => {
227
+ const enterSelection = enter
228
+ .append('rect')
229
+ enterSelection
230
+ .transition()
231
+ .duration(transitionDuration)
232
+ .ease(getD3TransitionEase(transitionEase))
233
+ // .delay(100) // @Q@ 不知為何如果沒加 delay位置會有點跑掉
234
+ .tween('tween', (_d, _i, _g) => {
235
+ return (t) => {
236
+ const transitionWidth = _d.width * t
237
+
238
+ enterSelection
239
+ .attr('x', 0)
240
+ .attr('y', 0)
241
+ .attr('width', _d => transitionWidth)
242
+ .attr('height', _d => _d.height)
243
+ }
244
+ })
245
+ return enterSelection
246
+ },
247
+ update => {
248
+ return update
249
+ .attr('x', 0)
250
+ .attr('y', 0)
251
+ .attr('width', _d => _d.width)
252
+ .attr('height', _d => _d.height)
253
+ },
254
+ exit => exit.remove()
255
+ )
256
+ })
257
+
258
+ }
259
+
260
+ export const createBaseLineAreas: BasePluginFn<BaseLineAreasContext> = (pluginName: string, {
261
+ selection,
262
+ computedData$,
263
+ computedLayoutData$,
264
+ visibleComputedData$,
265
+ visibleComputedLayoutData$,
266
+ seriesLabels$,
267
+ SeriesDataMap$,
268
+ GroupDataMap$,
269
+ fullParams$,
270
+ fullDataFormatter$,
271
+ fullChartParams$,
272
+ gridAxesTransform$,
273
+ gridGraphicTransform$,
274
+ gridAxesSize$,
275
+ gridHighlight$,
276
+ gridContainerPosition$,
277
+ layout$,
278
+ event$
279
+ }) => {
280
+
281
+ const destroy$ = new Subject()
282
+
283
+ const clipPathID = getUniID(pluginName, 'clipPath-box')
284
+ const pathClassName = getClassName(pluginName, 'path')
285
+
286
+ const {
287
+ seriesSelection$,
288
+ axesSelection$,
289
+ defsSelection$,
290
+ graphicGSelection$
291
+ } = gridSelectionsObservable({
292
+ selection,
293
+ pluginName,
294
+ clipPathID,
295
+ seriesLabels$,
296
+ gridContainerPosition$,
297
+ gridAxesTransform$,
298
+ gridGraphicTransform$
299
+ })
300
+
301
+ // valueAxis 的起始座標
302
+ const valueAxisStart$: Observable<number> = gridGraphicTransform$.pipe(
303
+ takeUntil(destroy$),
304
+ map(data => {
305
+ // 抵消掉外層的變型
306
+ return - data.translate[1] / data.scale[1]
307
+ })
308
+ )
309
+
310
+ const areaPath$: Observable<d3.Line<ComputedLayoutDatumGrid>> = new Observable(subscriber => {
311
+ const paramsSubscription = combineLatest({
312
+ fullParams: fullParams$,
313
+ valueAxisStart: valueAxisStart$
314
+ }).pipe(
315
+ takeUntil(destroy$)
316
+ )
317
+ .subscribe(d => {
318
+ const areaPath = createAreaPath(d.fullParams.lineCurve, d.valueAxisStart)
319
+ subscriber.next(areaPath)
320
+ })
321
+ return () => {
322
+ paramsSubscription.unsubscribe()
323
+ }
324
+ })
325
+
326
+ // // 顯示範圍內的series labels
327
+ // const seriesLabels$: Observable<string[]> = new Observable(subscriber => {
328
+ // computedData$.pipe(
329
+ // takeUntil(destroy$),
330
+ // switchMap(async (d) => d),
331
+ // ).subscribe(data => {
332
+ // const labels = data[0] && data[0][0]
333
+ // ? data.map(d => d[0].seriesLabel)
334
+ // : []
335
+ // subscriber.next(labels)
336
+ // })
337
+ // })
338
+
339
+ // const axisSize$ = gridAxisSizeObservable({
340
+ // fullDataFormatter$,
341
+ // computedLayout$
342
+ // })
343
+
344
+ const transitionDuration$ = fullChartParams$
345
+ .pipe(
346
+ map(d => d.transitionDuration),
347
+ distinctUntilChanged()
348
+ )
349
+
350
+ const transitionEase$ = fullChartParams$
351
+ .pipe(
352
+ map(d => d.transitionEase),
353
+ distinctUntilChanged()
354
+ )
355
+
356
+ const clipPathSubscription = combineLatest({
357
+ defsSelection: defsSelection$,
358
+ seriesLabels: seriesLabels$,
359
+ axisSize: gridAxesSize$,
360
+ transitionDuration: transitionDuration$,
361
+ transitionEase: transitionEase$
362
+ }).pipe(
363
+ takeUntil(destroy$),
364
+ switchMap(async (d) => d),
365
+ ).subscribe(data => {
366
+ // 外層的遮罩
367
+ const clipPathBox = [{
368
+ id: clipPathID,
369
+ width: data.axisSize.width,
370
+ height: data.axisSize.height
371
+ }]
372
+ // 各別線條的遮罩(各別動畫)
373
+ const clipPathData = clipPathBox.concat(
374
+ data.seriesLabels.map(d => {
375
+ return {
376
+ id: `orbcharts__clipPath_${d}`,
377
+ width: data.axisSize.width,
378
+ height: data.axisSize.height
379
+ }
380
+ })
381
+ )
382
+ renderClipPath({
383
+ defsSelection: data.defsSelection,
384
+ clipPathData,
385
+ transitionDuration: data.transitionDuration,
386
+ transitionEase: data.transitionEase
387
+ })
388
+ })
389
+
390
+ // const SeriesDataMap$ = computedData$.pipe(
391
+ // map(d => makeGridSeriesDataMap(d))
392
+ // )
393
+
394
+ // const GroupDataMap$ = computedData$.pipe(
395
+ // map(d => makeGridGroupDataMap(d))
396
+ // )
397
+
398
+ const DataMap$ = computedData$.pipe(
399
+ map(d => {
400
+ const DataMap: Map<string, ComputedDatumGrid> = new Map()
401
+ d.flat().forEach(_d => DataMap.set(_d.id, _d))
402
+ return DataMap
403
+ })
404
+ )
405
+
406
+ // 取得事件座標的group資料
407
+ const gridGroupPositionFn$ = gridGroupPositionFnObservable({
408
+ fullDataFormatter$,
409
+ gridAxesSize$: gridAxesSize$,
410
+ computedData$: computedData$,
411
+ fullChartParams$: fullChartParams$
412
+ })
413
+
414
+ const highlightTarget$ = fullChartParams$.pipe(
415
+ takeUntil(destroy$),
416
+ map(d => d.highlightTarget),
417
+ distinctUntilChanged()
418
+ )
419
+
420
+ const linearGradientIds$ = seriesLabels$.pipe(
421
+ takeUntil(destroy$),
422
+ map(d => d.map((d, i) => {
423
+ return getUniID(pluginName, `lineargradient-${d}`)
424
+ }))
425
+ )
426
+
427
+ const pathSelectionArr$ = combineLatest({
428
+ graphicGSelection: graphicGSelection$,
429
+ defsSelection: defsSelection$,
430
+ visibleComputedLayoutData: visibleComputedLayoutData$,
431
+ linearGradientIds: linearGradientIds$,
432
+ areaPath: areaPath$,
433
+ params: fullParams$,
434
+ }).pipe(
435
+ takeUntil(destroy$),
436
+ switchMap(async (d) => d),
437
+ map(data => {
438
+ // const updateGraphic = data.graphicGSelection
439
+ // .selectAll<SVGGElement, number>('g')
440
+ // .data(data.seriesLabels, (d, i) => d)
441
+ // const enterGraphic = updateGraphic.enter()
442
+ // .append('g')
443
+ // .classed(graphicClassName, true)
444
+ // updateGraphic.exit().remove()
445
+ // const graphicSelection = updateGraphic.merge(enterGraphic)
446
+ // .attr('clip-path', (d, i) => `url(#orbcharts__clipPath_${d})`)
447
+ let pathSelectionArr: d3.Selection<SVGPathElement, ComputedLayoutDatumGrid[], any, any>[] = []
448
+
449
+ // 繪圖
450
+ data.graphicGSelection.each((d, i, all) => {
451
+ // 將資料分段
452
+ const segmentData = makeSegmentData(data.visibleComputedLayoutData[i] ?? [])
453
+
454
+ pathSelectionArr[i] = renderLineAreas({
455
+ selection: d3.select(all[i]),
456
+ pathClassName,
457
+ areaPath: data.areaPath,
458
+ segmentData: segmentData,
459
+ linearGradientIds: data.linearGradientIds,
460
+ params: data.params
461
+ })
462
+ renderLinearGradient({
463
+ defsSelection: data.defsSelection,
464
+ computedData: data.visibleComputedLayoutData,
465
+ linearGradientIds: data.linearGradientIds,
466
+ params: data.params
467
+ })
468
+ })
469
+
470
+ return pathSelectionArr
471
+ })
472
+ )
473
+
474
+ combineLatest({
475
+ pathSelectionArr: pathSelectionArr$,
476
+ computedData: computedData$,
477
+ SeriesDataMap: SeriesDataMap$,
478
+ GroupDataMap: GroupDataMap$,
479
+ highlightTarget: highlightTarget$,
480
+ gridGroupPositionFn: gridGroupPositionFn$,
481
+ }).pipe(
482
+ takeUntil(destroy$),
483
+ switchMap(async (d) => d),
484
+ ).subscribe(data => {
485
+ data.pathSelectionArr.forEach(pathSelection => {
486
+ pathSelection
487
+ .on('mouseover', (event, datum) => {
488
+ event.stopPropagation()
489
+
490
+ const seriesLabel = datum[0] ? datum[0].seriesLabel : ''
491
+ const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
492
+ const groupData = data.GroupDataMap.get(groupLabel)!
493
+ const targetDatum = groupData.find(d => d.seriesLabel === seriesLabel)
494
+ const _datum = targetDatum ?? datum[0]
495
+
496
+ event$.next({
497
+ type: 'grid',
498
+ eventName: 'mouseover',
499
+ pluginName,
500
+ highlightTarget: data.highlightTarget,
501
+ datum: _datum,
502
+ gridIndex: _datum.gridIndex,
503
+ series: data.SeriesDataMap.get(_datum.seriesLabel)!,
504
+ seriesIndex: _datum.seriesIndex,
505
+ seriesLabel: _datum.seriesLabel,
506
+ groups: data.GroupDataMap.get(_datum.groupLabel)!,
507
+ groupIndex: _datum.groupIndex,
508
+ groupLabel: _datum.groupLabel,
509
+ event,
510
+ data: data.computedData
511
+ })
512
+ })
513
+ .on('mousemove', (event, datum) => {
514
+ event.stopPropagation()
515
+
516
+ const seriesLabel = datum[0] ? datum[0].seriesLabel : ''
517
+ const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
518
+ const groupData = data.GroupDataMap.get(groupLabel)!
519
+ const targetDatum = groupData.find(d => d.seriesLabel === seriesLabel)
520
+ const _datum = targetDatum ?? datum[0]
521
+
522
+ event$.next({
523
+ type: 'grid',
524
+ eventName: 'mousemove',
525
+ pluginName,
526
+ highlightTarget: data.highlightTarget,
527
+ datum: _datum,
528
+ gridIndex: _datum.gridIndex,
529
+ series: data.SeriesDataMap.get(_datum.seriesLabel)!,
530
+ seriesIndex: _datum.seriesIndex,
531
+ seriesLabel: _datum.seriesLabel,
532
+ groups: data.GroupDataMap.get(_datum.groupLabel)!,
533
+ groupIndex: _datum.groupIndex,
534
+ groupLabel: _datum.groupLabel,
535
+ event,
536
+ data: data.computedData
537
+ })
538
+ })
539
+ .on('mouseout', (event, datum) => {
540
+ event.stopPropagation()
541
+
542
+ const seriesLabel = datum[0] ? datum[0].seriesLabel : ''
543
+ const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
544
+ const groupData = data.GroupDataMap.get(groupLabel)!
545
+ const targetDatum = groupData.find(d => d.seriesLabel === seriesLabel)
546
+ const _datum = targetDatum ?? datum[0]
547
+
548
+ event$.next({
549
+ type: 'grid',
550
+ eventName: 'mouseout',
551
+ pluginName,
552
+ highlightTarget: data.highlightTarget,
553
+ datum: _datum,
554
+ gridIndex: _datum.gridIndex,
555
+ series: data.SeriesDataMap.get(_datum.seriesLabel)!,
556
+ seriesIndex: _datum.seriesIndex,
557
+ seriesLabel: _datum.seriesLabel,
558
+ groups: data.GroupDataMap.get(_datum.groupLabel)!,
559
+ groupIndex: _datum.groupIndex,
560
+ groupLabel: _datum.groupLabel,
561
+ event,
562
+ data: data.computedData
563
+ })
564
+ })
565
+ .on('click', (event, datum) => {
566
+ event.stopPropagation()
567
+
568
+ const seriesLabel = datum[0] ? datum[0].seriesLabel : ''
569
+ const { groupIndex, groupLabel } = data.gridGroupPositionFn(event)
570
+ const groupData = data.GroupDataMap.get(groupLabel)!
571
+ const targetDatum = groupData.find(d => d.seriesLabel === seriesLabel)
572
+ const _datum = targetDatum ?? datum[0]
573
+
574
+ event$.next({
575
+ type: 'grid',
576
+ eventName: 'click',
577
+ pluginName,
578
+ highlightTarget: data.highlightTarget,
579
+ datum: _datum,
580
+ gridIndex: _datum.gridIndex,
581
+ series: data.SeriesDataMap.get(_datum.seriesLabel)!,
582
+ seriesIndex: _datum.seriesIndex,
583
+ seriesLabel: _datum.seriesLabel,
584
+ groups: data.GroupDataMap.get(_datum.groupLabel)!,
585
+ groupIndex: _datum.groupIndex,
586
+ groupLabel: _datum.groupLabel,
587
+ event,
588
+ data: data.computedData
589
+ })
590
+ })
591
+ })
592
+ })
593
+
594
+ // const datumList$ = computedData$.pipe(
595
+ // takeUntil(destroy$),
596
+ // map(d => d.flat())
597
+ // )
598
+ // const highlight$ = highlightObservable({ datumList$, fullChartParams$, event$: store.event$ })
599
+ // const highlightSubscription = gridHighlight$.subscribe()
600
+
601
+ fullChartParams$.pipe(
602
+ takeUntil(destroy$),
603
+ filter(d => d.highlightTarget === 'series'),
604
+ switchMap(d => combineLatest({
605
+ graphicGSelection: graphicGSelection$,
606
+ gridHighlight: gridHighlight$,
607
+ DataMap: DataMap$,
608
+ fullChartParams: fullChartParams$
609
+ }).pipe(
610
+ takeUntil(destroy$),
611
+ switchMap(async d => d)
612
+ ))
613
+ ).subscribe(data => {
614
+ const seriesLabel = data.gridHighlight[0] ? data.gridHighlight[0].seriesLabel : null
615
+ highlightLineAreas({
616
+ selection: data.graphicGSelection,
617
+ seriesLabel,
618
+ fullChartParams: data.fullChartParams
619
+ })
620
+ })
621
+
622
+ return () => {
623
+ destroy$.next(undefined)
624
+ // highlightSubscription.unsubscribe()
625
+ }
622
626
  }