@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,660 +1,673 @@
1
- import * as d3 from 'd3'
2
- import {
3
- combineLatest,
4
- map,
5
- switchMap,
6
- takeUntil,
7
- distinctUntilChanged,
8
- Observable,
9
- Subject } from 'rxjs'
10
- import type { BasePluginFn } from './types'
11
- import type {
12
- ComputedDatumGrid,
13
- ComputedDataGrid,
14
- EventGrid,
15
- ChartParams,
16
- ContainerPosition,
17
- Layout,
18
- TransformData } from '@orbcharts/core'
19
- import { getD3TransitionEase } from '../utils/d3Utils'
20
- import { getClassName, getUniID } from '../utils/orbchartsUtils'
21
- import { gridSelectionsObservable } from '../grid/gridObservables'
22
-
23
- export interface BaseBarsTriangleParams {
24
- barWidth: number
25
- barPadding: number
26
- barGroupPadding: number // 群組和群組間的間隔
27
- linearGradientOpacity: [number, number]
28
- }
29
-
30
- interface BaseBarsContext {
31
- selection: d3.Selection<any, unknown, any, unknown>
32
- computedData$: Observable<ComputedDataGrid>
33
- visibleComputedData$: Observable<ComputedDatumGrid[][]>
34
- existSeriesLabels$: Observable<string[]>
35
- SeriesDataMap$: Observable<Map<string, ComputedDatumGrid[]>>
36
- GroupDataMap$: Observable<Map<string, ComputedDatumGrid[]>>
37
- fullParams$: Observable<BaseBarsTriangleParams>
38
- fullChartParams$: Observable<ChartParams>
39
- gridAxesTransform$: Observable<TransformData>
40
- gridGraphicTransform$: Observable<TransformData>
41
- gridAxesSize$: Observable<{
42
- width: number;
43
- height: number;
44
- }>
45
- gridHighlight$: Observable<ComputedDatumGrid[]>
46
- gridContainer$: Observable<ContainerPosition[]>
47
- isSeriesPositionSeprate$: Observable<boolean>
48
- event$: Subject<EventGrid>
49
- }
50
-
51
-
52
- interface RenderBarParams {
53
- graphicGSelection: d3.Selection<SVGGElement, unknown, any, any>
54
- pathGClassName: string
55
- pathClassName: string
56
- computedData: ComputedDataGrid
57
- linearGradientIds: string[]
58
- zeroYArr: number[]
59
- groupLabels: string[]
60
- barScale: d3.ScalePoint<string>
61
- params: BaseBarsTriangleParams
62
- chartParams: ChartParams
63
- barWidth: number
64
- delayGroup: number
65
- transitionItem: number
66
- isSeriesPositionSeprate: boolean
67
- }
68
-
69
- // interface BarDatumGrid extends ComputedDatumGrid {
70
- // linearGradientId: string
71
- // }
72
-
73
- type ClipPathDatum = {
74
- id: string;
75
- // x: number;
76
- // y: number;
77
- width: number;
78
- height: number;
79
- }
80
-
81
- // const pluginName = 'BaseBarsTriangle'
82
- // const pathGClassName = getClassName(pluginName, 'pathG')
83
- // const pathClassName = getClassName(pluginName, 'path')
84
- // group的delay在動畫中的佔比(剩餘部份的時間為圖形本身的動畫時間,因為delay時間和最後一個group的動畫時間加總為1)
85
- const groupDelayProportionOfDuration = 0.3
86
-
87
- function calcBarWidth ({ axisWidth, groupAmount, barAmountOfGroup, barPadding = 0, barGroupPadding = 0 }: {
88
- axisWidth: number
89
- groupAmount: number
90
- barAmountOfGroup: number
91
- barPadding: number
92
- barGroupPadding: number
93
- }) {
94
- const eachGroupWidth = axisWidth / (groupAmount - 1)
95
- const width = (eachGroupWidth - barGroupPadding) / barAmountOfGroup - barPadding
96
- return width > 1 ? width : 1
97
- }
98
-
99
- function makeBarScale (barWidth: number, seriesLabels: string[], params: BaseBarsTriangleParams) {
100
- const barHalfWidth = barWidth! / 2
101
- const barGroupWidth = barWidth * seriesLabels.length + params.barPadding! * seriesLabels.length
102
- return d3.scalePoint()
103
- .domain(seriesLabels)
104
- .range([-barGroupWidth / 2 + barHalfWidth, barGroupWidth / 2 - barHalfWidth])
105
- }
106
-
107
- function calcDelayGroup (barGroupAmount: number, totalDuration: number) {
108
- if (barGroupAmount <= 1) {
109
- // 一筆內計算會出錯所以不算
110
- return 0
111
- }
112
- return totalDuration / (barGroupAmount - 1) * groupDelayProportionOfDuration // 依group數量計算
113
- }
114
-
115
- function calctransitionItem (barGroupAmount: number, totalDuration: number) {
116
- if (barGroupAmount <= 1) {
117
- // 一筆內不會有delay
118
- return totalDuration
119
- }
120
- return totalDuration * (1 - groupDelayProportionOfDuration) // delay後剩餘的時間
121
- }
122
-
123
- function renderTriangleBars ({ graphicGSelection, pathGClassName, pathClassName, computedData, linearGradientIds, zeroYArr, groupLabels, barScale, params, chartParams, barWidth, delayGroup, transitionItem, isSeriesPositionSeprate }: RenderBarParams) {
124
-
125
- const barHalfWidth = barWidth! / 2
126
-
127
- graphicGSelection
128
- .each((d, seriesIndex, g) => {
129
- // g
130
- const gSelection = d3.select(g[seriesIndex])
131
- .selectAll<SVGGElement, ComputedDatumGrid>(`g.${pathGClassName}`)
132
- .data(computedData[seriesIndex] ?? [])
133
- .join(
134
- enter => {
135
- const enterSelection = enter
136
- .append('g')
137
- .classed(pathGClassName, true)
138
- .attr('cursor', 'pointer')
139
- enterSelection
140
- .append('path')
141
- .classed(pathClassName, true)
142
- .style('vector-effect', 'non-scaling-stroke')
143
- .attr('d', (d) => {
144
- const x = -barHalfWidth
145
- const y1 = zeroYArr[seriesIndex]
146
- const y2 = zeroYArr[seriesIndex]
147
- return `M${x},${y1} L${x + (barWidth! / 2)},${y2} ${x + barWidth!},${y1}`
148
- })
149
- return enterSelection
150
- },
151
- update => update,
152
- exit => exit.remove()
153
- )
154
- .attr('transform', d => `translate(${isSeriesPositionSeprate ? 0 : barScale(d.seriesLabel)!}, 0)`)
155
-
156
- // path
157
- gSelection.select(`path.${pathClassName}`)
158
- .attr('height', d => Math.abs(d.axisYFromZero))
159
- .attr('y', d => d.axisY < zeroYArr[seriesIndex] ? d.axisY : zeroYArr[seriesIndex])
160
- .attr('x', d => isSeriesPositionSeprate ? 0 : barScale(d.seriesLabel)!)
161
- // .style('fill', d => `url(#${d.linearGradientId})`)
162
- .style('fill', d => `url(#${linearGradientIds[d.seriesIndex]})`)
163
- .attr('stroke', d => d.color)
164
- .attr('transform', d => `translate(${(d ? d.axisX : 0)}, ${0})`)
165
- .transition()
166
- .duration(transitionItem)
167
- .ease(getD3TransitionEase(chartParams.transitionEase))
168
- .delay((d, i) => d.groupIndex * delayGroup)
169
- // .attr('transform', `translate(${-barHalfWidth}, 0)`)
170
- // .attr('x', d => itemScale(d.itemLabel)!)
171
- // .attr('y', d => -d.y)
172
- .attr('d', (d) => {
173
- const x = -barHalfWidth
174
- const y1 = zeroYArr[seriesIndex]
175
- const y2 = d.axisY
176
- return `M${x},${y1} L${x + (barWidth! / 2)},${y2} ${x + barWidth!},${y1}`
177
- })
178
- })
179
-
180
- const graphicBarSelection: d3.Selection<SVGPathElement, ComputedDatumGrid, any, any> = graphicGSelection.selectAll(`path.${pathClassName}`)
181
-
182
- return graphicBarSelection
183
- }
184
-
185
- function renderLinearGradient ({ defsSelection, computedData, linearGradientIds, params }: {
186
- defsSelection: d3.Selection<SVGDefsElement, any, any, any>
187
- computedData: ComputedDataGrid
188
- linearGradientIds: string[]
189
- params: BaseBarsTriangleParams
190
- }) {
191
- defsSelection!
192
- .selectAll<SVGLinearGradientElement, ComputedDatumGrid>('linearGradient')
193
- .data(computedData ?? [])
194
- .join(
195
- enter => {
196
- return enter
197
- .append('linearGradient')
198
- .attr('x1', '0%')
199
- .attr('x2', '0%')
200
- .attr('y1', '100%')
201
- .attr('y2', '0%')
202
- .attr('spreadMethod', 'pad')
203
- },
204
- update => update,
205
- exit => exit.remove()
206
- )
207
- .attr('id', (d, i) => {
208
- return d[0] ? linearGradientIds[d[0].seriesIndex] : ''
209
- })
210
- .html((d, i) => {
211
- const color = d[0] ? d[0].color : ''
212
- return `
213
- <stop offset="0%" stop-color="${color}" stop-opacity="${params.linearGradientOpacity[0]}"/>
214
- <stop offset="100%" stop-color="${color}" stop-opacity="${params.linearGradientOpacity[1]}"/>
215
- `
216
- })
217
-
218
- }
219
-
220
-
221
- function renderClipPath ({ defsSelection, clipPathData }: {
222
- defsSelection: d3.Selection<SVGDefsElement, any, any, any>
223
- clipPathData: ClipPathDatum[]
224
- }) {
225
- const clipPath = defsSelection
226
- .selectAll<SVGClipPathElement, Layout>('clipPath')
227
- .data(clipPathData)
228
- .join(
229
- enter => {
230
- return enter
231
- .append('clipPath')
232
- },
233
- update => update,
234
- exit => exit.remove()
235
- )
236
- .attr('id', d => d.id)
237
- .each((d, i, g) => {
238
- const rect = d3.select(g[i])
239
- .selectAll<SVGRectElement, typeof d>('rect')
240
- .data([d])
241
- .join(
242
- enter => {
243
- return enter
244
- .append('rect')
245
- },
246
- update => update,
247
- exit => exit.remove()
248
- )
249
- .attr('x', 0)
250
- .attr('y', 0)
251
- .attr('width', _d => _d.width)
252
- .attr('height', _d => _d.height)
253
- })
254
- }
255
-
256
- function highlight ({ selection, ids, fullChartParams }: {
257
- selection: d3.Selection<SVGPathElement, ComputedDatumGrid, any, any>
258
- ids: string[]
259
- fullChartParams: ChartParams
260
- }) {
261
- selection.interrupt('highlight')
262
-
263
- const removeHighlight = () => {
264
- selection
265
- .transition('highlight')
266
- .duration(200)
267
- .style('opacity', 1)
268
- }
269
-
270
- if (!ids.length) {
271
- removeHighlight()
272
- return
273
- }
274
-
275
- selection
276
- .each((d, i, n) => {
277
- if (ids.includes(d.id)) {
278
- d3.select(n[i])
279
- .style('opacity', 1)
280
- } else {
281
- d3.select(n[i])
282
- .style('opacity', fullChartParams.styles.unhighlightedOpacity)
283
- }
284
- })
285
- }
286
-
287
-
288
- export const createBaseBarsTriangle: BasePluginFn<BaseBarsContext> = (pluginName: string, {
289
- selection,
290
- computedData$,
291
- visibleComputedData$,
292
- existSeriesLabels$,
293
- SeriesDataMap$,
294
- GroupDataMap$,
295
- fullParams$,
296
- fullChartParams$,
297
- gridAxesTransform$,
298
- gridGraphicTransform$,
299
- gridAxesSize$,
300
- gridHighlight$,
301
- gridContainer$,
302
- isSeriesPositionSeprate$,
303
- event$
304
- }) => {
305
- const destroy$ = new Subject()
306
-
307
- const clipPathID = getUniID(pluginName, 'clipPath-box')
308
- const pathGClassName = getClassName(pluginName, 'pathG')
309
- const pathClassName = getClassName(pluginName, 'path')
310
-
311
- const {
312
- seriesSelection$,
313
- axesSelection$,
314
- defsSelection$,
315
- graphicGSelection$
316
- } = gridSelectionsObservable({
317
- selection,
318
- pluginName,
319
- clipPathID,
320
- existSeriesLabels$,
321
- gridContainer$,
322
- gridAxesTransform$,
323
- gridGraphicTransform$
324
- })
325
-
326
- // valueAxis 的起始座標
327
- const valueAxisStart$: Observable<number> = gridGraphicTransform$.pipe(
328
- takeUntil(destroy$),
329
- map(data => {
330
- // 抵消掉外層的變型
331
- return - data.translate[1] / data.scale[1]
332
- })
333
- )
334
-
335
- const zeroYArr$ = visibleComputedData$.pipe(
336
- // map(d => d[0] && d[0][0]
337
- // ? d[0][0].axisY - d[0][0].axisYFromZero
338
- // : 0),
339
- map(data => {
340
- return data.map(d => {
341
- return d[0] ? d[0].axisY - d[0].axisYFromZero : 0
342
- })
343
- }),
344
- distinctUntilChanged()
345
- )
346
-
347
- const barWidth$ = combineLatest({
348
- computedData: computedData$,
349
- visibleComputedData: visibleComputedData$,
350
- params: fullParams$,
351
- gridAxesSize: gridAxesSize$,
352
- isSeriesPositionSeprate: isSeriesPositionSeprate$
353
- }).pipe(
354
- takeUntil(destroy$),
355
- switchMap(async d => d),
356
- map(data => {
357
- if (data.params.barWidth) {
358
- return data.params.barWidth
359
- } else if (data.isSeriesPositionSeprate) {
360
- return calcBarWidth({
361
- axisWidth: data.gridAxesSize.width,
362
- groupAmount: data.computedData[0] ? data.computedData[0].length : 0,
363
- barAmountOfGroup: 1,
364
- barPadding: data.params.barPadding,
365
- barGroupPadding: data.params.barGroupPadding
366
- })
367
- } else {
368
- return calcBarWidth({
369
- axisWidth: data.gridAxesSize.width,
370
- groupAmount: data.computedData[0] ? data.computedData[0].length : 0,
371
- barAmountOfGroup: data.visibleComputedData.length,
372
- barPadding: data.params.barPadding,
373
- barGroupPadding: data.params.barGroupPadding
374
- })
375
- }
376
- })
377
- )
378
-
379
- const seriesLabels$ = visibleComputedData$.pipe(
380
- takeUntil(destroy$),
381
- map(data => {
382
- const SeriesLabelSet: Set<string> = new Set()
383
- data.forEach(d => {
384
- d.forEach(_d => {
385
- SeriesLabelSet.add(_d.seriesLabel)
386
- })
387
- })
388
- return Array.from(SeriesLabelSet)
389
- })
390
- )
391
-
392
- const groupLabels$ = visibleComputedData$.pipe(
393
- takeUntil(destroy$),
394
- map(data => {
395
- const GroupLabelSet: Set<string> = new Set()
396
- data.forEach(d => {
397
- d.forEach(_d => {
398
- GroupLabelSet.add(_d.groupLabel)
399
- })
400
- })
401
- return Array.from(GroupLabelSet)
402
- })
403
- )
404
-
405
- const barScale$: Observable<d3.ScalePoint<string>> = new Observable(subscriber => {
406
- combineLatest({
407
- seriesLabels: seriesLabels$,
408
- barWidth: barWidth$,
409
- params: fullParams$,
410
- }).pipe(
411
- takeUntil(destroy$),
412
- switchMap(async d => d)
413
- ).subscribe(data => {
414
- const barScale = makeBarScale(data.barWidth, data.seriesLabels, data.params)
415
- subscriber.next(barScale)
416
- })
417
- })
418
-
419
- const transitionDuration$ = fullChartParams$.pipe(
420
- takeUntil(destroy$),
421
- map(d => d.transitionDuration),
422
- distinctUntilChanged()
423
- )
424
-
425
- const delayGroup$ = new Observable<number>(subscriber => {
426
- combineLatest({
427
- groupLabels: groupLabels$,
428
- transitionDuration: transitionDuration$,
429
- }).pipe(
430
- switchMap(async d => d)
431
- ).subscribe(data => {
432
- const delay = calcDelayGroup(data.groupLabels.length, data.transitionDuration)
433
- subscriber.next(delay)
434
- })
435
- }).pipe(
436
- takeUntil(destroy$),
437
- distinctUntilChanged()
438
- )
439
-
440
- const transitionItem$ = new Observable<number>(subscriber => {
441
- combineLatest({
442
- groupLabels: groupLabels$,
443
- transitionDuration: transitionDuration$
444
- }).pipe(
445
- switchMap(async d => d)
446
- ).subscribe(data => {
447
- const transition = calctransitionItem(data.groupLabels.length, data.transitionDuration)
448
- subscriber.next(transition)
449
- })
450
- }).pipe(
451
- takeUntil(destroy$),
452
- distinctUntilChanged()
453
- )
454
-
455
- //
456
-
457
- combineLatest({
458
- defsSelection: defsSelection$,
459
- gridAxesSize: gridAxesSize$,
460
- }).pipe(
461
- takeUntil(destroy$),
462
- switchMap(async d => d)
463
- ).subscribe(data => {
464
- const clipPathData = [{
465
- id: clipPathID,
466
- width: data.gridAxesSize.width,
467
- height: data.gridAxesSize.height
468
- }]
469
- renderClipPath({
470
- defsSelection: data.defsSelection,
471
- clipPathData
472
- })
473
- })
474
-
475
-
476
- const highlightTarget$ = fullChartParams$.pipe(
477
- takeUntil(destroy$),
478
- map(d => d.highlightTarget),
479
- distinctUntilChanged()
480
- )
481
-
482
- const barSelection$ = new Subject<d3.Selection<SVGPathElement, ComputedDatumGrid, SVGGElement, unknown>>()
483
-
484
- const linearGradientIds$ = seriesLabels$.pipe(
485
- takeUntil(destroy$),
486
- map(d => d.map((d, i) => {
487
- return getUniID(pluginName, `lineargradient-${d}`)
488
- }))
489
- )
490
-
491
- // const barData$ = combineLatest({
492
- // linearGradientIds: linearGradientIds$,
493
- // computedData: computedData$
494
- // }).pipe(
495
- // takeUntil(destroy$),
496
- // switchMap(async d => d),
497
- // map(data => {
498
- // return data.computedData.map((series, seriesIndex) => {
499
- // return series.map((_d, _i) => {
500
- // return <BarDatumGrid>{
501
- // linearGradientId: data.linearGradientIds[seriesIndex],
502
- // ..._d
503
- // }
504
- // })
505
- // })
506
- // })
507
- // )
508
-
509
- combineLatest({
510
- graphicGSelection: graphicGSelection$,
511
- defsSelection: defsSelection$,
512
- computedData: computedData$,
513
- linearGradientIds: linearGradientIds$,
514
- zeroYArr: zeroYArr$,
515
- groupLabels: groupLabels$,
516
- barScale: barScale$,
517
- params: fullParams$,
518
- chartParams: fullChartParams$,
519
- highlightTarget: highlightTarget$,
520
- barWidth: barWidth$,
521
- delayGroup: delayGroup$,
522
- transitionItem: transitionItem$,
523
- SeriesDataMap: SeriesDataMap$,
524
- GroupDataMap: GroupDataMap$,
525
- isSeriesPositionSeprate: isSeriesPositionSeprate$
526
- }).pipe(
527
- takeUntil(destroy$),
528
- switchMap(async (d) => d),
529
- ).subscribe(data => {
530
-
531
- const barSelection = renderTriangleBars({
532
- graphicGSelection: data.graphicGSelection,
533
- pathGClassName,
534
- pathClassName,
535
- computedData: data.computedData,
536
- linearGradientIds: data.linearGradientIds,
537
- zeroYArr: data.zeroYArr,
538
- groupLabels: data.groupLabels,
539
- barScale: data.barScale,
540
- params: data.params,
541
- chartParams: data.chartParams,
542
- barWidth: data.barWidth,
543
- delayGroup: data.delayGroup,
544
- transitionItem: data.transitionItem,
545
- isSeriesPositionSeprate: data.isSeriesPositionSeprate
546
- })
547
- renderLinearGradient({
548
- defsSelection: data.defsSelection,
549
- computedData: data.computedData,
550
- linearGradientIds: data.linearGradientIds,
551
- params: data.params
552
- })
553
-
554
- barSelection!
555
- .on('mouseover', (event, datum) => {
556
- event.stopPropagation()
557
-
558
- event$.next({
559
- type: 'grid',
560
- eventName: 'mouseover',
561
- pluginName,
562
- highlightTarget: data.highlightTarget,
563
- 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
- .on('mousemove', (event, datum) => {
576
- event.stopPropagation()
577
-
578
- event$.next({
579
- type: 'grid',
580
- eventName: 'mousemove',
581
- pluginName,
582
- highlightTarget: data.highlightTarget,
583
- datum,
584
- gridIndex: datum.gridIndex,
585
- series: data.SeriesDataMap.get(datum.seriesLabel)!,
586
- seriesIndex: datum.seriesIndex,
587
- seriesLabel: datum.seriesLabel,
588
- groups: data.GroupDataMap.get(datum.groupLabel)!,
589
- groupIndex: datum.groupIndex,
590
- groupLabel: datum.groupLabel,
591
- event,
592
- data: data.computedData
593
- })
594
- })
595
- .on('mouseout', (event, datum) => {
596
- event.stopPropagation()
597
-
598
- event$.next({
599
- type: 'grid',
600
- eventName: 'mouseout',
601
- pluginName,
602
- highlightTarget: data.highlightTarget,
603
- datum,
604
- gridIndex: datum.gridIndex,
605
- series: data.SeriesDataMap.get(datum.seriesLabel)!,
606
- seriesIndex: datum.seriesIndex,
607
- seriesLabel: datum.seriesLabel,
608
- groups: data.GroupDataMap.get(datum.groupLabel)!,
609
- groupIndex: datum.groupIndex,
610
- groupLabel: datum.groupLabel,
611
- event,
612
- data: data.computedData
613
- })
614
- })
615
- .on('click', (event, datum) => {
616
- event.stopPropagation()
617
-
618
- event$.next({
619
- type: 'grid',
620
- eventName: 'click',
621
- pluginName,
622
- highlightTarget: data.highlightTarget,
623
- datum,
624
- gridIndex: datum.gridIndex,
625
- series: data.SeriesDataMap.get(datum.seriesLabel)!,
626
- seriesIndex: datum.seriesIndex,
627
- seriesLabel: datum.seriesLabel,
628
- groups: data.GroupDataMap.get(datum.groupLabel)!,
629
- groupIndex: datum.groupIndex,
630
- groupLabel: datum.groupLabel,
631
- event,
632
- data: data.computedData
633
- })
634
- })
635
-
636
- barSelection$.next(barSelection!)
637
- })
638
-
639
-
640
- combineLatest({
641
- barSelection: barSelection$,
642
- highlight: gridHighlight$.pipe(
643
- map(data => data.map(d => d.id))
644
- ),
645
- fullChartParams: fullChartParams$
646
- }).pipe(
647
- takeUntil(destroy$),
648
- switchMap(async d => d)
649
- ).subscribe(data => {
650
- highlight({
651
- selection: data.barSelection,
652
- ids: data.highlight,
653
- fullChartParams: data.fullChartParams
654
- })
655
- })
656
-
657
- return () => {
658
- destroy$.next(undefined)
659
- }
1
+ import * as d3 from 'd3'
2
+ import {
3
+ combineLatest,
4
+ map,
5
+ switchMap,
6
+ takeUntil,
7
+ distinctUntilChanged,
8
+ shareReplay,
9
+ Observable,
10
+ Subject } from 'rxjs'
11
+ import type { BasePluginFn } from './types'
12
+ import type {
13
+ ComputedDatumGrid,
14
+ ComputedDataGrid,
15
+ ComputedLayoutDatumGrid,
16
+ ComputedLayoutDataGrid,
17
+ DataFormatterTypeMap,
18
+ EventGrid,
19
+ ChartParams,
20
+ GridContainerPosition,
21
+ Layout,
22
+ TransformData } from '@orbcharts/core'
23
+ import { getD3TransitionEase } from '../utils/d3Utils'
24
+ import { getClassName, getUniID } from '../utils/orbchartsUtils'
25
+ import { gridSelectionsObservable } from '../grid/gridObservables'
26
+
27
+ export interface BaseBarsTriangleParams {
28
+ barWidth: number
29
+ barPadding: number
30
+ barGroupPadding: number // 群組和群組間的間隔
31
+ linearGradientOpacity: [number, number]
32
+ }
33
+
34
+ interface BaseBarsContext {
35
+ selection: d3.Selection<any, unknown, any, unknown>
36
+ computedData$: Observable<ComputedDataGrid>
37
+ computedLayoutData$: Observable<ComputedLayoutDataGrid>
38
+ visibleComputedData$: Observable<ComputedDatumGrid[][]>
39
+ visibleComputedLayoutData$: Observable<ComputedLayoutDataGrid>
40
+ fullDataFormatter$: Observable<DataFormatterTypeMap<'grid'>>
41
+ seriesLabels$: Observable<string[]>
42
+ SeriesDataMap$: Observable<Map<string, ComputedDatumGrid[]>>
43
+ GroupDataMap$: Observable<Map<string, ComputedDatumGrid[]>>
44
+ fullParams$: Observable<BaseBarsTriangleParams>
45
+ fullChartParams$: Observable<ChartParams>
46
+ gridAxesTransform$: Observable<TransformData>
47
+ gridGraphicTransform$: Observable<TransformData>
48
+ gridAxesSize$: Observable<{
49
+ width: number;
50
+ height: number;
51
+ }>
52
+ gridHighlight$: Observable<ComputedDatumGrid[]>
53
+ gridContainerPosition$: Observable<GridContainerPosition[]>
54
+ isSeriesSeprate$: Observable<boolean>
55
+ event$: Subject<EventGrid>
56
+ }
57
+
58
+
59
+ interface RenderBarParams {
60
+ graphicGSelection: d3.Selection<SVGGElement, unknown, any, any>
61
+ pathGClassName: string
62
+ pathClassName: string
63
+ visibleComputedLayoutData: ComputedLayoutDataGrid
64
+ linearGradientIds: string[]
65
+ zeroYArr: number[]
66
+ groupLabels: string[]
67
+ barScale: d3.ScalePoint<string>
68
+ params: BaseBarsTriangleParams
69
+ chartParams: ChartParams
70
+ barWidth: number
71
+ delayGroup: number
72
+ transitionItem: number
73
+ isSeriesSeprate: boolean
74
+ }
75
+
76
+ // interface BarDatumGrid extends ComputedDatumGrid {
77
+ // linearGradientId: string
78
+ // }
79
+
80
+ type ClipPathDatum = {
81
+ id: string;
82
+ // x: number;
83
+ // y: number;
84
+ width: number;
85
+ height: number;
86
+ }
87
+
88
+ // const pluginName = 'BaseBarsTriangle'
89
+ // const pathGClassName = getClassName(pluginName, 'pathG')
90
+ // const pathClassName = getClassName(pluginName, 'path')
91
+ // group的delay在動畫中的佔比(剩餘部份的時間為圖形本身的動畫時間,因為delay時間和最後一個group的動畫時間加總為1)
92
+ const groupDelayProportionOfDuration = 0.3
93
+
94
+ function calcBarWidth ({ axisWidth, groupAmount, barAmountOfGroup, barPadding = 0, barGroupPadding = 0 }: {
95
+ axisWidth: number
96
+ groupAmount: number
97
+ barAmountOfGroup: number
98
+ barPadding: number
99
+ barGroupPadding: number
100
+ }) {
101
+ const eachGroupWidth = axisWidth / (groupAmount - 1)
102
+ const width = (eachGroupWidth - barGroupPadding) / barAmountOfGroup - barPadding
103
+ return width > 1 ? width : 1
104
+ }
105
+
106
+ function makeBarScale (barWidth: number, seriesLabels: string[], params: BaseBarsTriangleParams) {
107
+ const barHalfWidth = barWidth! / 2
108
+ const barGroupWidth = barWidth * seriesLabels.length + params.barPadding! * seriesLabels.length
109
+ return d3.scalePoint()
110
+ .domain(seriesLabels)
111
+ .range([-barGroupWidth / 2 + barHalfWidth, barGroupWidth / 2 - barHalfWidth])
112
+ }
113
+
114
+ function calcDelayGroup (barGroupAmount: number, totalDuration: number) {
115
+ if (barGroupAmount <= 1) {
116
+ // 一筆內計算會出錯所以不算
117
+ return 0
118
+ }
119
+ return totalDuration / (barGroupAmount - 1) * groupDelayProportionOfDuration // 依group數量計算
120
+ }
121
+
122
+ function calctransitionItem (barGroupAmount: number, totalDuration: number) {
123
+ if (barGroupAmount <= 1) {
124
+ // 一筆內不會有delay
125
+ return totalDuration
126
+ }
127
+ return totalDuration * (1 - groupDelayProportionOfDuration) // delay後剩餘的時間
128
+ }
129
+
130
+ function renderTriangleBars ({ graphicGSelection, pathGClassName, pathClassName, visibleComputedLayoutData, linearGradientIds, zeroYArr, groupLabels, barScale, params, chartParams, barWidth, delayGroup, transitionItem, isSeriesSeprate }: RenderBarParams) {
131
+
132
+ const barHalfWidth = barWidth! / 2
133
+
134
+ graphicGSelection
135
+ .each((d, seriesIndex, g) => {
136
+ // g
137
+ const gSelection = d3.select(g[seriesIndex])
138
+ .selectAll<SVGGElement, ComputedDatumGrid>(`g.${pathGClassName}`)
139
+ .data(visibleComputedLayoutData[seriesIndex] ?? [])
140
+ .join(
141
+ enter => {
142
+ const enterSelection = enter
143
+ .append('g')
144
+ .classed(pathGClassName, true)
145
+ .attr('cursor', 'pointer')
146
+ enterSelection
147
+ .append('path')
148
+ .classed(pathClassName, true)
149
+ .style('vector-effect', 'non-scaling-stroke')
150
+ .attr('d', (d) => {
151
+ const x = -barHalfWidth
152
+ const y1 = zeroYArr[seriesIndex]
153
+ const y2 = zeroYArr[seriesIndex]
154
+ return `M${x},${y1} L${x + (barWidth! / 2)},${y2} ${x + barWidth!},${y1}`
155
+ })
156
+ return enterSelection
157
+ },
158
+ update => update,
159
+ exit => exit.remove()
160
+ )
161
+ .attr('transform', d => `translate(${isSeriesSeprate ? 0 : barScale(d.seriesLabel)!}, 0)`)
162
+
163
+ // path
164
+ gSelection.select(`path.${pathClassName}`)
165
+ .attr('height', d => Math.abs(d.axisYFromZero))
166
+ .attr('y', d => d.axisY < zeroYArr[seriesIndex] ? d.axisY : zeroYArr[seriesIndex])
167
+ .attr('x', d => isSeriesSeprate ? 0 : barScale(d.seriesLabel)!)
168
+ // .style('fill', d => `url(#${d.linearGradientId})`)
169
+ .style('fill', d => `url(#${linearGradientIds[d.seriesIndex]})`)
170
+ .attr('stroke', d => d.color)
171
+ .attr('transform', d => `translate(${(d ? d.axisX : 0)}, ${0})`)
172
+ .transition()
173
+ .duration(transitionItem)
174
+ .ease(getD3TransitionEase(chartParams.transitionEase))
175
+ .delay((d, i) => d.groupIndex * delayGroup)
176
+ // .attr('transform', `translate(${-barHalfWidth}, 0)`)
177
+ // .attr('x', d => itemScale(d.itemLabel)!)
178
+ // .attr('y', d => -d.y)
179
+ .attr('d', (d) => {
180
+ const x = -barHalfWidth
181
+ const y1 = zeroYArr[seriesIndex]
182
+ const y2 = d.axisY
183
+ return `M${x},${y1} L${x + (barWidth! / 2)},${y2} ${x + barWidth!},${y1}`
184
+ })
185
+ })
186
+
187
+ const graphicBarSelection: d3.Selection<SVGPathElement, ComputedDatumGrid, any, any> = graphicGSelection.selectAll(`path.${pathClassName}`)
188
+
189
+ return graphicBarSelection
190
+ }
191
+
192
+ function renderLinearGradient ({ defsSelection, computedData, linearGradientIds, params }: {
193
+ defsSelection: d3.Selection<SVGDefsElement, any, any, any>
194
+ computedData: ComputedDataGrid
195
+ linearGradientIds: string[]
196
+ params: BaseBarsTriangleParams
197
+ }) {
198
+ defsSelection!
199
+ .selectAll<SVGLinearGradientElement, ComputedDatumGrid>('linearGradient')
200
+ .data(computedData ?? [])
201
+ .join(
202
+ enter => {
203
+ return enter
204
+ .append('linearGradient')
205
+ .attr('x1', '0%')
206
+ .attr('x2', '0%')
207
+ .attr('y1', '100%')
208
+ .attr('y2', '0%')
209
+ .attr('spreadMethod', 'pad')
210
+ },
211
+ update => update,
212
+ exit => exit.remove()
213
+ )
214
+ .attr('id', (d, i) => {
215
+ return d[0] ? linearGradientIds[d[0].seriesIndex] : ''
216
+ })
217
+ .html((d, i) => {
218
+ const color = d[0] ? d[0].color : ''
219
+ return `
220
+ <stop offset="0%" stop-color="${color}" stop-opacity="${params.linearGradientOpacity[0]}"/>
221
+ <stop offset="100%" stop-color="${color}" stop-opacity="${params.linearGradientOpacity[1]}"/>
222
+ `
223
+ })
224
+
225
+ }
226
+
227
+
228
+ function renderClipPath ({ defsSelection, clipPathData }: {
229
+ defsSelection: d3.Selection<SVGDefsElement, any, any, any>
230
+ clipPathData: ClipPathDatum[]
231
+ }) {
232
+ const clipPath = defsSelection
233
+ .selectAll<SVGClipPathElement, Layout>('clipPath')
234
+ .data(clipPathData)
235
+ .join(
236
+ enter => {
237
+ return enter
238
+ .append('clipPath')
239
+ },
240
+ update => update,
241
+ exit => exit.remove()
242
+ )
243
+ .attr('id', d => d.id)
244
+ .each((d, i, g) => {
245
+ const rect = d3.select(g[i])
246
+ .selectAll<SVGRectElement, typeof d>('rect')
247
+ .data([d])
248
+ .join(
249
+ enter => {
250
+ return enter
251
+ .append('rect')
252
+ },
253
+ update => update,
254
+ exit => exit.remove()
255
+ )
256
+ .attr('x', 0)
257
+ .attr('y', 0)
258
+ .attr('width', _d => _d.width)
259
+ .attr('height', _d => _d.height)
260
+ })
261
+ }
262
+
263
+ function highlight ({ selection, ids, fullChartParams }: {
264
+ selection: d3.Selection<SVGPathElement, ComputedDatumGrid, any, any>
265
+ ids: string[]
266
+ fullChartParams: ChartParams
267
+ }) {
268
+ selection.interrupt('highlight')
269
+
270
+ const removeHighlight = () => {
271
+ selection
272
+ .transition('highlight')
273
+ .duration(200)
274
+ .style('opacity', 1)
275
+ }
276
+
277
+ if (!ids.length) {
278
+ removeHighlight()
279
+ return
280
+ }
281
+
282
+ selection
283
+ .each((d, i, n) => {
284
+ if (ids.includes(d.id)) {
285
+ d3.select(n[i])
286
+ .style('opacity', 1)
287
+ } else {
288
+ d3.select(n[i])
289
+ .style('opacity', fullChartParams.styles.unhighlightedOpacity)
290
+ }
291
+ })
292
+ }
293
+
294
+
295
+ export const createBaseBarsTriangle: BasePluginFn<BaseBarsContext> = (pluginName: string, {
296
+ selection,
297
+ computedData$,
298
+ computedLayoutData$,
299
+ visibleComputedData$,
300
+ visibleComputedLayoutData$,
301
+ fullDataFormatter$,
302
+ seriesLabels$,
303
+ SeriesDataMap$,
304
+ GroupDataMap$,
305
+ fullParams$,
306
+ fullChartParams$,
307
+ gridAxesTransform$,
308
+ gridGraphicTransform$,
309
+ gridAxesSize$,
310
+ gridHighlight$,
311
+ gridContainerPosition$,
312
+ isSeriesSeprate$,
313
+ event$
314
+ }) => {
315
+ const destroy$ = new Subject()
316
+
317
+ const clipPathID = getUniID(pluginName, 'clipPath-box')
318
+ const pathGClassName = getClassName(pluginName, 'pathG')
319
+ const pathClassName = getClassName(pluginName, 'path')
320
+
321
+ const {
322
+ seriesSelection$,
323
+ axesSelection$,
324
+ defsSelection$,
325
+ graphicGSelection$
326
+ } = gridSelectionsObservable({
327
+ selection,
328
+ pluginName,
329
+ clipPathID,
330
+ seriesLabels$,
331
+ gridContainerPosition$,
332
+ gridAxesTransform$,
333
+ gridGraphicTransform$
334
+ })
335
+
336
+ // valueAxis 的起始座標
337
+ const valueAxisStart$: Observable<number> = gridGraphicTransform$.pipe(
338
+ takeUntil(destroy$),
339
+ map(data => {
340
+ // 抵消掉外層的變型
341
+ return - data.translate[1] / data.scale[1]
342
+ })
343
+ )
344
+
345
+ const zeroYArr$ = visibleComputedLayoutData$.pipe(
346
+ // map(d => d[0] && d[0][0]
347
+ // ? d[0][0].axisY - d[0][0].axisYFromZero
348
+ // : 0),
349
+ map(data => {
350
+ return data.map(d => {
351
+ return d[0] ? d[0].axisY - d[0].axisYFromZero : 0
352
+ })
353
+ }),
354
+ distinctUntilChanged()
355
+ )
356
+
357
+ const barWidth$ = combineLatest({
358
+ computedData: computedData$,
359
+ visibleComputedData: visibleComputedData$,
360
+ params: fullParams$,
361
+ gridAxesSize: gridAxesSize$,
362
+ isSeriesSeprate: isSeriesSeprate$
363
+ }).pipe(
364
+ takeUntil(destroy$),
365
+ switchMap(async d => d),
366
+ map(data => {
367
+ if (data.params.barWidth) {
368
+ return data.params.barWidth
369
+ } else if (data.isSeriesSeprate) {
370
+ return calcBarWidth({
371
+ axisWidth: data.gridAxesSize.width,
372
+ groupAmount: data.computedData[0] ? data.computedData[0].length : 0,
373
+ barAmountOfGroup: 1,
374
+ barPadding: data.params.barPadding,
375
+ barGroupPadding: data.params.barGroupPadding
376
+ })
377
+ } else {
378
+ return calcBarWidth({
379
+ axisWidth: data.gridAxesSize.width,
380
+ groupAmount: data.computedData[0] ? data.computedData[0].length : 0,
381
+ barAmountOfGroup: data.visibleComputedData.length,
382
+ barPadding: data.params.barPadding,
383
+ barGroupPadding: data.params.barGroupPadding
384
+ })
385
+ }
386
+ })
387
+ )
388
+
389
+ // const seriesLabels$ = visibleComputedData$.pipe(
390
+ // takeUntil(destroy$),
391
+ // map(data => {
392
+ // const SeriesLabelSet: Set<string> = new Set()
393
+ // data.forEach(d => {
394
+ // d.forEach(_d => {
395
+ // SeriesLabelSet.add(_d.seriesLabel)
396
+ // })
397
+ // })
398
+ // return Array.from(SeriesLabelSet)
399
+ // })
400
+ // )
401
+
402
+ const groupLabels$ = visibleComputedData$.pipe(
403
+ takeUntil(destroy$),
404
+ map(data => {
405
+ const GroupLabelSet: Set<string> = new Set()
406
+ data.forEach(d => {
407
+ d.forEach(_d => {
408
+ GroupLabelSet.add(_d.groupLabel)
409
+ })
410
+ })
411
+ return Array.from(GroupLabelSet)
412
+ })
413
+ )
414
+
415
+ const barScale$: Observable<d3.ScalePoint<string>> = new Observable(subscriber => {
416
+ combineLatest({
417
+ seriesLabels: seriesLabels$,
418
+ barWidth: barWidth$,
419
+ params: fullParams$,
420
+ }).pipe(
421
+ takeUntil(destroy$),
422
+ switchMap(async d => d)
423
+ ).subscribe(data => {
424
+ const barScale = makeBarScale(data.barWidth, data.seriesLabels, data.params)
425
+ subscriber.next(barScale)
426
+ })
427
+ })
428
+
429
+ const transitionDuration$ = fullChartParams$.pipe(
430
+ takeUntil(destroy$),
431
+ map(d => d.transitionDuration),
432
+ distinctUntilChanged()
433
+ )
434
+
435
+ const delayGroup$ = new Observable<number>(subscriber => {
436
+ combineLatest({
437
+ groupLabels: groupLabels$,
438
+ transitionDuration: transitionDuration$,
439
+ }).pipe(
440
+ switchMap(async d => d)
441
+ ).subscribe(data => {
442
+ const delay = calcDelayGroup(data.groupLabels.length, data.transitionDuration)
443
+ subscriber.next(delay)
444
+ })
445
+ }).pipe(
446
+ takeUntil(destroy$),
447
+ distinctUntilChanged()
448
+ )
449
+
450
+ const transitionItem$ = new Observable<number>(subscriber => {
451
+ combineLatest({
452
+ groupLabels: groupLabels$,
453
+ transitionDuration: transitionDuration$
454
+ }).pipe(
455
+ switchMap(async d => d)
456
+ ).subscribe(data => {
457
+ const transition = calctransitionItem(data.groupLabels.length, data.transitionDuration)
458
+ subscriber.next(transition)
459
+ })
460
+ }).pipe(
461
+ takeUntil(destroy$),
462
+ distinctUntilChanged()
463
+ )
464
+
465
+ //
466
+
467
+ combineLatest({
468
+ defsSelection: defsSelection$,
469
+ gridAxesSize: gridAxesSize$,
470
+ }).pipe(
471
+ takeUntil(destroy$),
472
+ switchMap(async d => d)
473
+ ).subscribe(data => {
474
+ const clipPathData = [{
475
+ id: clipPathID,
476
+ width: data.gridAxesSize.width,
477
+ height: data.gridAxesSize.height
478
+ }]
479
+ renderClipPath({
480
+ defsSelection: data.defsSelection,
481
+ clipPathData
482
+ })
483
+ })
484
+
485
+
486
+ const highlightTarget$ = fullChartParams$.pipe(
487
+ takeUntil(destroy$),
488
+ map(d => d.highlightTarget),
489
+ distinctUntilChanged()
490
+ )
491
+
492
+ const linearGradientIds$ = seriesLabels$.pipe(
493
+ takeUntil(destroy$),
494
+ map(d => d.map((d, i) => {
495
+ return getUniID(pluginName, `lineargradient-${d}`)
496
+ }))
497
+ )
498
+
499
+ // const barData$ = combineLatest({
500
+ // linearGradientIds: linearGradientIds$,
501
+ // computedData: computedData$
502
+ // }).pipe(
503
+ // takeUntil(destroy$),
504
+ // switchMap(async d => d),
505
+ // map(data => {
506
+ // return data.computedData.map((series, seriesIndex) => {
507
+ // return series.map((_d, _i) => {
508
+ // return <BarDatumGrid>{
509
+ // linearGradientId: data.linearGradientIds[seriesIndex],
510
+ // ..._d
511
+ // }
512
+ // })
513
+ // })
514
+ // })
515
+ // )
516
+
517
+ const barSelection$ = combineLatest({
518
+ graphicGSelection: graphicGSelection$,
519
+ defsSelection: defsSelection$,
520
+ computedData: computedData$,
521
+ visibleComputedLayoutData: visibleComputedLayoutData$,
522
+ linearGradientIds: linearGradientIds$,
523
+ zeroYArr: zeroYArr$,
524
+ groupLabels: groupLabels$,
525
+ barScale: barScale$,
526
+ params: fullParams$,
527
+ chartParams: fullChartParams$,
528
+ barWidth: barWidth$,
529
+ delayGroup: delayGroup$,
530
+ transitionItem: transitionItem$,
531
+ isSeriesSeprate: isSeriesSeprate$
532
+ }).pipe(
533
+ takeUntil(destroy$),
534
+ switchMap(async (d) => d),
535
+ map(data => {
536
+ renderLinearGradient({
537
+ defsSelection: data.defsSelection,
538
+ computedData: data.computedData,
539
+ linearGradientIds: data.linearGradientIds,
540
+ params: data.params
541
+ })
542
+
543
+ return renderTriangleBars({
544
+ graphicGSelection: data.graphicGSelection,
545
+ pathGClassName,
546
+ pathClassName,
547
+ visibleComputedLayoutData: data.visibleComputedLayoutData,
548
+ linearGradientIds: data.linearGradientIds,
549
+ zeroYArr: data.zeroYArr,
550
+ groupLabels: data.groupLabels,
551
+ barScale: data.barScale,
552
+ params: data.params,
553
+ chartParams: data.chartParams,
554
+ barWidth: data.barWidth,
555
+ delayGroup: data.delayGroup,
556
+ transitionItem: data.transitionItem,
557
+ isSeriesSeprate: data.isSeriesSeprate
558
+ })
559
+ })
560
+ )
561
+
562
+ combineLatest({
563
+ barSelection: barSelection$,
564
+ computedData: computedData$,
565
+ highlightTarget: highlightTarget$,
566
+ SeriesDataMap: SeriesDataMap$,
567
+ GroupDataMap: GroupDataMap$,
568
+ }).subscribe(data => {
569
+
570
+ data.barSelection!
571
+ .on('mouseover', (event, datum) => {
572
+ event.stopPropagation()
573
+
574
+ event$.next({
575
+ type: 'grid',
576
+ eventName: 'mouseover',
577
+ pluginName,
578
+ highlightTarget: data.highlightTarget,
579
+ 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
+ .on('mousemove', (event, datum) => {
592
+ event.stopPropagation()
593
+
594
+ event$.next({
595
+ type: 'grid',
596
+ eventName: 'mousemove',
597
+ pluginName,
598
+ highlightTarget: data.highlightTarget,
599
+ datum,
600
+ gridIndex: datum.gridIndex,
601
+ series: data.SeriesDataMap.get(datum.seriesLabel)!,
602
+ seriesIndex: datum.seriesIndex,
603
+ seriesLabel: datum.seriesLabel,
604
+ groups: data.GroupDataMap.get(datum.groupLabel)!,
605
+ groupIndex: datum.groupIndex,
606
+ groupLabel: datum.groupLabel,
607
+ event,
608
+ data: data.computedData
609
+ })
610
+ })
611
+ .on('mouseout', (event, datum) => {
612
+ event.stopPropagation()
613
+
614
+ event$.next({
615
+ type: 'grid',
616
+ eventName: 'mouseout',
617
+ pluginName,
618
+ highlightTarget: data.highlightTarget,
619
+ datum,
620
+ gridIndex: datum.gridIndex,
621
+ series: data.SeriesDataMap.get(datum.seriesLabel)!,
622
+ seriesIndex: datum.seriesIndex,
623
+ seriesLabel: datum.seriesLabel,
624
+ groups: data.GroupDataMap.get(datum.groupLabel)!,
625
+ groupIndex: datum.groupIndex,
626
+ groupLabel: datum.groupLabel,
627
+ event,
628
+ data: data.computedData
629
+ })
630
+ })
631
+ .on('click', (event, datum) => {
632
+ event.stopPropagation()
633
+
634
+ event$.next({
635
+ type: 'grid',
636
+ eventName: 'click',
637
+ pluginName,
638
+ highlightTarget: data.highlightTarget,
639
+ datum,
640
+ gridIndex: datum.gridIndex,
641
+ series: data.SeriesDataMap.get(datum.seriesLabel)!,
642
+ seriesIndex: datum.seriesIndex,
643
+ seriesLabel: datum.seriesLabel,
644
+ groups: data.GroupDataMap.get(datum.groupLabel)!,
645
+ groupIndex: datum.groupIndex,
646
+ groupLabel: datum.groupLabel,
647
+ event,
648
+ data: data.computedData
649
+ })
650
+ })
651
+ })
652
+
653
+ combineLatest({
654
+ barSelection: barSelection$,
655
+ highlight: gridHighlight$.pipe(
656
+ map(data => data.map(d => d.id))
657
+ ),
658
+ fullChartParams: fullChartParams$
659
+ }).pipe(
660
+ takeUntil(destroy$),
661
+ switchMap(async d => d)
662
+ ).subscribe(data => {
663
+ highlight({
664
+ selection: data.barSelection,
665
+ ids: data.highlight,
666
+ fullChartParams: data.fullChartParams
667
+ })
668
+ })
669
+
670
+ return () => {
671
+ destroy$.next(undefined)
672
+ }
660
673
  }