@orbcharts/plugins-basic 3.0.2 → 3.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/LICENSE +200 -200
  2. package/dist/lib/gridObservables.d.ts +1 -0
  3. package/dist/orbcharts-plugins-basic.es.js +14803 -13543
  4. package/dist/orbcharts-plugins-basic.umd.js +129 -99
  5. package/dist/src/base/BaseOrdinalBubbles.d.ts +26 -0
  6. package/dist/src/base/BaseRacingBars.d.ts +2 -1
  7. package/dist/src/base/BaseRacingLabels.d.ts +1 -0
  8. package/dist/src/base/BaseXZoom.d.ts +18 -0
  9. package/dist/src/grid/gridObservables.d.ts +4 -7
  10. package/dist/src/multiValue/defaults.d.ts +5 -1
  11. package/dist/src/multiValue/index.d.ts +4 -0
  12. package/dist/src/multiValue/multiValueObservables.d.ts +14 -1
  13. package/dist/src/multiValue/plugins/OrdinalAux.d.ts +3 -0
  14. package/dist/src/multiValue/plugins/OrdinalAxis.d.ts +3 -0
  15. package/dist/src/multiValue/plugins/OrdinalBubbles.d.ts +3 -0
  16. package/dist/src/multiValue/plugins/OrdinalZoom.d.ts +1 -0
  17. package/lib/core-types.ts +7 -7
  18. package/lib/core.ts +6 -6
  19. package/lib/gridObservables.ts +6 -0
  20. package/lib/plugins-basic-types.ts +6 -6
  21. package/package.json +48 -48
  22. package/src/base/BaseBars.ts +765 -765
  23. package/src/base/BaseBarsTriangle.ts +676 -676
  24. package/src/base/BaseDots.ts +464 -464
  25. package/src/base/BaseGroupAxis.ts +691 -691
  26. package/src/base/BaseLegend.ts +684 -684
  27. package/src/base/BaseLineAreas.ts +629 -629
  28. package/src/base/BaseLines.ts +706 -706
  29. package/src/base/BaseOrdinalBubbles.ts +730 -0
  30. package/src/base/BaseRacingBars.ts +582 -551
  31. package/src/base/BaseRacingLabels.ts +404 -396
  32. package/src/base/BaseRacingValueLabels.ts +403 -403
  33. package/src/base/BaseStackedBars.ts +782 -782
  34. package/src/base/BaseTooltip.ts +386 -386
  35. package/src/base/BaseValueAxis.ts +600 -600
  36. package/src/base/BaseXAxis.ts +427 -427
  37. package/src/base/BaseXZoom.ts +242 -0
  38. package/src/base/BaseYAxis.ts +389 -389
  39. package/src/base/types.ts +2 -2
  40. package/src/const.ts +30 -30
  41. package/src/grid/defaults.ts +213 -213
  42. package/src/grid/gridObservables.ts +635 -612
  43. package/src/grid/index.ts +16 -16
  44. package/src/grid/plugins/Bars.ts +69 -69
  45. package/src/grid/plugins/BarsPN.ts +66 -66
  46. package/src/grid/plugins/BarsTriangle.ts +73 -73
  47. package/src/grid/plugins/Dots.ts +68 -68
  48. package/src/grid/plugins/GridLegend.ts +107 -107
  49. package/src/grid/plugins/GridTooltip.ts +66 -66
  50. package/src/grid/plugins/GroupAux.ts +1095 -1120
  51. package/src/grid/plugins/GroupAxis.ts +73 -73
  52. package/src/grid/plugins/GroupZoom.ts +218 -218
  53. package/src/grid/plugins/LineAreas.ts +65 -65
  54. package/src/grid/plugins/Lines.ts +59 -59
  55. package/src/grid/plugins/StackedBars.ts +64 -64
  56. package/src/grid/plugins/StackedValueAxis.ts +96 -96
  57. package/src/grid/plugins/ValueAxis.ts +94 -94
  58. package/src/index.ts +6 -6
  59. package/src/multiGrid/defaults.ts +244 -244
  60. package/src/multiGrid/index.ts +14 -14
  61. package/src/multiGrid/multiGridObservables.ts +50 -50
  62. package/src/multiGrid/plugins/MultiBars.ts +108 -108
  63. package/src/multiGrid/plugins/MultiBarsTriangle.ts +114 -114
  64. package/src/multiGrid/plugins/MultiDots.ts +102 -102
  65. package/src/multiGrid/plugins/MultiGridLegend.ts +169 -169
  66. package/src/multiGrid/plugins/MultiGridTooltip.ts +66 -66
  67. package/src/multiGrid/plugins/MultiGroupAxis.ts +137 -137
  68. package/src/multiGrid/plugins/MultiLineAreas.ts +107 -107
  69. package/src/multiGrid/plugins/MultiLines.ts +101 -101
  70. package/src/multiGrid/plugins/MultiStackedBars.ts +106 -106
  71. package/src/multiGrid/plugins/MultiStackedValueAxis.ts +134 -134
  72. package/src/multiGrid/plugins/MultiValueAxis.ts +134 -134
  73. package/src/multiGrid/plugins/OverlappingStackedValueAxes.ts +300 -300
  74. package/src/multiGrid/plugins/OverlappingValueAxes.ts +300 -300
  75. package/src/multiValue/defaults.ts +523 -431
  76. package/src/multiValue/index.ts +16 -12
  77. package/src/multiValue/multiValueObservables.ts +781 -666
  78. package/src/multiValue/plugins/MultiValueLegend.ts +107 -107
  79. package/src/multiValue/plugins/MultiValueTooltip.ts +66 -66
  80. package/src/multiValue/plugins/OrdinalAux.ts +661 -0
  81. package/src/multiValue/plugins/OrdinalAxis.ts +525 -0
  82. package/src/multiValue/plugins/OrdinalBubbles.ts +226 -0
  83. package/src/multiValue/plugins/OrdinalZoom.ts +57 -0
  84. package/src/multiValue/plugins/RacingBars.ts +375 -373
  85. package/src/multiValue/plugins/RacingCounterTexts.ts +300 -300
  86. package/src/multiValue/plugins/RacingValueAxis.ts +114 -114
  87. package/src/multiValue/plugins/Scatter.ts +486 -426
  88. package/src/multiValue/plugins/ScatterBubbles.ts +635 -554
  89. package/src/multiValue/plugins/XAxis.ts +107 -107
  90. package/src/multiValue/plugins/XYAux.ts +683 -682
  91. package/src/multiValue/plugins/XYAxes.ts +194 -194
  92. package/src/multiValue/plugins/XYAxes_legacy.ts +683 -683
  93. package/src/multiValue/plugins/XZoom.ts +40 -299
  94. package/src/noneData/defaults.ts +102 -102
  95. package/src/noneData/index.ts +3 -3
  96. package/src/noneData/plugins/Container.ts +27 -27
  97. package/src/noneData/plugins/Tooltip.ts +373 -373
  98. package/src/relationship/defaults.ts +221 -221
  99. package/src/relationship/index.ts +5 -5
  100. package/src/relationship/plugins/ForceDirected.ts +1173 -1173
  101. package/src/relationship/plugins/ForceDirectedBubbles.ts +1411 -1411
  102. package/src/relationship/plugins/RelationshipLegend.ts +100 -100
  103. package/src/relationship/plugins/RelationshipTooltip.ts +66 -66
  104. package/src/relationship/relationshipObservables.ts +49 -49
  105. package/src/series/defaults.ts +221 -221
  106. package/src/series/index.ts +9 -9
  107. package/src/series/plugins/Bubbles.ts +636 -636
  108. package/src/series/plugins/Pie.ts +623 -623
  109. package/src/series/plugins/PieEventTexts.ts +284 -284
  110. package/src/series/plugins/PieLabels.ts +640 -640
  111. package/src/series/plugins/Rose.ts +516 -516
  112. package/src/series/plugins/RoseLabels.ts +600 -600
  113. package/src/series/plugins/SeriesLegend.ts +107 -107
  114. package/src/series/plugins/SeriesTooltip.ts +66 -66
  115. package/src/series/seriesObservables.ts +145 -145
  116. package/src/series/seriesUtils.ts +51 -51
  117. package/src/tree/defaults.ts +102 -102
  118. package/src/tree/index.ts +4 -4
  119. package/src/tree/plugins/TreeLegend.ts +100 -100
  120. package/src/tree/plugins/TreeMap.ts +341 -341
  121. package/src/tree/plugins/TreeTooltip.ts +66 -66
  122. package/src/utils/commonUtils.ts +31 -31
  123. package/src/utils/d3Graphics.ts +176 -176
  124. package/src/utils/d3Utils.ts +92 -92
  125. package/src/utils/observables.ts +14 -14
  126. package/src/utils/orbchartsUtils.ts +129 -129
  127. package/tsconfig.base.json +13 -13
  128. package/tsconfig.json +2 -2
  129. package/vite.config.js +22 -22
  130. package/dist/src/multiValue/plugins/OrdinalXAxis.d.ts +0 -0
  131. package/dist/src/multiValue/plugins/RankingAxis_legacy.d.ts +0 -0
  132. package/src/multiValue/plugins/OrdinalXAxis.ts +0 -0
  133. package/src/multiValue/plugins/RankingAxis_legacy.ts +0 -109
