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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/LICENSE +200 -200
  2. package/dist/orbcharts-plugins-basic.es.js +2609 -2595
  3. package/dist/orbcharts-plugins-basic.umd.js +36 -34
  4. package/dist/src/grid/defaults.d.ts +2 -2
  5. package/dist/src/multiGrid/defaults.d.ts +1 -1
  6. package/dist/src/multiValue/defaults.d.ts +1 -1
  7. package/lib/core-types.ts +7 -7
  8. package/lib/core.ts +6 -6
  9. package/lib/plugins-basic-types.ts +6 -6
  10. package/package.json +44 -44
  11. package/src/base/BaseBars.ts +765 -765
  12. package/src/base/BaseBarsTriangle.ts +676 -676
  13. package/src/base/BaseDots.ts +464 -464
  14. package/src/base/BaseGroupAxis.ts +691 -691
  15. package/src/base/BaseLegend.ts +684 -684
  16. package/src/base/BaseLineAreas.ts +629 -629
  17. package/src/base/BaseLines.ts +706 -706
  18. package/src/base/BaseRacingBars.ts +551 -551
  19. package/src/base/BaseRacingLabels.ts +396 -396
  20. package/src/base/BaseRacingValueLabels.ts +403 -403
  21. package/src/base/BaseStackedBars.ts +782 -782
  22. package/src/base/BaseTooltip.ts +386 -386
  23. package/src/base/BaseValueAxis.ts +600 -600
  24. package/src/base/BaseXAxis.ts +427 -427
  25. package/src/base/BaseYAxis.ts +389 -389
  26. package/src/base/types.ts +2 -2
  27. package/src/const.ts +30 -30
  28. package/src/grid/defaults.ts +213 -213
  29. package/src/grid/gridObservables.ts +612 -612
  30. package/src/grid/index.ts +16 -16
  31. package/src/grid/plugins/Bars.ts +69 -69
  32. package/src/grid/plugins/BarsPN.ts +66 -66
  33. package/src/grid/plugins/BarsTriangle.ts +73 -73
  34. package/src/grid/plugins/Dots.ts +68 -68
  35. package/src/grid/plugins/GridLegend.ts +107 -107
  36. package/src/grid/plugins/GridTooltip.ts +66 -66
  37. package/src/grid/plugins/GroupAux.ts +1120 -1120
  38. package/src/grid/plugins/GroupAxis.ts +73 -73
  39. package/src/grid/plugins/GroupZoom.ts +218 -218
  40. package/src/grid/plugins/LineAreas.ts +65 -65
  41. package/src/grid/plugins/Lines.ts +59 -59
  42. package/src/grid/plugins/StackedBars.ts +64 -64
  43. package/src/grid/plugins/StackedValueAxis.ts +96 -96
  44. package/src/grid/plugins/ValueAxis.ts +94 -94
  45. package/src/index.ts +6 -6
  46. package/src/multiGrid/defaults.ts +244 -244
  47. package/src/multiGrid/index.ts +14 -14
  48. package/src/multiGrid/multiGridObservables.ts +50 -50
  49. package/src/multiGrid/plugins/MultiBars.ts +108 -108
  50. package/src/multiGrid/plugins/MultiBarsTriangle.ts +114 -114
  51. package/src/multiGrid/plugins/MultiDots.ts +102 -102
  52. package/src/multiGrid/plugins/MultiGridLegend.ts +169 -169
  53. package/src/multiGrid/plugins/MultiGridTooltip.ts +66 -66
  54. package/src/multiGrid/plugins/MultiGroupAxis.ts +137 -137
  55. package/src/multiGrid/plugins/MultiLineAreas.ts +107 -107
  56. package/src/multiGrid/plugins/MultiLines.ts +101 -101
  57. package/src/multiGrid/plugins/MultiStackedBars.ts +106 -106
  58. package/src/multiGrid/plugins/MultiStackedValueAxis.ts +134 -134
  59. package/src/multiGrid/plugins/MultiValueAxis.ts +134 -134
  60. package/src/multiGrid/plugins/OverlappingStackedValueAxes.ts +300 -300
  61. package/src/multiGrid/plugins/OverlappingValueAxes.ts +300 -300
  62. package/src/multiValue/defaults.ts +391 -388
  63. package/src/multiValue/index.ts +12 -12
  64. package/src/multiValue/multiValueObservables.ts +666 -666
  65. package/src/multiValue/plugins/MultiValueLegend.ts +107 -107
  66. package/src/multiValue/plugins/MultiValueTooltip.ts +66 -66
  67. package/src/multiValue/plugins/RacingBars.ts +373 -373
  68. package/src/multiValue/plugins/RacingCounterTexts.ts +300 -300
  69. package/src/multiValue/plugins/RacingValueAxis.ts +114 -114
  70. package/src/multiValue/plugins/RankingAxis_legacy.ts +109 -109
  71. package/src/multiValue/plugins/Scatter.ts +426 -426
  72. package/src/multiValue/plugins/ScatterBubbles.ts +554 -554
  73. package/src/multiValue/plugins/XAxis.ts +107 -107
  74. package/src/multiValue/plugins/XYAux.ts +682 -682
  75. package/src/multiValue/plugins/XYAxes.ts +194 -194
  76. package/src/multiValue/plugins/XYAxes_legacy.ts +683 -683
  77. package/src/multiValue/plugins/XZoom.ts +299 -299
  78. package/src/noneData/defaults.ts +102 -102
  79. package/src/noneData/index.ts +3 -3
  80. package/src/noneData/plugins/Container.ts +27 -27
  81. package/src/noneData/plugins/Tooltip.ts +373 -373
  82. package/src/relationship/defaults.ts +221 -220
  83. package/src/relationship/index.ts +5 -5
  84. package/src/relationship/plugins/ForceDirected.ts +1173 -1168
  85. package/src/relationship/plugins/ForceDirectedBubbles.ts +1411 -1403
  86. package/src/relationship/plugins/RelationshipLegend.ts +100 -100
  87. package/src/relationship/plugins/RelationshipTooltip.ts +66 -66
  88. package/src/relationship/relationshipObservables.ts +49 -49
  89. package/src/series/defaults.ts +221 -221
  90. package/src/series/index.ts +9 -9
  91. package/src/series/plugins/Bubbles.ts +636 -636
  92. package/src/series/plugins/Pie.ts +623 -623
  93. package/src/series/plugins/PieEventTexts.ts +284 -284
  94. package/src/series/plugins/PieLabels.ts +640 -640
  95. package/src/series/plugins/Rose.ts +516 -516
  96. package/src/series/plugins/RoseLabels.ts +600 -600
  97. package/src/series/plugins/SeriesLegend.ts +107 -107
  98. package/src/series/plugins/SeriesTooltip.ts +66 -66
  99. package/src/series/seriesObservables.ts +145 -145
  100. package/src/series/seriesUtils.ts +51 -51
  101. package/src/tree/defaults.ts +102 -102
  102. package/src/tree/index.ts +4 -4
  103. package/src/tree/plugins/TreeLegend.ts +100 -100
  104. package/src/tree/plugins/TreeMap.ts +341 -341
  105. package/src/tree/plugins/TreeTooltip.ts +66 -66
  106. package/src/utils/commonUtils.ts +31 -31
  107. package/src/utils/d3Graphics.ts +176 -176
  108. package/src/utils/d3Utils.ts +92 -92
  109. package/src/utils/observables.ts +14 -14
  110. package/src/utils/orbchartsUtils.ts +129 -129
  111. package/tsconfig.base.json +13 -13
  112. package/tsconfig.json +2 -2
  113. package/vite.config.js +22 -22
