@orbcharts/plugins-basic 3.0.0-alpha.24

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 (52) hide show
  1. package/LICENSE +201 -0
  2. package/package.json +41 -0
  3. package/src/grid/defaults.ts +95 -0
  4. package/src/grid/gridObservables.ts +114 -0
  5. package/src/grid/index.ts +12 -0
  6. package/src/grid/plugins/BarStack.ts +661 -0
  7. package/src/grid/plugins/Bars.ts +604 -0
  8. package/src/grid/plugins/BarsTriangle.ts +594 -0
  9. package/src/grid/plugins/Dots.ts +427 -0
  10. package/src/grid/plugins/GroupArea.ts +636 -0
  11. package/src/grid/plugins/GroupAxis.ts +363 -0
  12. package/src/grid/plugins/Lines.ts +528 -0
  13. package/src/grid/plugins/Ranking.ts +0 -0
  14. package/src/grid/plugins/RankingAxis.ts +0 -0
  15. package/src/grid/plugins/ScalingArea.ts +168 -0
  16. package/src/grid/plugins/ValueAxis.ts +356 -0
  17. package/src/grid/plugins/ValueStackAxis.ts +372 -0
  18. package/src/grid/types.ts +102 -0
  19. package/src/index.ts +7 -0
  20. package/src/multiGrid/index.ts +0 -0
  21. package/src/multiGrid/plugins/Diverging.ts +0 -0
  22. package/src/multiGrid/plugins/DivergingAxes.ts +0 -0
  23. package/src/multiGrid/plugins/TwoScaleAxes.ts +0 -0
  24. package/src/multiGrid/plugins/TwoScales.ts +0 -0
  25. package/src/multiValue/index.ts +0 -0
  26. package/src/multiValue/plugins/Scatter.ts +0 -0
  27. package/src/multiValue/plugins/ScatterAxes.ts +0 -0
  28. package/src/noneData/defaults.ts +47 -0
  29. package/src/noneData/index.ts +4 -0
  30. package/src/noneData/plugins/Container.ts +11 -0
  31. package/src/noneData/plugins/Tooltip.ts +305 -0
  32. package/src/noneData/types.ts +26 -0
  33. package/src/relationship/index.ts +0 -0
  34. package/src/relationship/plugins/Relationship.ts +0 -0
  35. package/src/series/defaults.ts +82 -0
  36. package/src/series/index.ts +6 -0
  37. package/src/series/plugins/Bubbles.ts +553 -0
  38. package/src/series/plugins/Pie.ts +603 -0
  39. package/src/series/plugins/PieEventTexts.ts +194 -0
  40. package/src/series/plugins/PieLabels.ts +289 -0
  41. package/src/series/plugins/Waffle.ts +0 -0
  42. package/src/series/seriesUtils.ts +51 -0
  43. package/src/series/types.ts +53 -0
  44. package/src/tree/index.ts +0 -0
  45. package/src/tree/plugins/TreeMap.ts +0 -0
  46. package/src/utils/commonUtils.ts +22 -0
  47. package/src/utils/d3Graphics.ts +125 -0
  48. package/src/utils/d3Utils.ts +73 -0
  49. package/src/utils/observables.ts +14 -0
  50. package/src/utils/orbchartsUtils.ts +70 -0
  51. package/tsconfig.json +14 -0
  52. package/vite.config.js +45 -0