@@ -0,0 +1,730 @@
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
+ ComputedDatumMultiValue,
14
+ ComputedDatumWithSumMultiValue,
15
+ ComputedDataMultiValue,
16
+ // ComputedLayoutDataGrid,
17
+ DataFormatterTypeMap,
18
+ ContainerPositionScaled,
19
+ ContainerSize,
20
+ EventMultiValue,
21
+ ChartParams,
22
+ Layout,
23
+ TransformData } from '../../lib/core-types'
24
+ import type { BaseOrdinalBubblesParams } from '../../lib/plugins-basic-types'
25
+ import { getD3TransitionEase } from '../utils/d3Utils'
26
+ import { getClassName, getUniID } from '../utils/orbchartsUtils'
27
+ import { multiValueContainerSelectionsObservable } from '../multiValue/multiValueObservables'
28
+
29
+ // export interface BaseBarsParams {
30
+ // // barType: BarType
31
+ // barWidth: number
32
+ // barPadding: number
33
+ // barGroupPadding: number // 群組和群組間的間隔
34
+ // barRadius: number | boolean
35
+ // }
36
+
37
+ interface BaseRacingBarsContext {
38
+ selection: d3.Selection<any, unknown, any, unknown>
39
+ computedData$: Observable<ComputedDataMultiValue>
40
+ visibleComputedRankingData$: Observable<ComputedDatumWithSumMultiValue[][]>
41
+ CategoryDataMap$: Observable<Map<string, ComputedDatumMultiValue[]>>
42
+ valueLabels$: Observable<string[]>
43
+ fullParams$: Observable<BaseOrdinalBubblesParams >
44
+ fullDataFormatter$: Observable<DataFormatterTypeMap<'multiValue'>>
45
+ fullChartParams$: Observable<ChartParams>
46
+ // xyValueIndex$: Observable<[number, number]>
47
+ highlight$: Observable<ComputedDatumMultiValue[]>
48
+ rankingItemHeight$: Observable<number>
49
+ rankingScaleList$: Observable<d3.ScalePoint<string>[]>
50
+ containerPosition$: Observable<ContainerPositionScaled[]>
51
+ containerSize$: Observable<ContainerSize>
52
+ ordinalScale$: Observable<d3.ScaleLinear<number, number>>
53
+ ordinalPadding$: Observable<number>
54
+ isCategorySeprate$: Observable<boolean>
55
+ event$: Subject<EventMultiValue>
56
+ }
57
+
58
+ interface RenderGraphicGParams {
59
+ containerSelection: d3.Selection<SVGGElement, ComputedDatumMultiValue[], any, any>
60
+ paddingGClassName: string
61
+ itemGClassName: string
62
+ bubbleData: BubblesDatum[][]
63
+ // rankingScaleList: d3.ScalePoint<string>[]
64
+ transitionDuration: number
65
+ transitionEase: string
66
+ ordinalPadding: number
67
+ }
68
+
69
+ // 對應到 value 裡的每個值
70
+ interface BubbleValueDatum {
71
+ valueIndex: number
72
+ x: number
73
+ y: number
74
+ r: number
75
+ opacity: number
76
+ // _originR: number // 紀錄變化前的r
77
+ _refDatum: ComputedDatumWithSumMultiValue // reference到資料本身
78
+ }
79
+
80
+ interface BubblesDatum extends ComputedDatumWithSumMultiValue {
81
+ graphicValue: Array<BubbleValueDatum>
82
+ // _visibleValue: number[]
83
+ }
84
+
85
+ type ClipPathDatum = {
86
+ id: string;
87
+ // x: number;
88
+ // y: number;
89
+ width: number;
90
+ height: number;
91
+ }
92
+
93
+
94
+ function renderGraphicG ({ containerSelection, paddingGClassName, itemGClassName, bubbleData, transitionDuration, transitionEase, ordinalPadding }: RenderGraphicGParams) {
95
+ containerSelection
96
+ .each((_, categoryIndex, g) => {
97
+ const container = d3.select(g[categoryIndex])
98
+ container.selectAll<SVGGElement, ComputedDatumMultiValue>(`g.${paddingGClassName}`)
99
+ .data([0])
100
+ .join(
101
+ enter => {
102
+ return enter
103
+ .append('g')
104
+ .attr('class', paddingGClassName)
105
+ },
106
+ update => update,
107
+ exit => exit.remove()
108
+ )
109
+ .attr('transform', `translate(${ordinalPadding}, 0)`)
110
+ .each((d, i, g) => {
111
+ const paddingG = d3.select(g[i])
112
+ paddingG.selectAll<SVGGElement, ComputedDatumMultiValue>(`g.${itemGClassName}`)
113
+ .data(bubbleData[categoryIndex] ?? [], d => d.id)
114
+ .join(
115
+ enter => {
116
+ return enter
117
+ .append('g')
118
+ .attr('class', itemGClassName)
119
+ .attr('cursor', 'pointer')
120
+ .attr('transform', d => {
121
+ return `translate(0, ${d.graphicValue[0] ? d.graphicValue[0].y : 0})`
122
+ })
123
+ },
124
+ update => {
125
+ return update
126
+ .transition()
127
+ .duration(transitionDuration)
128
+ // .ease(d3.easeLinear)
129
+ .ease(getD3TransitionEase(transitionEase))
130
+ .attr('transform', d => {
131
+ return `translate(0, ${d.graphicValue[0] ? d.graphicValue[0].y : 0})`
132
+ })
133
+ },
134
+ exit => exit.remove()
135
+ )
136
+ })
137
+ })
138
+
139
+ const graphicBarSelection: d3.Selection<SVGRectElement, BubblesDatum, SVGGElement, unknown> = containerSelection.selectAll(`g.${itemGClassName}`)
140
+
141
+ return graphicBarSelection
142
+ }
143
+
144
+ function renderBubbles ({ graphicGSelection, transitionDuration }: {
145
+ graphicGSelection: d3.Selection<SVGGElement, BubblesDatum, any, any>
146
+ // bubblesData: BubblesDatum[][]
147
+ // fullParams: BaseOrdinalBubblesParams
148
+ // fullChartParams: ChartParams
149
+ // sumSeries: boolean
150
+ transitionDuration: number
151
+ }) {
152
+
153
+ graphicGSelection
154
+ .each((datum, i, g) => {
155
+ const _graphicGSelection = d3.select(g[i])
156
+
157
+ _graphicGSelection.selectAll<SVGCircleElement, typeof datum>('circle')
158
+ .data(datum.graphicValue)
159
+ .join('circle')
160
+ .attr('fill', d => datum.color)
161
+ .style('opacity', d => d.opacity)
162
+ .transition()
163
+ .duration(100)
164
+ .ease(d3.easeLinear)
165
+ .attr('cx', d => d.x)
166
+ // .attr('cy', d => d.y)
167
+ .attr('r', d => d.r)
168
+
169
+ // const itemGSelection = _graphicGSelection.selectAll<SVGGElement, BubblesDatum>("g")
170
+ // .data(bubblesData[i], (d) => d.id)
171
+ // .join('g')
172
+ // .each((datum, datumIndex, _g) => {
173
+ // const _itemGSelection = d3.select(_g[datumIndex])
174
+
175
+ // _itemGSelection.selectAll<SVGCircleElement, typeof datum>('circle')
176
+ // .data(datum.graphicValue)
177
+ // .join('circle')
178
+ // .attr('fill', d => datum.color)
179
+ // .style('opacity', 0.8)
180
+ // .transition()
181
+ // .duration(200)
182
+ // .ease(d3.easeLinear)
183
+ // .attr('cx', d => d.x)
184
+ // .attr('cy', d => d.y)
185
+ // .attr('r', d => d.r)
186
+ // })
187
+ })
188
+
189
+ // const itemGSelection = selection.selectAll<SVGGElement, BubblesDatum[]>("g")
190
+ // .data(bubblesData)
191
+ // .join(
192
+ // enter => {
193
+ // return enter
194
+ // .append('g')
195
+ // .attr('cursor', 'pointer')
196
+ // },
197
+ // update => {
198
+ // return update
199
+ // },
200
+ // exit => {
201
+ // return exit
202
+ // .remove()
203
+ // }
204
+ // )
205
+ // .each((d, i, nodes) => {
206
+ // const g = d3.select(nodes[i])
207
+ // g.selectAll<SVGCircleElement, BubblesDatum>("circle")
208
+ // .data(d)
209
+ // })
210
+
211
+ const bubblesSelection: d3.Selection<SVGRectElement, BubbleValueDatum, SVGGElement, unknown> = graphicGSelection.selectAll(`circle`)
212
+
213
+ return bubblesSelection
214
+ }
215
+
216
+ function renderClipPath ({ defsSelection, clipPathData }: {
217
+ defsSelection: d3.Selection<SVGDefsElement, any, any, any>
218
+ clipPathData: ClipPathDatum[]
219
+ }) {
220
+ const clipPath = defsSelection
221
+ .selectAll<SVGClipPathElement, Layout>('clipPath')
222
+ .data(clipPathData)
223
+ .join(
224
+ enter => {
225
+ return enter
226
+ .append('clipPath')
227
+ },
228
+ update => update,
229
+ exit => exit.remove()
230
+ )
231
+ .attr('id', d => d.id)
232
+ .each((d, i, g) => {
233
+ const rect = d3.select(g[i])
234
+ .selectAll<SVGRectElement, typeof d>('rect')
235
+ .data([d])
236
+ .join(
237
+ enter => {
238
+ return enter
239
+ .append('rect')
240
+ },
241
+ update => update,
242
+ exit => exit.remove()
243
+ )
244
+ .attr('x', 0)
245
+ .attr('y', 0)
246
+ .attr('width', _d => _d.width)
247
+ .attr('height', _d => _d.height)
248
+ })
249
+ }
250
+
251
+ function highlight ({ selection, ids, fullChartParams }: {
252
+ selection: d3.Selection<any, BubbleValueDatum, any, any>
253
+ ids: string[]
254
+ fullChartParams: ChartParams
255
+ }) {
256
+ selection.interrupt('highlight')
257
+
258
+ if (!ids.length) {
259
+ // remove highlight
260
+ selection
261
+ .transition('highlight')
262
+ .duration(200)
263
+ .style('opacity', (d, i) => d.opacity)
264
+ return
265
+ }
266
+
267
+ selection
268
+ .each((d, i, n) => {
269
+ const datum = d._refDatum
270
+ if (ids.includes(datum.id)) {
271
+ d3.select(n[i])
272
+ .style('opacity', (d: BubbleValueDatum) => d.opacity)
273
+ } else {
274
+ d3.select(n[i])
275
+ .style('opacity', fullChartParams.styles.unhighlightedOpacity)
276
+ }
277
+ })
278
+ }
279
+
280
+
281
+ export const createBaseOrdinalBubbles: BasePluginFn<BaseRacingBarsContext> = (pluginName: string, {
282
+ selection,
283
+ computedData$,
284
+ visibleComputedRankingData$,
285
+ // xyValueIndex$,
286
+ // categoryLabels$,
287
+ CategoryDataMap$,
288
+ valueLabels$,
289
+ fullParams$,
290
+ fullDataFormatter$,
291
+ fullChartParams$,
292
+ // layout$,
293
+ // graphicTransform$,
294
+ // graphicReverseScale$,
295
+ highlight$,
296
+ // computedRankingAmountList$,
297
+ rankingItemHeight$,
298
+ rankingScaleList$,
299
+ containerPosition$,
300
+ containerSize$,
301
+ // layout$,
302
+ ordinalScale$,
303
+ ordinalPadding$,
304
+ isCategorySeprate$,
305
+ event$
306
+ }) => {
307
+
308
+ const destroy$ = new Subject()
309
+
310
+ const clipPathID = getUniID(pluginName, 'clipPath-box')
311
+ const paddingGClassName = getClassName(pluginName, 'padding-g')
312
+ const itemGClassName = getClassName(pluginName, 'item-g')
313
+ const bubbleClassName = getClassName(pluginName, 'bubble')
314
+ // const containerClassName = getClassName(pluginName, 'container')
315
+
316
+
317
+ const containerSelection$ = multiValueContainerSelectionsObservable({
318
+ selection,
319
+ pluginName,
320
+ clipPathID,
321
+ computedData$,
322
+ containerPosition$,
323
+ isCategorySeprate$,
324
+ }).pipe(
325
+ takeUntil(destroy$),
326
+ )
327
+
328
+ containerSize$.subscribe(data => {
329
+ const defsSelection = selection.selectAll<SVGDefsElement, any>('defs')
330
+ .data([clipPathID])
331
+ .join('defs')
332
+ const clipPathData = [{
333
+ id: clipPathID,
334
+ width: data.width,
335
+ height: data.height
336
+ }]
337
+ renderClipPath({
338
+ defsSelection: defsSelection,
339
+ clipPathData,
340
+ // textReverseTransform: data.textReverseTransform
341
+ })
342
+ })
343
+
344
+ const maxRadius$ = combineLatest({
345
+ sizeAdjust: fullParams$.pipe(
346
+ map(p => p.bubble.sizeAdjust),
347
+ distinctUntilChanged()
348
+ ),
349
+ rankingItemHeight: rankingItemHeight$
350
+ }).pipe(
351
+ takeUntil(destroy$),
352
+ switchMap(async d => d),
353
+ map(d => (d.rankingItemHeight * d.sizeAdjust) / 2),
354
+ distinctUntilChanged(),
355
+ shareReplay(1)
356
+ )
357
+
358
+ const scaleDomain$ = combineLatest({
359
+ visibleComputedRankingData: visibleComputedRankingData$,
360
+ scaleDomain: fullDataFormatter$.pipe(
361
+ map(d => d.xAxis.scaleDomain),
362
+ ),
363
+ }).pipe(
364
+ takeUntil(destroy$),
365
+ switchMap(async d => d),
366
+ map(data => {
367
+ const firstValue = data.visibleComputedRankingData[0] && data.visibleComputedRankingData[0][0]
368
+ ? data.visibleComputedRankingData[0][0].value
369
+ : []
370
+ let startIndex = data.scaleDomain[0] === 'auto' || data.scaleDomain[0] === 'min'
371
+ ? 0
372
+ : data.scaleDomain[0]
373
+ let endIndex = data.scaleDomain[1] === 'auto' || data.scaleDomain[1] === 'max'
374
+ ? firstValue.length - 1 // 用第一筆資料判斷value長度
375
+ : data.scaleDomain[1]
376
+ return [startIndex, endIndex]
377
+ }),
378
+ distinctUntilChanged((a, b) => a[0] === b[0] && a[1] === b[1]),
379
+ shareReplay(1)
380
+ )
381
+
382
+ const minMaxValue$ = combineLatest({
383
+ visibleComputedRankingData: visibleComputedRankingData$,
384
+ scaleDomain: scaleDomain$,
385
+ }).pipe(
386
+ takeUntil(destroy$),
387
+ switchMap(async d => d),
388
+ map(data => {
389
+ let minValue = 0
390
+ let maxValue = 0
391
+ // console.log(data.scaleDomain[0], data.scaleDomain[1])
392
+ data.visibleComputedRankingData.forEach(categoryData => {
393
+ categoryData.forEach(datum => {
394
+ for (let i = data.scaleDomain[0]; i <= data.scaleDomain[1]; i++) {
395
+ const v = datum.value[i]
396
+ if (v == null) {
397
+ continue
398
+ }
399
+ if (v > maxValue) {
400
+ maxValue = v
401
+ } else if (v < minValue) {
402
+ minValue = v
403
+ }
404
+ }
405
+ })
406
+ })
407
+ // console.log([minValue, maxValue])
408
+ return [minValue, maxValue]
409
+ }),
410
+ distinctUntilChanged(),
411
+ shareReplay(1)
412
+ )
413
+
414
+
415
+ const opacityScale$ = combineLatest({
416
+ minMaxValue: minMaxValue$,
417
+ fullParams: fullParams$
418
+ }).pipe(
419
+ takeUntil(destroy$),
420
+ map(data => {
421
+ const opacityScale = d3.scaleLinear()
422
+ .domain(data.minMaxValue)
423
+ .range(data.fullParams.bubble.valueLinearOpacity)
424
+ return opacityScale
425
+ }),
426
+ distinctUntilChanged(),
427
+ shareReplay(1)
428
+ )
429
+
430
+ const radiusScale$ = combineLatest({
431
+ maxRadius: maxRadius$,
432
+ minMaxValue: minMaxValue$,
433
+ arcScaleType: fullParams$.pipe(
434
+ map(p => p.bubble.arcScaleType),
435
+ distinctUntilChanged()
436
+ )
437
+ }).pipe(
438
+ takeUntil(destroy$),
439
+ switchMap(async d => d),
440
+ map(data => {
441
+ // 半徑比例尺
442
+ const radiusScale = d3.scalePow()
443
+ .domain([0, data.minMaxValue[1]])
444
+ .range([2, data.maxRadius]) // 最小半徑為2
445
+ .exponent(data.arcScaleType === 'area'
446
+ ? 0.5 // 數值映射面積(0.5為取平方根)
447
+ : 1 // 數值映射半徑
448
+ )
449
+ return radiusScale
450
+ }),
451
+ distinctUntilChanged(),
452
+ shareReplay(1)
453
+ )
454
+
455
+ const bubbleData$: Observable<BubblesDatum[][]> = combineLatest({
456
+ visibleComputedRankingData: visibleComputedRankingData$,
457
+ // computedData: computedData$,
458
+ // fullParams: fullParams$,
459
+ // fullChartParams: fullChartParams$,
460
+ radiusScale: radiusScale$,
461
+ rankingScaleList: rankingScaleList$,
462
+ ordinalScale: ordinalScale$,
463
+ opacityScale: opacityScale$,
464
+ scaleDomain: scaleDomain$,
465
+ containerSize: containerSize$,
466
+ }).pipe(
467
+ takeUntil(destroy$),
468
+ switchMap(async d => d),
469
+ map(data => {
470
+ // console.log('data.visibleComputedRankingData', data.visibleComputedRankingData)
471
+ return data.visibleComputedRankingData.map((categoryData, categoryIndex) => {
472
+ const rankingScale = data.rankingScaleList[categoryIndex]
473
+
474
+ return categoryData.map((_d, i) => {
475
+ const d = _d as BubblesDatum
476
+ const graphicValue = d.value.map((v, vIndex) => {
477
+ // console.log('data.ordinalScale(vIndex)', data.ordinalScale(vIndex))
478
+ // const opacity = vIndex < data.scaleDomain[0] || vIndex > data.scaleDomain[1]
479
+ // ? 0
480
+ // : data.opacityScale(v)
481
+ let x = data.ordinalScale(vIndex)
482
+
483
+ if (data.scaleDomain[0] === data.scaleDomain[1]) {
484
+ if (vIndex > data.scaleDomain[0]) {
485
+ x += data.containerSize.width * 1.5
486
+ } else if (vIndex < data.scaleDomain[0]) {
487
+ x -= data.containerSize.width * 1.5
488
+ }
489
+ }
490
+
491
+ return {
492
+ valueIndex: vIndex,
493
+ x,
494
+ y: rankingScale(d.label),
495
+ r: data.radiusScale(v),
496
+ opacity: data.opacityScale(v),
497
+ // _originR: data.radiusScale(v)
498
+ _refDatum: d // reference到資料本身
499
+ }
500
+ })
501
+
502
+ // d._visibleValue = [] // highlight的時候才寫入
503
+ return {
504
+ ...d,
505
+ graphicValue
506
+ }
507
+ })
508
+ })
509
+ })
510
+ )
511
+
512
+ const transitionDuration$ = fullChartParams$.pipe(
513
+ takeUntil(destroy$),
514
+ map(d => d.transitionDuration),
515
+ distinctUntilChanged()
516
+ )
517
+
518
+ const transitionEase$ = fullChartParams$.pipe(
519
+ takeUntil(destroy$),
520
+ map(d => d.transitionEase),
521
+ distinctUntilChanged()
522
+ )
523
+
524
+ const graphicGSelection$ = combineLatest({
525
+ containerSelection: containerSelection$,
526
+ bubbleData: bubbleData$,
527
+ rankingScaleList: rankingScaleList$,
528
+ transitionDuration: transitionDuration$,
529
+ transitionEase: transitionEase$,
530
+ ordinalPadding: ordinalPadding$
531
+ }).pipe(
532
+ takeUntil(destroy$),
533
+ switchMap(async (d) => d),
534
+ map(data => {
535
+ // console.log('bubbleData', data.bubbleData)
536
+ return renderGraphicG({
537
+ containerSelection: data.containerSelection,
538
+ paddingGClassName,
539
+ itemGClassName,
540
+ bubbleData: data.bubbleData,
541
+ // rankingScaleList: data.rankingScaleList,
542
+ transitionDuration: data.transitionDuration,
543
+ transitionEase: data.transitionEase,
544
+ ordinalPadding: data.ordinalPadding
545
+ })
546
+ })
547
+ )
548
+
549
+ const graphicSelection$ = combineLatest({
550
+ graphicGSelection: graphicGSelection$,
551
+ // xyValueIndex: xyValueIndex$,
552
+ ordinalScale: ordinalScale$,
553
+ bubbleData: bubbleData$,
554
+ transitionDuration: transitionDuration$,
555
+ fullParams: fullParams$,
556
+ }).pipe(
557
+ takeUntil(destroy$),
558
+ switchMap(async (d) => d),
559
+ map(data => {
560
+
561
+ return renderBubbles({
562
+ graphicGSelection: data.graphicGSelection,
563
+ // bubblesData: data.bubbleData
564
+ transitionDuration: data.transitionDuration
565
+ })
566
+ }),
567
+ shareReplay(1)
568
+ )
569
+
570
+ const highlightTarget$ = fullChartParams$.pipe(
571
+ takeUntil(destroy$),
572
+ map(d => d.highlightTarget),
573
+ distinctUntilChanged()
574
+ )
575
+
576
+ combineLatest({
577
+ graphicSelection: graphicSelection$,
578
+ computedData: computedData$,
579
+ CategoryDataMap: CategoryDataMap$,
580
+ valueLabels: valueLabels$,
581
+ highlightTarget: highlightTarget$,
582
+ fullDataFormatter: fullDataFormatter$,
583
+ }).pipe(
584
+ takeUntil(destroy$),
585
+ switchMap(async (d) => d),
586
+ ).subscribe(data => {
587
+
588
+ data.graphicSelection
589
+ .on('mouseover', (event, valueDatum) => {
590
+ // event.stopPropagation()
591
+
592
+ // reference 到資料本身
593
+ const datum = valueDatum._refDatum
594
+
595
+ // 只顯示目前的值
596
+ // datum._visibleValue = [datum.value[valueDatum.index]]
597
+ // 只顯示總數
598
+ // datum._visibleValue = [datum.sum]
599
+
600
+ event$.next({
601
+ type: 'multiValue',
602
+ eventName: 'mouseover',
603
+ pluginName,
604
+ highlightTarget: data.highlightTarget,
605
+ valueDetail: [
606
+ {
607
+ value: datum.value[valueDatum.valueIndex],
608
+ valueIndex: valueDatum.valueIndex,
609
+ valueLabel: data.valueLabels[valueDatum.valueIndex]
610
+ },
611
+ ],
612
+ datum: datum,
613
+ category: data.CategoryDataMap.get(datum.categoryLabel)!,
614
+ categoryIndex: datum.categoryIndex,
615
+ categoryLabel: datum.categoryLabel,
616
+ data: data.computedData,
617
+ event,
618
+ })
619
+ })
620
+ .on('mousemove', (event, valueDatum) => {
621
+ // event.stopPropagation()
622
+
623
+ // reference 到資料本身
624
+ const datum = valueDatum._refDatum
625
+
626
+ // 只顯示目前的值
627
+ // datum._visibleValue = [datum.value[valueDatum.index]]
628
+ // 只顯示總數
629
+ // datum._visibleValue = [datum.sum]
630
+ event$.next({
631
+ type: 'multiValue',
632
+ eventName: 'mousemove',
633
+ pluginName,
634
+ highlightTarget: data.highlightTarget,
635
+ valueDetail: [
636
+ {
637
+ value: datum.value[valueDatum.valueIndex],
638
+ valueIndex: valueDatum.valueIndex,
639
+ valueLabel: data.valueLabels[valueDatum.valueIndex]
640
+ },
641
+ ],
642
+ datum,
643
+ category: data.CategoryDataMap.get(datum.categoryLabel)!,
644
+ categoryIndex: datum.categoryIndex,
645
+ categoryLabel: datum.categoryLabel,
646
+ data: data.computedData,
647
+ event,
648
+ })
649
+ })
650
+ .on('mouseout', (event, valueDatum) => {
651
+ // event.stopPropagation()
652
+
653
+ // reference 到資料本身
654
+ const datum = valueDatum._refDatum
655
+
656
+ event$.next({
657
+ type: 'multiValue',
658
+ eventName: 'mouseout',
659
+ pluginName,
660
+ highlightTarget: data.highlightTarget,
661
+ valueDetail: [
662
+ {
663
+ value: datum.value[valueDatum.valueIndex],
664
+ valueIndex: valueDatum.valueIndex,
665
+ valueLabel: data.valueLabels[valueDatum.valueIndex]
666
+ },
667
+ ],
668
+ datum,
669
+ category: data.CategoryDataMap.get(datum.categoryLabel)!,
670
+ categoryIndex: datum.categoryIndex,
671
+ categoryLabel: datum.categoryLabel,
672
+ data: data.computedData,
673
+ event,
674
+ })
675
+ })
676
+ .on('click', (event, valueDatum) => {
677
+ // event.stopPropagation()
678
+
679
+ // reference 到資料本身
680
+ const datum = valueDatum._refDatum
681
+
682
+ // 只顯示目前的值
683
+ // datum._visibleValue = [datum.value[valueDatum.index]]
684
+ // 只顯示總數
685
+ // datum._visibleValue = [datum.sum]
686
+
687
+ event$.next({
688
+ type: 'multiValue',
689
+ eventName: 'click',
690
+ pluginName,
691
+ highlightTarget: data.highlightTarget,
692
+ valueDetail: [
693
+ {
694
+ value: datum.value[valueDatum.valueIndex],
695
+ valueIndex: valueDatum.valueIndex,
696
+ valueLabel: data.valueLabels[valueDatum.valueIndex]
697
+ },
698
+ ],
699
+ datum,
700
+ category: data.CategoryDataMap.get(datum.categoryLabel)!,
701
+ categoryIndex: datum.categoryIndex,
702
+ categoryLabel: datum.categoryLabel,
703
+ data: data.computedData,
704
+ event,
705
+ })
706
+ })
707
+
708
+ })
709
+
710
+ combineLatest({
711
+ graphicSelection: graphicSelection$,
712
+ highlight: highlight$.pipe(
713
+ map(data => data.map(d => d.id))
714
+ ),
715
+ fullChartParams: fullChartParams$
716
+ }).pipe(
717
+ takeUntil(destroy$),
718
+ switchMap(async d => d)
719
+ ).subscribe(data => {
720
+ highlight({
721
+ selection: data.graphicSelection,
722
+ ids: data.highlight,
723
+ fullChartParams: data.fullChartParams
724
+ })
725
+ })
726
+
727
+ return () => {
728
+ destroy$.next(undefined)
729
+ }
730
+ }