@@ -1,465 +1,465 @@
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
- ComputedAxesDataGrid,
15
- EventGrid,
16
- ChartParams,
17
- ContainerPositionScaled,
18
- Layout,
19
- TransformData,
20
- ColorType
21
- } from '../../lib/core-types'
22
- import type { BaseDotsParams } from '../../lib/plugins-basic-types'
23
- import { getDatumColor, getClassName, getUniID } from '../utils/orbchartsUtils'
24
- import { gridSelectionsObservable } from '../grid/gridObservables'
25
-
26
- // export interface BaseDotsParams {
27
- // radius: number
28
- // fillColorType: ColorType
29
- // strokeColorType: ColorType
30
- // strokeWidth: number
31
- // // strokeWidthWhileHighlight: number
32
- // onlyShowHighlighted: boolean
33
- // }
34
-
35
- interface BaseDotsContext {
36
- selection: d3.Selection<any, unknown, any, unknown>
37
- computedData$: Observable<ComputedDataGrid>
38
- computedAxesData$: Observable<ComputedAxesDataGrid>
39
- visibleComputedData$: Observable<ComputedDatumGrid[][]>
40
- visibleComputedAxesData$: Observable<ComputedAxesDataGrid>
41
- seriesLabels$: Observable<string[]>
42
- SeriesDataMap$: Observable<Map<string, ComputedDatumGrid[]>>
43
- GroupDataMap$: Observable<Map<string, ComputedDatumGrid[]>>
44
- fullParams$: Observable<BaseDotsParams>
45
- fullChartParams$: Observable<ChartParams>
46
- gridAxesTransform$: Observable<TransformData>
47
- gridGraphicTransform$: Observable<TransformData>
48
- gridGraphicReverseScale$: Observable<[number, number][]>
49
- gridAxesSize$: Observable<{
50
- width: number;
51
- height: number;
52
- }>
53
- gridHighlight$: Observable<ComputedDatumGrid[]>
54
- gridContainerPosition$: Observable<ContainerPositionScaled[]>
55
- event$: Subject<EventGrid>
56
- }
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 = 'Dots'
68
- // const circleGClassName = getClassName(pluginName, 'circleG')
69
- // const circleClassName = getClassName(pluginName, 'circle')
70
-
71
- function renderDots ({ graphicGSelection, circleGClassName, circleClassName, visibleComputedAxesData, fullParams, fullChartParams, graphicReverseScale }: {
72
- graphicGSelection: d3.Selection<SVGGElement, any, any, any>
73
- circleGClassName: string
74
- circleClassName: string
75
- visibleComputedAxesData: ComputedAxesDataGrid
76
- fullParams: BaseDotsParams
77
- fullChartParams: ChartParams
78
- graphicReverseScale: [number, number][]
79
- }) {
80
- const createEnterDuration = (enter: d3.Selection<d3.EnterElement, ComputedDatumGrid, SVGGElement, any>) => {
81
- const enterSize = enter.size()
82
- const eachDuration = fullChartParams.transitionDuration / enterSize
83
- return eachDuration
84
- }
85
- // enterDuration
86
- let enterDuration = 0
87
-
88
- graphicGSelection
89
- .each((seriesData, seriesIndex, g) => {
90
- d3.select(g[seriesIndex])
91
- .selectAll<SVGGElement, ComputedDatumGrid>('g')
92
- .data(visibleComputedAxesData[seriesIndex], d => d.id)
93
- .join(
94
- enter => {
95
- // enterDuration
96
- enterDuration = createEnterDuration(enter)
97
-
98
- return enter
99
- .append('g')
100
- .classed(circleGClassName, true)
101
- },
102
- update => update,
103
- exit => exit.remove()
104
- )
105
- .attr('transform', d => `translate(${d.axisX}, ${d.axisY})`)
106
- .each((d, i, g) => {
107
- const circle = d3.select(g[i])
108
- .selectAll('circle')
109
- .data([d])
110
- .join(
111
- enter => {
112
- return enter
113
- .append('circle')
114
- .style('cursor', 'pointer')
115
- .style('vector-effect', 'non-scaling-stroke')
116
- .classed(circleClassName, true)
117
- .attr('opacity', 0)
118
- .transition()
119
- .delay((_d, _i) => {
120
- return i * enterDuration
121
- })
122
- .attr('opacity', 1)
123
- },
124
- update => {
125
- return update
126
- .transition()
127
- .duration(50)
128
- // .attr('cx', d => d.axisX)
129
- // .attr('cy', d => d.axisY)
130
- .attr('opacity', 1)
131
- },
132
- exit => exit.remove()
133
- )
134
- .attr('r', fullParams.radius)
135
- .attr('fill', (d, i) => getDatumColor({ datum: d, colorType: fullParams.fillColorType, fullChartParams }))
136
- .attr('stroke', (d, i) => getDatumColor({ datum: d, colorType: fullParams.strokeColorType, fullChartParams }))
137
- .attr('stroke-width', fullParams.strokeWidth)
138
- .attr('transform', `scale(${graphicReverseScale[seriesIndex][0] ?? 1}, ${graphicReverseScale[seriesIndex][1] ?? 1})`)
139
- })
140
- })
141
-
142
- const graphicCircleSelection: d3.Selection<SVGRectElement, ComputedDatumGrid, SVGGElement, unknown> = graphicGSelection.selectAll(`circle.${circleClassName}`)
143
-
144
- return graphicCircleSelection
145
- }
146
-
147
-
148
- function highlightDots ({ selection, ids, onlyShowHighlighted, fullChartParams }: {
149
- selection: d3.Selection<SVGGElement, ComputedDatumGrid, any, any>
150
- ids: string[]
151
- onlyShowHighlighted: boolean
152
- // fullParams: BaseDotsParams
153
- fullChartParams: ChartParams
154
- }) {
155
- selection.interrupt('highlight')
156
- if (!ids.length) {
157
- // remove highlight
158
- selection
159
- .transition('highlight')
160
- .duration(200)
161
- .style('opacity', onlyShowHighlighted === true ? 0 : 1)
162
- // selection
163
- // .attr('stroke-width', fullParams.strokeWidth)
164
-
165
- return
166
- }
167
-
168
- selection
169
- .each((d, i, n) => {
170
- if (ids.includes(d.id)) {
171
- const dot = d3.select(n[i])
172
- dot
173
- .style('opacity', 1)
174
- .transition('highlight')
175
- .duration(200)
176
- // dot
177
- // .attr('stroke-width', fullParams.strokeWidthWhileHighlight)
178
- } else {
179
- const dot = d3.select(n[i])
180
- dot
181
- .style('opacity', onlyShowHighlighted === true ? 0 : fullChartParams.styles.unhighlightedOpacity)
182
- .transition('highlight')
183
- .duration(200)
184
- // dot
185
- // .attr('stroke-width', fullParams.strokeWidth)
186
-
187
- }
188
- })
189
- }
190
-
191
- function renderClipPath ({ defsSelection, clipPathData }: {
192
- defsSelection: d3.Selection<SVGDefsElement, any, any, any>
193
- clipPathData: ClipPathDatum[]
194
- }) {
195
- const clipPath = defsSelection
196
- .selectAll<SVGClipPathElement, Layout>('clipPath')
197
- .data(clipPathData)
198
- .join(
199
- enter => {
200
- return enter
201
- .append('clipPath')
202
- },
203
- update => update,
204
- exit => exit.remove()
205
- )
206
- .attr('id', d => d.id)
207
- .each((d, i, g) => {
208
- const rect = d3.select(g[i])
209
- .selectAll<SVGRectElement, typeof d>('rect')
210
- .data([d])
211
- .join('rect')
212
- .attr('x', 0)
213
- .attr('y', 0)
214
- .attr('width', _d => _d.width)
215
- .attr('height', _d => _d.height)
216
- })
217
-
218
- }
219
-
220
-
221
-
222
- export const createBaseDots: BasePluginFn<BaseDotsContext> = (pluginName: string, {
223
- selection,
224
- computedData$,
225
- computedAxesData$,
226
- visibleComputedData$,
227
- visibleComputedAxesData$,
228
- seriesLabels$,
229
- SeriesDataMap$,
230
- GroupDataMap$,
231
- fullParams$,
232
- fullChartParams$,
233
- gridAxesTransform$,
234
- gridGraphicTransform$,
235
- gridGraphicReverseScale$,
236
- gridAxesSize$,
237
- gridHighlight$,
238
- gridContainerPosition$,
239
- event$
240
- }) => {
241
-
242
- const destroy$ = new Subject()
243
-
244
- const clipPathID = getUniID(pluginName, 'clipPath-box')
245
- const circleGClassName = getClassName(pluginName, 'circleG')
246
- const circleClassName = getClassName(pluginName, 'circle')
247
-
248
- // const axisSelection: d3.Selection<SVGGElement, any, any, any> = selection
249
- // .append('g')
250
- // .attr('clip-path', `url(#${clipPathID})`)
251
- // const defsSelection: d3.Selection<SVGDefsElement, any, any, any> = axisSelection.append('defs')
252
- // const dataAreaSelection: d3.Selection<SVGGElement, any, any, any> = axisSelection.append('g')
253
- // const graphicSelection$: Subject<d3.Selection<SVGGElement, ComputedDatumGrid, any, any>> = new Subject()
254
-
255
- const {
256
- seriesSelection$,
257
- axesSelection$,
258
- defsSelection$,
259
- graphicGSelection$
260
- } = gridSelectionsObservable({
261
- selection,
262
- pluginName,
263
- clipPathID,
264
- seriesLabels$,
265
- gridContainerPosition$,
266
- gridAxesTransform$,
267
- gridGraphicTransform$
268
- })
269
-
270
- const graphicReverseScale$: Observable<[number, number][]> = combineLatest({
271
- // gridGraphicTransform: gridGraphicTransform$,
272
- // gridContainerPosition: gridContainerPosition$,
273
- // gridAxesTransform: gridAxesTransform$
274
- computedData: computedData$,
275
- gridGraphicReverseScale: gridGraphicReverseScale$
276
- }).pipe(
277
- takeUntil(destroy$),
278
- switchMap(async data => data),
279
- map(data => {
280
- return data.computedData.map((series, seriesIndex) => {
281
- return data.gridGraphicReverseScale[seriesIndex]
282
- })
283
- })
284
- )
285
-
286
- const clipPathSubscription = combineLatest({
287
- defsSelection: defsSelection$,
288
- gridAxesSize: gridAxesSize$,
289
- }).pipe(
290
- takeUntil(destroy$),
291
- switchMap(async (d) => d),
292
- ).subscribe(data => {
293
- // 外層的遮罩
294
- const clipPathData = [{
295
- id: clipPathID,
296
- width: data.gridAxesSize.width,
297
- height: data.gridAxesSize.height
298
- }]
299
- renderClipPath({
300
- defsSelection: data.defsSelection,
301
- clipPathData,
302
- })
303
- })
304
-
305
- const highlightTarget$ = fullChartParams$.pipe(
306
- takeUntil(destroy$),
307
- map(d => d.highlightTarget),
308
- distinctUntilChanged()
309
- )
310
-
311
- const graphicSelection$ = combineLatest({
312
- graphicGSelection: graphicGSelection$,
313
- visibleComputedAxesData: visibleComputedAxesData$,
314
- graphicReverseScale: graphicReverseScale$,
315
- fullChartParams: fullChartParams$,
316
- fullParams: fullParams$,
317
- }).pipe(
318
- takeUntil(destroy$),
319
- switchMap(async (d) => d),
320
- map(data => {
321
- return renderDots({
322
- graphicGSelection: data.graphicGSelection,
323
- circleGClassName,
324
- circleClassName,
325
- visibleComputedAxesData: data.visibleComputedAxesData,
326
- fullParams: data.fullParams,
327
- fullChartParams: data.fullChartParams,
328
- graphicReverseScale: data.graphicReverseScale
329
- })
330
- })
331
- )
332
-
333
- combineLatest({
334
- graphicSelection: graphicSelection$,
335
- computedData: computedData$,
336
- SeriesDataMap: SeriesDataMap$,
337
- GroupDataMap: GroupDataMap$,
338
- highlightTarget: highlightTarget$
339
- }).pipe(
340
- takeUntil(destroy$),
341
- switchMap(async (d) => d),
342
- ).subscribe(data => {
343
-
344
- data.graphicSelection
345
- .on('mouseover', (event, datum) => {
346
- // event.stopPropagation()
347
-
348
- event$.next({
349
- type: 'grid',
350
- eventName: 'mouseover',
351
- pluginName,
352
- highlightTarget: data.highlightTarget,
353
- datum,
354
- gridIndex: datum.gridIndex,
355
- series: data.SeriesDataMap.get(datum.seriesLabel)!,
356
- seriesIndex: datum.seriesIndex,
357
- seriesLabel: datum.seriesLabel,
358
- group: data.GroupDataMap.get(datum.groupLabel)!,
359
- groupIndex: datum.groupIndex,
360
- groupLabel: datum.groupLabel,
361
- event,
362
- data: data.computedData
363
- })
364
- })
365
- .on('mousemove', (event, datum) => {
366
- // event.stopPropagation()
367
-
368
- event$.next({
369
- type: 'grid',
370
- eventName: 'mousemove',
371
- pluginName,
372
- highlightTarget: data.highlightTarget,
373
- data: data.computedData,
374
- datum,
375
- gridIndex: datum.gridIndex,
376
- series: data.SeriesDataMap.get(datum.seriesLabel)!,
377
- seriesIndex: datum.seriesIndex,
378
- seriesLabel: datum.seriesLabel,
379
- group: data.GroupDataMap.get(datum.groupLabel)!,
380
- groupIndex: datum.groupIndex,
381
- groupLabel: datum.groupLabel,
382
- event
383
- })
384
- })
385
- .on('mouseout', (event, datum) => {
386
- // event.stopPropagation()
387
-
388
- event$.next({
389
- type: 'grid',
390
- eventName: 'mouseout',
391
- pluginName,
392
- highlightTarget: data.highlightTarget,
393
- datum,
394
- gridIndex: datum.gridIndex,
395
- series: data.SeriesDataMap.get(datum.seriesLabel)!,
396
- seriesIndex: datum.seriesIndex,
397
- seriesLabel: datum.seriesLabel,
398
- group: data.GroupDataMap.get(datum.groupLabel)!,
399
- groupIndex: datum.groupIndex,
400
- groupLabel: datum.groupLabel,
401
- event,
402
- data: data.computedData
403
- })
404
- })
405
- .on('click', (event, datum) => {
406
- // event.stopPropagation()
407
-
408
- event$.next({
409
- type: 'grid',
410
- eventName: 'click',
411
- pluginName,
412
- highlightTarget: data.highlightTarget,
413
- datum,
414
- gridIndex: datum.gridIndex,
415
- series: data.SeriesDataMap.get(datum.seriesLabel)!,
416
- seriesIndex: datum.seriesIndex,
417
- seriesLabel: datum.seriesLabel,
418
- group: data.GroupDataMap.get(datum.groupLabel)!,
419
- groupIndex: datum.groupIndex,
420
- groupLabel: datum.groupLabel,
421
- event,
422
- data: data.computedData
423
- })
424
- })
425
-
426
- })
427
-
428
- // const datumList$ = computedData$.pipe(
429
- // takeUntil(destroy$),
430
- // map(d => d.flat())
431
- // )
432
- // const highlight$ = highlightObservable({ datumList$, fullChartParams$, event$: store.event$ })
433
- // const highlightSubscription = gridHighlight$.subscribe()
434
- const onlyShowHighlighted$ = fullParams$.pipe(
435
- takeUntil(destroy$),
436
- map(d => d.onlyShowHighlighted),
437
- distinctUntilChanged()
438
- )
439
-
440
- combineLatest({
441
- graphicSelection: graphicSelection$,
442
- highlight: gridHighlight$.pipe(
443
- map(data => data.map(d => d.id))
444
- ),
445
- onlyShowHighlighted: onlyShowHighlighted$,
446
- // fullParams: fullParams$,
447
- fullChartParams: fullChartParams$
448
- }).pipe(
449
- takeUntil(destroy$),
450
- switchMap(async d => d)
451
- ).subscribe(data => {
452
- highlightDots({
453
- selection: data.graphicSelection,
454
- ids: data.highlight,
455
- onlyShowHighlighted: data.onlyShowHighlighted,
456
- // fullParams: data.fullParams,
457
- fullChartParams: data.fullChartParams
458
- })
459
- })
460
-
461
- return () => {
462
- destroy$.next(undefined)
463
- // highlightSubscription.unsubscribe()
464
- }
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
+ ComputedAxesDataGrid,
15
+ EventGrid,
16
+ ChartParams,
17
+ ContainerPositionScaled,
18
+ Layout,
19
+ TransformData,
20
+ ColorType
21
+ } from '../../lib/core-types'
22
+ import type { BaseDotsParams } from '../../lib/plugins-basic-types'
23
+ import { getDatumColor, getClassName, getUniID } from '../utils/orbchartsUtils'
24
+ import { gridSelectionsObservable } from '../grid/gridObservables'
25
+
26
+ // export interface BaseDotsParams {
27
+ // radius: number
28
+ // fillColorType: ColorType
29
+ // strokeColorType: ColorType
30
+ // strokeWidth: number
31
+ // // strokeWidthWhileHighlight: number
32
+ // onlyShowHighlighted: boolean
33
+ // }
34
+
35
+ interface BaseDotsContext {
36
+ selection: d3.Selection<any, unknown, any, unknown>
37
+ computedData$: Observable<ComputedDataGrid>
38
+ computedAxesData$: Observable<ComputedAxesDataGrid>
39
+ visibleComputedData$: Observable<ComputedDatumGrid[][]>
40
+ visibleComputedAxesData$: Observable<ComputedAxesDataGrid>
41
+ seriesLabels$: Observable<string[]>
42
+ SeriesDataMap$: Observable<Map<string, ComputedDatumGrid[]>>
43
+ GroupDataMap$: Observable<Map<string, ComputedDatumGrid[]>>
44
+ fullParams$: Observable<BaseDotsParams>
45
+ fullChartParams$: Observable<ChartParams>
46
+ gridAxesTransform$: Observable<TransformData>
47
+ gridGraphicTransform$: Observable<TransformData>
48
+ gridGraphicReverseScale$: Observable<[number, number][]>
49
+ gridAxesSize$: Observable<{
50
+ width: number;
51
+ height: number;
52
+ }>
53
+ gridHighlight$: Observable<ComputedDatumGrid[]>
54
+ gridContainerPosition$: Observable<ContainerPositionScaled[]>
55
+ event$: Subject<EventGrid>
56
+ }
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 = 'Dots'
68
+ // const circleGClassName = getClassName(pluginName, 'circleG')
69
+ // const circleClassName = getClassName(pluginName, 'circle')
70
+
71
+ function renderDots ({ graphicGSelection, circleGClassName, circleClassName, visibleComputedAxesData, fullParams, fullChartParams, graphicReverseScale }: {
72
+ graphicGSelection: d3.Selection<SVGGElement, any, any, any>
73
+ circleGClassName: string
74
+ circleClassName: string
75
+ visibleComputedAxesData: ComputedAxesDataGrid
76
+ fullParams: BaseDotsParams
77
+ fullChartParams: ChartParams
78
+ graphicReverseScale: [number, number][]
79
+ }) {
80
+ const createEnterDuration = (enter: d3.Selection<d3.EnterElement, ComputedDatumGrid, SVGGElement, any>) => {
81
+ const enterSize = enter.size()
82
+ const eachDuration = fullChartParams.transitionDuration / enterSize
83
+ return eachDuration
84
+ }
85
+ // enterDuration
86
+ let enterDuration = 0
87
+
88
+ graphicGSelection
89
+ .each((seriesData, seriesIndex, g) => {
90
+ d3.select(g[seriesIndex])
91
+ .selectAll<SVGGElement, ComputedDatumGrid>('g')
92
+ .data(visibleComputedAxesData[seriesIndex], d => d.id)
93
+ .join(
94
+ enter => {
95
+ // enterDuration
96
+ enterDuration = createEnterDuration(enter)
97
+
98
+ return enter
99
+ .append('g')
100
+ .classed(circleGClassName, true)
101
+ },
102
+ update => update,
103
+ exit => exit.remove()
104
+ )
105
+ .attr('transform', d => `translate(${d.axisX}, ${d.axisY})`)
106
+ .each((d, i, g) => {
107
+ const circle = d3.select(g[i])
108
+ .selectAll('circle')
109
+ .data([d])
110
+ .join(
111
+ enter => {
112
+ return enter
113
+ .append('circle')
114
+ .style('cursor', 'pointer')
115
+ .style('vector-effect', 'non-scaling-stroke')
116
+ .classed(circleClassName, true)
117
+ .attr('opacity', 0)
118
+ .transition()
119
+ .delay((_d, _i) => {
120
+ return i * enterDuration
121
+ })
122
+ .attr('opacity', 1)
123
+ },
124
+ update => {
125
+ return update
126
+ .transition()
127
+ .duration(50)
128
+ // .attr('cx', d => d.axisX)
129
+ // .attr('cy', d => d.axisY)
130
+ .attr('opacity', 1)
131
+ },
132
+ exit => exit.remove()
133
+ )
134
+ .attr('r', fullParams.radius)
135
+ .attr('fill', (d, i) => getDatumColor({ datum: d, colorType: fullParams.fillColorType, fullChartParams }))
136
+ .attr('stroke', (d, i) => getDatumColor({ datum: d, colorType: fullParams.strokeColorType, fullChartParams }))
137
+ .attr('stroke-width', fullParams.strokeWidth)
138
+ .attr('transform', `scale(${graphicReverseScale[seriesIndex][0] ?? 1}, ${graphicReverseScale[seriesIndex][1] ?? 1})`)
139
+ })
140
+ })
141
+
142
+ const graphicCircleSelection: d3.Selection<SVGRectElement, ComputedDatumGrid, SVGGElement, unknown> = graphicGSelection.selectAll(`circle.${circleClassName}`)
143
+
144
+ return graphicCircleSelection
145
+ }
146
+
147
+
148
+ function highlightDots ({ selection, ids, onlyShowHighlighted, fullChartParams }: {
149
+ selection: d3.Selection<SVGGElement, ComputedDatumGrid, any, any>
150
+ ids: string[]
151
+ onlyShowHighlighted: boolean
152
+ // fullParams: BaseDotsParams
153
+ fullChartParams: ChartParams
154
+ }) {
155
+ selection.interrupt('highlight')
156
+ if (!ids.length) {
157
+ // remove highlight
158
+ selection
159
+ .transition('highlight')
160
+ .duration(200)
161
+ .style('opacity', onlyShowHighlighted === true ? 0 : 1)
162
+ // selection
163
+ // .attr('stroke-width', fullParams.strokeWidth)
164
+
165
+ return
166
+ }
167
+
168
+ selection
169
+ .each((d, i, n) => {
170
+ if (ids.includes(d.id)) {
171
+ const dot = d3.select(n[i])
172
+ dot
173
+ .style('opacity', 1)
174
+ .transition('highlight')
175
+ .duration(200)
176
+ // dot
177
+ // .attr('stroke-width', fullParams.strokeWidthWhileHighlight)
178
+ } else {
179
+ const dot = d3.select(n[i])
180
+ dot
181
+ .style('opacity', onlyShowHighlighted === true ? 0 : fullChartParams.styles.unhighlightedOpacity)
182
+ .transition('highlight')
183
+ .duration(200)
184
+ // dot
185
+ // .attr('stroke-width', fullParams.strokeWidth)
186
+
187
+ }
188
+ })
189
+ }
190
+
191
+ function renderClipPath ({ defsSelection, clipPathData }: {
192
+ defsSelection: d3.Selection<SVGDefsElement, any, any, any>
193
+ clipPathData: ClipPathDatum[]
194
+ }) {
195
+ const clipPath = defsSelection
196
+ .selectAll<SVGClipPathElement, Layout>('clipPath')
197
+ .data(clipPathData)
198
+ .join(
199
+ enter => {
200
+ return enter
201
+ .append('clipPath')
202
+ },
203
+ update => update,
204
+ exit => exit.remove()
205
+ )
206
+ .attr('id', d => d.id)
207
+ .each((d, i, g) => {
208
+ const rect = d3.select(g[i])
209
+ .selectAll<SVGRectElement, typeof d>('rect')
210
+ .data([d])
211
+ .join('rect')
212
+ .attr('x', 0)
213
+ .attr('y', 0)
214
+ .attr('width', _d => _d.width)
215
+ .attr('height', _d => _d.height)
216
+ })
217
+
218
+ }
219
+
220
+
221
+
222
+ export const createBaseDots: BasePluginFn<BaseDotsContext> = (pluginName: string, {
223
+ selection,
224
+ computedData$,
225
+ computedAxesData$,
226
+ visibleComputedData$,
227
+ visibleComputedAxesData$,
228
+ seriesLabels$,
229
+ SeriesDataMap$,
230
+ GroupDataMap$,
231
+ fullParams$,
232
+ fullChartParams$,
233
+ gridAxesTransform$,
234
+ gridGraphicTransform$,
235
+ gridGraphicReverseScale$,
236
+ gridAxesSize$,
237
+ gridHighlight$,
238
+ gridContainerPosition$,
239
+ event$
240
+ }) => {
241
+
242
+ const destroy$ = new Subject()
243
+
244
+ const clipPathID = getUniID(pluginName, 'clipPath-box')
245
+ const circleGClassName = getClassName(pluginName, 'circleG')
246
+ const circleClassName = getClassName(pluginName, 'circle')
247
+
248
+ // const axisSelection: d3.Selection<SVGGElement, any, any, any> = selection
249
+ // .append('g')
250
+ // .attr('clip-path', `url(#${clipPathID})`)
251
+ // const defsSelection: d3.Selection<SVGDefsElement, any, any, any> = axisSelection.append('defs')
252
+ // const dataAreaSelection: d3.Selection<SVGGElement, any, any, any> = axisSelection.append('g')
253
+ // const graphicSelection$: Subject<d3.Selection<SVGGElement, ComputedDatumGrid, any, any>> = new Subject()
254
+
255
+ const {
256
+ seriesSelection$,
257
+ axesSelection$,
258
+ defsSelection$,
259
+ graphicGSelection$
260
+ } = gridSelectionsObservable({
261
+ selection,
262
+ pluginName,
263
+ clipPathID,
264
+ seriesLabels$,
265
+ gridContainerPosition$,
266
+ gridAxesTransform$,
267
+ gridGraphicTransform$
268
+ })
269
+
270
+ const graphicReverseScale$: Observable<[number, number][]> = combineLatest({
271
+ // gridGraphicTransform: gridGraphicTransform$,
272
+ // gridContainerPosition: gridContainerPosition$,
273
+ // gridAxesTransform: gridAxesTransform$
274
+ computedData: computedData$,
275
+ gridGraphicReverseScale: gridGraphicReverseScale$
276
+ }).pipe(
277
+ takeUntil(destroy$),
278
+ switchMap(async data => data),
279
+ map(data => {
280
+ return data.computedData.map((series, seriesIndex) => {
281
+ return data.gridGraphicReverseScale[seriesIndex]
282
+ })
283
+ })
284
+ )
285
+
286
+ const clipPathSubscription = combineLatest({
287
+ defsSelection: defsSelection$,
288
+ gridAxesSize: gridAxesSize$,
289
+ }).pipe(
290
+ takeUntil(destroy$),
291
+ switchMap(async (d) => d),
292
+ ).subscribe(data => {
293
+ // 外層的遮罩
294
+ const clipPathData = [{
295
+ id: clipPathID,
296
+ width: data.gridAxesSize.width,
297
+ height: data.gridAxesSize.height
298
+ }]
299
+ renderClipPath({
300
+ defsSelection: data.defsSelection,
301
+ clipPathData,
302
+ })
303
+ })
304
+
305
+ const highlightTarget$ = fullChartParams$.pipe(
306
+ takeUntil(destroy$),
307
+ map(d => d.highlightTarget),
308
+ distinctUntilChanged()
309
+ )
310
+
311
+ const graphicSelection$ = combineLatest({
312
+ graphicGSelection: graphicGSelection$,
313
+ visibleComputedAxesData: visibleComputedAxesData$,
314
+ graphicReverseScale: graphicReverseScale$,
315
+ fullChartParams: fullChartParams$,
316
+ fullParams: fullParams$,
317
+ }).pipe(
318
+ takeUntil(destroy$),
319
+ switchMap(async (d) => d),
320
+ map(data => {
321
+ return renderDots({
322
+ graphicGSelection: data.graphicGSelection,
323
+ circleGClassName,
324
+ circleClassName,
325
+ visibleComputedAxesData: data.visibleComputedAxesData,
326
+ fullParams: data.fullParams,
327
+ fullChartParams: data.fullChartParams,
328
+ graphicReverseScale: data.graphicReverseScale
329
+ })
330
+ })
331
+ )
332
+
333
+ combineLatest({
334
+ graphicSelection: graphicSelection$,
335
+ computedData: computedData$,
336
+ SeriesDataMap: SeriesDataMap$,
337
+ GroupDataMap: GroupDataMap$,
338
+ highlightTarget: highlightTarget$
339
+ }).pipe(
340
+ takeUntil(destroy$),
341
+ switchMap(async (d) => d),
342
+ ).subscribe(data => {
343
+
344
+ data.graphicSelection
345
+ .on('mouseover', (event, datum) => {
346
+ // event.stopPropagation()
347
+
348
+ event$.next({
349
+ type: 'grid',
350
+ eventName: 'mouseover',
351
+ pluginName,
352
+ highlightTarget: data.highlightTarget,
353
+ datum,
354
+ gridIndex: datum.gridIndex,
355
+ series: data.SeriesDataMap.get(datum.seriesLabel)!,
356
+ seriesIndex: datum.seriesIndex,
357
+ seriesLabel: datum.seriesLabel,
358
+ group: data.GroupDataMap.get(datum.groupLabel)!,
359
+ groupIndex: datum.groupIndex,
360
+ groupLabel: datum.groupLabel,
361
+ event,
362
+ data: data.computedData
363
+ })
364
+ })
365
+ .on('mousemove', (event, datum) => {
366
+ // event.stopPropagation()
367
+
368
+ event$.next({
369
+ type: 'grid',
370
+ eventName: 'mousemove',
371
+ pluginName,
372
+ highlightTarget: data.highlightTarget,
373
+ data: data.computedData,
374
+ datum,
375
+ gridIndex: datum.gridIndex,
376
+ series: data.SeriesDataMap.get(datum.seriesLabel)!,
377
+ seriesIndex: datum.seriesIndex,
378
+ seriesLabel: datum.seriesLabel,
379
+ group: data.GroupDataMap.get(datum.groupLabel)!,
380
+ groupIndex: datum.groupIndex,
381
+ groupLabel: datum.groupLabel,
382
+ event
383
+ })
384
+ })
385
+ .on('mouseout', (event, datum) => {
386
+ // event.stopPropagation()
387
+
388
+ event$.next({
389
+ type: 'grid',
390
+ eventName: 'mouseout',
391
+ pluginName,
392
+ highlightTarget: data.highlightTarget,
393
+ datum,
394
+ gridIndex: datum.gridIndex,
395
+ series: data.SeriesDataMap.get(datum.seriesLabel)!,
396
+ seriesIndex: datum.seriesIndex,
397
+ seriesLabel: datum.seriesLabel,
398
+ group: data.GroupDataMap.get(datum.groupLabel)!,
399
+ groupIndex: datum.groupIndex,
400
+ groupLabel: datum.groupLabel,
401
+ event,
402
+ data: data.computedData
403
+ })
404
+ })
405
+ .on('click', (event, datum) => {
406
+ // event.stopPropagation()
407
+
408
+ event$.next({
409
+ type: 'grid',
410
+ eventName: 'click',
411
+ pluginName,
412
+ highlightTarget: data.highlightTarget,
413
+ datum,
414
+ gridIndex: datum.gridIndex,
415
+ series: data.SeriesDataMap.get(datum.seriesLabel)!,
416
+ seriesIndex: datum.seriesIndex,
417
+ seriesLabel: datum.seriesLabel,
418
+ group: data.GroupDataMap.get(datum.groupLabel)!,
419
+ groupIndex: datum.groupIndex,
420
+ groupLabel: datum.groupLabel,
421
+ event,
422
+ data: data.computedData
423
+ })
424
+ })
425
+
426
+ })
427
+
428
+ // const datumList$ = computedData$.pipe(
429
+ // takeUntil(destroy$),
430
+ // map(d => d.flat())
431
+ // )
432
+ // const highlight$ = highlightObservable({ datumList$, fullChartParams$, event$: store.event$ })
433
+ // const highlightSubscription = gridHighlight$.subscribe()
434
+ const onlyShowHighlighted$ = fullParams$.pipe(
435
+ takeUntil(destroy$),
436
+ map(d => d.onlyShowHighlighted),
437
+ distinctUntilChanged()
438
+ )
439
+
440
+ combineLatest({
441
+ graphicSelection: graphicSelection$,
442
+ highlight: gridHighlight$.pipe(
443
+ map(data => data.map(d => d.id))
444
+ ),
445
+ onlyShowHighlighted: onlyShowHighlighted$,
446
+ // fullParams: fullParams$,
447
+ fullChartParams: fullChartParams$
448
+ }).pipe(
449
+ takeUntil(destroy$),
450
+ switchMap(async d => d)
451
+ ).subscribe(data => {
452
+ highlightDots({
453
+ selection: data.graphicSelection,
454
+ ids: data.highlight,
455
+ onlyShowHighlighted: data.onlyShowHighlighted,
456
+ // fullParams: data.fullParams,
457
+ fullChartParams: data.fullChartParams
458
+ })
459
+ })
460
+
461
+ return () => {
462
+ destroy$.next(undefined)
463
+ // highlightSubscription.unsubscribe()
464
+ }
465
465
  }