@@ -0,0 +1,427 @@
1
+ import * as d3 from 'd3'
2
+ import {
3
+ Observable,
4
+ combineLatest,
5
+ switchMap,
6
+ distinctUntilChanged,
7
+ filter,
8
+ first,
9
+ map,
10
+ takeUntil,
11
+ Subject } from 'rxjs'
12
+ import {
13
+ defineGridPlugin } from '@orbcharts/core'
14
+ import type {
15
+ ChartParams,
16
+ ComputedDatumGrid,
17
+ Layout } from '@orbcharts/core'
18
+ import type { DotsPluginParams } from '../types'
19
+ import { DEFAULT_DOTS_PLUGIN_PARAMS } from '../defaults'
20
+ import { getDatumColor, getClassName, getUniID } from '../../utils/orbchartsUtils'
21
+
22
+ type ClipPathDatum = {
23
+ id: string;
24
+ // x: number;
25
+ // y: number;
26
+ width: number;
27
+ height: number;
28
+ }
29
+
30
+ const pluginName = 'Dots'
31
+ const gClassName = getClassName(pluginName, 'g')
32
+ const circleClassName = getClassName(pluginName, 'circle')
33
+
34
+ function renderDots ({ selection, data, fullParams, fullChartParams, graphicOppositeScale }: {
35
+ selection: d3.Selection<SVGGElement, any, any, any>
36
+ data: ComputedDatumGrid[]
37
+ fullParams: DotsPluginParams
38
+ fullChartParams: ChartParams
39
+ graphicOppositeScale: [number, number]
40
+ }) {
41
+ const createEnterDuration = (enter: d3.Selection<d3.EnterElement, ComputedDatumGrid, SVGGElement, any>) => {
42
+ const enterSize = enter.size()
43
+ const eachDuration = fullChartParams.transitionDuration / enterSize
44
+ return eachDuration
45
+ }
46
+ // enterDuration
47
+ let enterDuration = 0
48
+
49
+ const dots = selection
50
+ .selectAll<SVGGElement, ComputedDatumGrid>('g')
51
+ .data(data, d => d.id)
52
+ .join(
53
+ enter => {
54
+ // enterDuration
55
+ enterDuration = createEnterDuration(enter)
56
+
57
+ return enter
58
+ .append('g')
59
+ .classed(gClassName, true)
60
+ },
61
+ update => update,
62
+ exit => exit.remove()
63
+ )
64
+ .attr('transform', d => `translate(${d.axisX}, ${d.axisY})`)
65
+ .each((d, i, g) => {
66
+ const circle = d3.select(g[i])
67
+ .selectAll('circle')
68
+ .data([d])
69
+ .join(
70
+ enter => {
71
+ return enter
72
+ .append('circle')
73
+ .style('cursor', 'pointer')
74
+ .style('vector-effect', 'non-scaling-stroke')
75
+ .classed(circleClassName, true)
76
+ .attr('opacity', 0)
77
+ .transition()
78
+ .delay((_d, _i) => {
79
+ return i * enterDuration
80
+ })
81
+ .attr('opacity', 1)
82
+ },
83
+ update => {
84
+ return update
85
+ .transition()
86
+ .duration(50)
87
+ // .attr('cx', d => d.axisX)
88
+ // .attr('cy', d => d.axisY)
89
+ .attr('opacity', 1)
90
+ },
91
+ exit => exit.remove()
92
+ )
93
+ .attr('r', fullParams.radius)
94
+ .attr('fill', (d, i) => getDatumColor({ datum: d, colorType: fullParams.fillColorType, fullChartParams }))
95
+ .attr('stroke', (d, i) => getDatumColor({ datum: d, colorType: fullParams.strokeColorType, fullChartParams }))
96
+ .attr('stroke-width', fullParams.strokeWidth)
97
+ .attr('transform', `scale(${graphicOppositeScale[0]}, ${graphicOppositeScale[1]})`)
98
+ })
99
+
100
+ return dots
101
+ }
102
+
103
+
104
+ function highlightDots ({ selection, ids, onlyShowHighlighted, fullChartParams }: {
105
+ selection: d3.Selection<SVGGElement, ComputedDatumGrid, any, any>
106
+ ids: string[]
107
+ onlyShowHighlighted: boolean
108
+ fullChartParams: ChartParams
109
+ }) {
110
+ selection.interrupt('highlight')
111
+ if (!ids.length) {
112
+ // remove highlight
113
+ selection
114
+ .transition('highlight')
115
+ .duration(200)
116
+ .style('opacity', onlyShowHighlighted === true ? 0 : 1)
117
+ return
118
+ }
119
+
120
+ selection
121
+ .each((d, i, n) => {
122
+ if (ids.includes(d.id)) {
123
+ d3.select(n[i])
124
+ .style('opacity', 1)
125
+ .transition('highlight')
126
+ .duration(200)
127
+ } else {
128
+ d3.select(n[i])
129
+ .style('opacity', onlyShowHighlighted === true ? 0 : fullChartParams.styles.unhighlightedOpacity)
130
+ .transition('highlight')
131
+ .duration(200)
132
+ }
133
+ })
134
+ }
135
+
136
+ function renderClipPath ({ defsSelection, clipPathData }: {
137
+ defsSelection: d3.Selection<SVGDefsElement, any, any, any>
138
+ clipPathData: ClipPathDatum[]
139
+ }) {
140
+ const clipPath = defsSelection
141
+ .selectAll<SVGClipPathElement, Layout>('clipPath')
142
+ .data(clipPathData)
143
+ .join(
144
+ enter => {
145
+ return enter
146
+ .append('clipPath')
147
+ },
148
+ update => update,
149
+ exit => exit.remove()
150
+ )
151
+ .attr('id', d => d.id)
152
+ .each((d, i, g) => {
153
+ const rect = d3.select(g[i])
154
+ .selectAll<SVGRectElement, typeof d>('rect')
155
+ .data([d])
156
+ .join('rect')
157
+ .attr('x', 0)
158
+ .attr('y', 0)
159
+ .attr('width', _d => _d.width)
160
+ .attr('height', _d => _d.height)
161
+ })
162
+
163
+ }
164
+
165
+ export const Dots = defineGridPlugin(pluginName, DEFAULT_DOTS_PLUGIN_PARAMS)(({ selection, name, subject, observer }) => {
166
+ // const axisGUpdate = selection
167
+ // .selectAll('g')
168
+ // .data()
169
+ const destroy$ = new Subject()
170
+
171
+ const clipPathID = getUniID(pluginName, 'clipPath-box')
172
+
173
+ // const rectSelection: d3.Selection<SVGRectElement, any, any, any> = selection
174
+ // .append('rect')
175
+ // .attr('pointer-events', 'none')
176
+ const axisSelection: d3.Selection<SVGGElement, any, any, any> = selection
177
+ .append('g')
178
+ .attr('clip-path', `url(#${clipPathID})`)
179
+ const defsSelection: d3.Selection<SVGDefsElement, any, any, any> = axisSelection.append('defs')
180
+ const dataAreaSelection: d3.Selection<SVGGElement, any, any, any> = axisSelection.append('g')
181
+ const graphicSelection$: Subject<d3.Selection<SVGGElement, ComputedDatumGrid, any, any>> = new Subject()
182
+ // const dotSelection$: Subject<d3.Selection<SVGCircleElement, ComputedDatumGrid, SVGGElement, any>> = new Subject()
183
+
184
+ observer.gridAxesTransform$
185
+ .pipe(
186
+ takeUntil(destroy$),
187
+ map(d => d.value),
188
+ distinctUntilChanged()
189
+ ).subscribe(d => {
190
+ axisSelection
191
+ .style('transform', d)
192
+ })
193
+
194
+ observer.gridGraphicTransform$
195
+ .pipe(
196
+ takeUntil(destroy$),
197
+ switchMap(async d => d.value),
198
+ distinctUntilChanged()
199
+ ).subscribe(d => {
200
+ dataAreaSelection
201
+ .transition()
202
+ .duration(50)
203
+ .style('transform', d)
204
+ })
205
+
206
+ const graphicOppositeScale$: Observable<[number, number]> = observer.gridGraphicTransform$.pipe(
207
+ takeUntil(destroy$),
208
+ map(d => [1 / d.scale[0], 1 / d.scale[1]])
209
+ )
210
+
211
+ // const axisSize$ = gridAxisSizeObservable({
212
+ // dataFormatter$,
213
+ // observer.layout$
214
+ // })
215
+
216
+ // combineLatest({
217
+ // axisSized: axisSize$,
218
+ // computedLayout: observer.layout$
219
+ // }).pipe(
220
+ // takeUntil(destroy$),
221
+ // // 轉換後會退訂前一個未完成的訂閱事件,因此可以取到「同時間」最後一次的訂閱事件
222
+ // switchMap(async (d) => d),
223
+ // ).subscribe(d => {
224
+ // rectSelection
225
+ // .style('transform', d.computedLayout.content.axesTransform)
226
+ // .attr('opacity', 0)
227
+ // .attr('width', d.axisSized.width)
228
+ // .attr('height', d.axisSized.height)
229
+ // // .transition()
230
+ // // .attr('opacity', 1)
231
+ // })
232
+ // selection.on('mouseover', (event, datum) => {
233
+
234
+ // console.log('selection mouseover', event, datum)
235
+ // })
236
+
237
+ const clipPathSubscription = observer.gridAxesSize$.pipe(
238
+ takeUntil(destroy$),
239
+ // 轉換後會退訂前一個未完成的訂閱事件,因此可以取到「同時間」最後一次的訂閱事件
240
+ switchMap(async (d) => d),
241
+ ).subscribe(data => {
242
+ // 外層的遮罩
243
+ const clipPathData = [{
244
+ id: clipPathID,
245
+ width: data.width,
246
+ height: data.height
247
+ }]
248
+ renderClipPath({
249
+ defsSelection,
250
+ clipPathData,
251
+ })
252
+ })
253
+
254
+ // const visibleComputedData$ = observer.computedData$.pipe(
255
+ // map(computedData => {
256
+ // return computedData
257
+ // .map(d => {
258
+ // return d.filter(_d => _d.visible == true)
259
+ // })
260
+ // })
261
+ // )
262
+
263
+ // const SeriesDataMap$ = visibleComputedData$.pipe(
264
+ // map(d => makeGridSeriesDataMap(d))
265
+ // )
266
+
267
+ // const GroupDataMap$ = visibleComputedData$.pipe(
268
+ // map(d => makeGridGroupDataMap(d))
269
+ // )
270
+
271
+ // const DataMap$ = computedData$.pipe(
272
+ // map(d => {
273
+ // const DataMap: Map<string, ComputedDatumGrid> = new Map()
274
+ // d.flat().forEach(_d => DataMap.set(_d.id, _d))
275
+ // return DataMap
276
+ // })
277
+ // )
278
+
279
+ const highlightTarget$ = observer.fullChartParams$.pipe(
280
+ takeUntil(destroy$),
281
+ map(d => d.highlightTarget),
282
+ distinctUntilChanged()
283
+ )
284
+
285
+ combineLatest({
286
+ computedData: observer.computedData$,
287
+ visibleComputedData: observer.visibleComputedData$,
288
+ SeriesDataMap: observer.SeriesDataMap$,
289
+ GroupDataMap: observer.GroupDataMap$,
290
+ graphicOppositeScale: graphicOppositeScale$,
291
+ fullChartParams: observer.fullChartParams$,
292
+ fullParams: observer.fullParams$,
293
+ highlightTarget: highlightTarget$
294
+ }).pipe(
295
+ takeUntil(destroy$),
296
+ // 轉換後會退訂前一個未完成的訂閱事件,因此可以取到「同時間」最後一次的訂閱事件
297
+ switchMap(async (d) => d),
298
+ ).subscribe(data => {
299
+
300
+ const graphicSelection = renderDots({
301
+ selection: dataAreaSelection,
302
+ data: data.visibleComputedData.flat(),
303
+ fullParams: data.fullParams,
304
+ fullChartParams: data.fullChartParams,
305
+ graphicOppositeScale: data.graphicOppositeScale
306
+ })
307
+
308
+ graphicSelection
309
+ .on('mouseover', (event, datum) => {
310
+ event.stopPropagation()
311
+
312
+ subject.event$.next({
313
+ type: 'grid',
314
+ eventName: 'mouseover',
315
+ pluginName: name,
316
+ highlightTarget: data.highlightTarget,
317
+ datum,
318
+ series: data.SeriesDataMap.get(datum.seriesLabel)!,
319
+ seriesIndex: datum.seriesIndex,
320
+ seriesLabel: datum.seriesLabel,
321
+ groups: data.GroupDataMap.get(datum.groupLabel)!,
322
+ groupIndex: datum.groupIndex,
323
+ groupLabel: datum.groupLabel,
324
+ event,
325
+ data: data.computedData
326
+ })
327
+ })
328
+ .on('mousemove', (event, datum) => {
329
+ event.stopPropagation()
330
+
331
+ subject.event$.next({
332
+ type: 'grid',
333
+ eventName: 'mousemove',
334
+ pluginName: name,
335
+ highlightTarget: data.highlightTarget,
336
+ data: data.computedData,
337
+ datum,
338
+ series: data.SeriesDataMap.get(datum.seriesLabel)!,
339
+ seriesIndex: datum.seriesIndex,
340
+ seriesLabel: datum.seriesLabel,
341
+ groups: data.GroupDataMap.get(datum.groupLabel)!,
342
+ groupIndex: datum.groupIndex,
343
+ groupLabel: datum.groupLabel,
344
+ event
345
+ })
346
+ })
347
+ .on('mouseout', (event, datum) => {
348
+ event.stopPropagation()
349
+
350
+ subject.event$.next({
351
+ type: 'grid',
352
+ eventName: 'mouseout',
353
+ pluginName: name,
354
+ highlightTarget: data.highlightTarget,
355
+ datum,
356
+ series: data.SeriesDataMap.get(datum.seriesLabel)!,
357
+ seriesIndex: datum.seriesIndex,
358
+ seriesLabel: datum.seriesLabel,
359
+ groups: data.GroupDataMap.get(datum.groupLabel)!,
360
+ groupIndex: datum.groupIndex,
361
+ groupLabel: datum.groupLabel,
362
+ event,
363
+ data: data.computedData
364
+ })
365
+ })
366
+ .on('click', (event, datum) => {
367
+ event.stopPropagation()
368
+
369
+ subject.event$.next({
370
+ type: 'grid',
371
+ eventName: 'click',
372
+ pluginName: name,
373
+ highlightTarget: data.highlightTarget,
374
+ datum,
375
+ series: data.SeriesDataMap.get(datum.seriesLabel)!,
376
+ seriesIndex: datum.seriesIndex,
377
+ seriesLabel: datum.seriesLabel,
378
+ groups: data.GroupDataMap.get(datum.groupLabel)!,
379
+ groupIndex: datum.groupIndex,
380
+ groupLabel: datum.groupLabel,
381
+ event,
382
+ data: data.computedData
383
+ })
384
+ })
385
+
386
+ graphicSelection$.next(graphicSelection)
387
+
388
+ })
389
+
390
+ // const datumList$ = observer.computedData$.pipe(
391
+ // takeUntil(destroy$),
392
+ // map(d => d.flat())
393
+ // )
394
+ // const highlight$ = highlightObservable({ datumList$, fullChartParams$, event$: store.event$ })
395
+ const highlightSubscription = observer.gridHighlight$.subscribe()
396
+ const onlyShowHighlighted$ = observer.fullParams$.pipe(
397
+ takeUntil(destroy$),
398
+ map(d => d.onlyShowHighlighted),
399
+ distinctUntilChanged()
400
+ )
401
+
402
+ observer.fullChartParams$.pipe(
403
+ takeUntil(destroy$),
404
+ switchMap(d => combineLatest({
405
+ graphicSelection: graphicSelection$,
406
+ highlight: observer.gridHighlight$,
407
+ onlyShowHighlighted: onlyShowHighlighted$,
408
+ fullChartParams: observer.fullChartParams$
409
+ }).pipe(
410
+ takeUntil(destroy$),
411
+ switchMap(async d => d)
412
+ ))
413
+ ).subscribe(data => {
414
+ highlightDots({
415
+ selection: data.graphicSelection,
416
+ ids: data.highlight,
417
+ onlyShowHighlighted: data.onlyShowHighlighted,
418
+ fullChartParams: data.fullChartParams
419
+ })
420
+ })
421
+
422
+
423
+ return () => {
424
+ highlightSubscription.unsubscribe()
425
+ destroy$.next(undefined)
426
+ }
427
+ })