@orbcharts/plugins-basic 3.0.0-alpha.24

Sign up to get free protection for your applications and to get access to all the features.
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,194 @@
1
+ import * as d3 from 'd3'
2
+ import {
3
+ combineLatest,
4
+ switchMap,
5
+ first,
6
+ takeUntil,
7
+ map,
8
+ distinctUntilChanged,
9
+ Subject } from 'rxjs'
10
+ import type { Subscription } from 'rxjs'
11
+ import {
12
+ defineSeriesPlugin} from '@orbcharts/core'
13
+ import type {
14
+ EventName,
15
+ EventSeries } from '@orbcharts/core'
16
+ import { DEFAULT_PIE_EVENT_TEXTS_PARAMS } from '../defaults'
17
+ import { getD3TransitionEase } from '../../utils/d3Utils'
18
+ import { getClassName } from '../../utils/orbchartsUtils'
19
+
20
+ type TextDatum = {
21
+ text: string
22
+ attr: { [key:string]: any }
23
+ style: { [key:string]: any }
24
+ }
25
+
26
+ const pluginName = 'PieEventTexts'
27
+ const textClassName = getClassName(pluginName, 'text')
28
+
29
+ function renderText (
30
+ selection: d3.Selection<SVGGElement, unknown, any, any>,
31
+ data: Array<TextDatum>
32
+ ): d3.Selection<SVGTextElement, TextDatum, SVGGElement, unknown> {
33
+ const textUpdate = selection
34
+ .selectAll<SVGTextElement, TextDatum>(`text.${textClassName}`)
35
+ .data(data)
36
+ const textEnter = textUpdate.enter()
37
+ .append('text')
38
+ .classed(textClassName, true)
39
+ const text = textUpdate.merge(textEnter)
40
+ text
41
+ .each((d, i, g) => {
42
+ const t = d3.select(g[i])
43
+ .text(d.text)
44
+ Object.keys(d.attr)
45
+ .forEach(key => {
46
+ t.attr(key, d.attr[key])
47
+ })
48
+ Object.keys(d.style)
49
+ .forEach(key => {
50
+ t.style(key, d.style[key])
51
+ })
52
+ })
53
+
54
+ textUpdate.exit().remove()
55
+
56
+ return text
57
+ }
58
+
59
+ function makeTextData ({ eventData, eventName, t, eventFn, textAttrs, textStyles }: {
60
+ eventData: EventSeries,
61
+ eventName: EventName,
62
+ t: number,
63
+ eventFn: (d: EventSeries, eventName: EventName, t: number) => string[]
64
+ textAttrs: Array<{ [key:string]: string | number }>
65
+ textStyles: Array<{ [key:string]: string | number }>
66
+ }): TextDatum[] {
67
+ const callbackText = eventFn(eventData, eventName, t)
68
+ return callbackText.map((d, i) => {
69
+ return {
70
+ text: d,
71
+ attr: textAttrs[i],
72
+ style: textStyles[i]
73
+ }
74
+ })
75
+ }
76
+
77
+
78
+
79
+ export const PieEventTexts = defineSeriesPlugin(pluginName, DEFAULT_PIE_EVENT_TEXTS_PARAMS)(({ selection, name, observer, subject }) => {
80
+
81
+ const destroy$ = new Subject()
82
+
83
+ const graphicSelection: d3.Selection<SVGGElement, any, any, any> = selection.append('g')
84
+ let centerTextSelection: d3.Selection<SVGTextElement, TextDatum, SVGGElement, unknown> | undefined
85
+ let storeEventSubscription: Subscription | undefined
86
+
87
+ observer.layout$
88
+ .pipe(
89
+ first()
90
+ )
91
+ .subscribe(size => {
92
+ selection
93
+ .attr('transform', `translate(${size.width / 2}, ${size.height / 2})`)
94
+ observer.layout$
95
+ .pipe(
96
+ takeUntil(destroy$)
97
+ )
98
+ .subscribe(size => {
99
+ selection
100
+ .transition()
101
+ .attr('transform', `translate(${size.width / 2}, ${size.height / 2})`)
102
+ })
103
+ })
104
+
105
+
106
+ const highlightTarget$ = observer.fullChartParams$.pipe(
107
+ takeUntil(destroy$),
108
+ map(d => d.highlightTarget),
109
+ distinctUntilChanged()
110
+ )
111
+
112
+ combineLatest({
113
+ computedData: observer.computedData$,
114
+ fullParams: observer.fullParams$,
115
+ fullChartParams: observer.fullChartParams$,
116
+ highlightTarget: highlightTarget$
117
+ }).pipe(
118
+ takeUntil(destroy$),
119
+ // 轉換後會退訂前一個未完成的訂閱事件,因此可以取到「同時間」最後一次的訂閱事件
120
+ switchMap(async (d) => d),
121
+ ).subscribe(data => {
122
+
123
+ graphicSelection
124
+ .transition()
125
+ .duration(data.fullChartParams.transitionDuration!)
126
+ .ease(getD3TransitionEase(data.fullChartParams.transitionEase!))
127
+ .tween('move', (event, datum) => {
128
+ return (t) => {
129
+ const renderData = makeTextData({
130
+ eventData: {
131
+ type: 'series',
132
+ pluginName: name,
133
+ eventName: 'transitionMove',
134
+ event,
135
+ highlightTarget: data.highlightTarget,
136
+ data: data.computedData,
137
+ series: [],
138
+ seriesIndex: -1,
139
+ seriesLabel: '',
140
+ datum: null
141
+ },
142
+ eventName: 'transitionMove',
143
+ t,
144
+ eventFn: data.fullParams.eventFn!,
145
+ textAttrs: data.fullParams.textAttrs!,
146
+ textStyles: data.fullParams.textStyles!
147
+ })
148
+ centerTextSelection = renderText(graphicSelection, renderData)
149
+ }
150
+ })
151
+ .on('end', (event, datum) => {
152
+ const renderData = makeTextData({
153
+ eventData: {
154
+ type: 'series',
155
+ pluginName: name,
156
+ eventName: 'transitionEnd',
157
+ event,
158
+ highlightTarget: data.highlightTarget,
159
+ data: data.computedData,
160
+ series: [],
161
+ seriesIndex: -1,
162
+ seriesLabel: '',
163
+ datum: null
164
+ },
165
+ eventName: 'transitionMove',
166
+ t: 1,
167
+ eventFn: data.fullParams.eventFn!,
168
+ textAttrs: data.fullParams.textAttrs!,
169
+ textStyles: data.fullParams.textStyles!
170
+ })
171
+ centerTextSelection = renderText(graphicSelection, renderData)
172
+
173
+ if (storeEventSubscription) {
174
+ storeEventSubscription.unsubscribe()
175
+ }
176
+ storeEventSubscription = subject.event$
177
+ .subscribe(eventData => {
178
+ const renderData = makeTextData({
179
+ eventData,
180
+ eventName: eventData.eventName,
181
+ t: 1,
182
+ eventFn: data.fullParams.eventFn!,
183
+ textAttrs: data.fullParams.textAttrs!,
184
+ textStyles: data.fullParams.textStyles!
185
+ })
186
+ centerTextSelection = renderText(graphicSelection, renderData)
187
+ })
188
+ })
189
+ })
190
+
191
+ return () => {
192
+ destroy$.next(undefined)
193
+ }
194
+ })
@@ -0,0 +1,289 @@
1
+ import * as d3 from 'd3'
2
+ import {
3
+ combineLatest,
4
+ switchMap,
5
+ first,
6
+ takeUntil,
7
+ Subject,
8
+ BehaviorSubject } from 'rxjs'
9
+ import {
10
+ defineSeriesPlugin } from '@orbcharts/core'
11
+ import type {
12
+ ChartParams } from '@orbcharts/core'
13
+ import type { PieLabelsPluginParams } from '../types'
14
+ import type { PieDatum } from '../seriesUtils'
15
+ import { DEFAULT_PIE_LABELS_PARAMS } from '../defaults'
16
+ import { makePieData } from '../seriesUtils'
17
+ import { makeD3Arc } from '../../utils/d3Utils'
18
+ import { getDatumColor, getClassName } from '../../utils/orbchartsUtils'
19
+
20
+ interface RenderDatum {
21
+ pieDatum: PieDatum
22
+ arcIndex: number
23
+ arcLabel: string
24
+ x: number
25
+ y: number
26
+ mouseoverX: number
27
+ mouseoverY: number
28
+ }
29
+
30
+ const pluginName = 'PieLabels'
31
+ const textClassName = getClassName(pluginName, 'text')
32
+
33
+ function makeRenderData (pieData: PieDatum[], arc: d3.Arc<any, d3.DefaultArcObject>, mouseoverArc: d3.Arc<any, d3.DefaultArcObject>, centroid: number): RenderDatum[] {
34
+ return pieData
35
+ .map((d, i) => {
36
+ const [_x, _y] = arc!.centroid(d as any)
37
+ const [_mouseoverX, _mouseoverY] = mouseoverArc!.centroid(d as any)
38
+ return {
39
+ pieDatum: d,
40
+ arcIndex: i,
41
+ arcLabel: d.data.label,
42
+ x: _x * centroid!,
43
+ y: _y * centroid!,
44
+ mouseoverX: _mouseoverX * centroid!,
45
+ mouseoverY: _mouseoverY * centroid!
46
+ }
47
+ })
48
+ .filter(d => d.pieDatum.data.visible)
49
+ }
50
+
51
+ // 繪製圓餅圖
52
+ function renderLabel (selection: d3.Selection<SVGGElement, undefined, any, any>, data: RenderDatum[], pluginParams: PieLabelsPluginParams, fullChartParams: ChartParams) {
53
+ // console.log(data)
54
+ // let update = this.gSelection.selectAll('g').data(pieData)
55
+ let update: d3.Selection<SVGPathElement, RenderDatum, any, any> = selection
56
+ .selectAll<SVGPathElement, RenderDatum>('text')
57
+ .data(data, d => d.pieDatum.id)
58
+ let enter = update.enter()
59
+ .append<SVGPathElement>('text')
60
+ .classed(textClassName, true)
61
+ let exit = update.exit()
62
+
63
+ enter
64
+ .append('text')
65
+
66
+ const labelSelection = update.merge(enter)
67
+ labelSelection
68
+ .attr('font-weight', 'bold')
69
+ .attr('text-anchor', 'middle')
70
+ .style('dominant-baseline', 'middle')
71
+ // .style('pointer-events', 'none')
72
+ .style('cursor', d => fullChartParams.highlightTarget && fullChartParams.highlightTarget != 'none'
73
+ ? 'pointer'
74
+ : 'none')
75
+ // .text((d, i) => d.arcLabel)
76
+ .text(d => pluginParams.labelFn(d.pieDatum.data))
77
+ .attr('font-size', fullChartParams.styles.textSize)
78
+ .attr('fill', (d, i) => getDatumColor({ datum: d.pieDatum.data, colorType: pluginParams.labelColorType, fullChartParams }))
79
+ .transition()
80
+ .attr('transform', (d) => {
81
+ // console.log('transform', d)
82
+ return 'translate(' + d.x + ',' + d.y + ')'
83
+ })
84
+ // .on('end', () => initHighlight({ labelSelection, data, fullChartParams }))
85
+ exit.remove()
86
+
87
+ // 如無新增資料則不用等動畫
88
+ // if (enter.size() == 0) {
89
+ // this.initHighlight()
90
+ // }
91
+
92
+ return labelSelection
93
+ }
94
+
95
+ // function initHighlight ({ labelSelection, data, fullChartParams }: {
96
+ // labelSelection: (d3.Selection<SVGPathElement, RenderDatum, any, any>)
97
+ // data: RenderDatum[]
98
+ // fullChartParams: ChartParams
99
+ // }) {
100
+ // removeHighlight({ labelSelection })
101
+ // // if (fullParams.highlightSeriesId || fullParams.highlightDatumId) {
102
+ // highlight({
103
+ // labelSelection,
104
+ // data,
105
+ // id: fullChartParams.highlightDefault,
106
+ // label: fullChartParams.highlightDefault,
107
+ // fullChartParams
108
+ // })
109
+ // // }
110
+ // }
111
+
112
+ function highlight ({ labelSelection, ids, fullChartParams }: {
113
+ labelSelection: (d3.Selection<SVGPathElement, RenderDatum, any, any>)
114
+ ids: string[]
115
+ fullChartParams: ChartParams
116
+ }) {
117
+ labelSelection.interrupt('highlight')
118
+
119
+ if (!ids.length) {
120
+ labelSelection
121
+ .transition()
122
+ .duration(200)
123
+ .attr('transform', (d) => {
124
+ return 'translate(' + d.x + ',' + d.y + ')'
125
+ })
126
+ .style('opacity', 1)
127
+ return
128
+ }
129
+
130
+ labelSelection.each((d, i, n) => {
131
+ const segment = d3.select<SVGPathElement, RenderDatum>(n[i])
132
+
133
+ if (ids.includes(d.pieDatum.id)) {
134
+ segment
135
+ .style('opacity', 1)
136
+ .transition()
137
+ .duration(200)
138
+ .attr('transform', (d) => {
139
+ return 'translate(' + d.mouseoverX + ',' + d.mouseoverY + ')'
140
+ })
141
+ } else {
142
+ segment
143
+ .style('opacity', fullChartParams.styles.unhighlightedOpacity)
144
+ .transition()
145
+ .duration(200)
146
+ .attr('transform', (d) => {
147
+ return 'translate(' + d.x + ',' + d.y + ')'
148
+ })
149
+ }
150
+ })
151
+ }
152
+
153
+
154
+ // function removeHighlight ({ labelSelection }: {
155
+ // labelSelection: (d3.Selection<SVGPathElement, RenderDatum, any, any> | undefined)
156
+ // }) {
157
+ // if (!labelSelection) {
158
+ // return
159
+ // }
160
+
161
+ // // 取消放大
162
+ // labelSelection
163
+ // .transition()
164
+ // .duration(200)
165
+ // .attr('transform', (d) => {
166
+ // return 'translate(' + d.x + ',' + d.y + ')'
167
+ // })
168
+ // .style('opacity', 1)
169
+
170
+ // }
171
+
172
+
173
+ export const PieLabels = defineSeriesPlugin(pluginName, DEFAULT_PIE_LABELS_PARAMS)(({ selection, observer, subject }) => {
174
+
175
+ const destroy$ = new Subject()
176
+
177
+ const graphicSelection: d3.Selection<SVGGElement, any, any, any> = selection.append('g')
178
+ let labelSelection$: Subject<d3.Selection<SVGPathElement, RenderDatum, any, any>> = new Subject()
179
+ let renderData: RenderDatum[] = []
180
+ // let highlightTarget: HighlightTarget | undefined
181
+ // let fullChartParams: ChartParams | undefined
182
+
183
+ observer.layout$
184
+ .pipe(
185
+ first()
186
+ )
187
+ .subscribe(size => {
188
+ selection
189
+ .attr('transform', `translate(${size.width / 2}, ${size.height / 2})`)
190
+ observer.layout$
191
+ .pipe(
192
+ takeUntil(destroy$)
193
+ )
194
+ .subscribe(size => {
195
+ selection
196
+ .transition()
197
+ .attr('transform', `translate(${size.width / 2}, ${size.height / 2})`)
198
+ })
199
+ })
200
+
201
+
202
+
203
+ // combineLatest({
204
+ // event: store.event$,
205
+ // fullChartParams: fullChartParams$
206
+ // }).pipe(
207
+ // // 轉換後會退訂前一個未完成的訂閱事件,因此可以取到「同時間」最後一次的訂閱事件
208
+ // switchMap(async (d) => d),
209
+ // ).subscribe(d => {
210
+ // if (d.event.eventName === 'mouseover' && d.event.datum) {
211
+ // highlight({
212
+ // labelSelection,
213
+ // data: renderData,
214
+ // id: d.fullChartParams.highlightTarget === 'datum' ? d.event.datum!.id : undefined,
215
+ // label: d.fullChartParams.highlightTarget === 'series' ? d.event.datum!.label : undefined,
216
+ // fullChartParams: d.fullChartParams
217
+ // })
218
+ // } else if (d.event.eventName === 'mouseout') {
219
+ // removeHighlight({ labelSelection })
220
+ // }
221
+ // })
222
+
223
+ combineLatest({
224
+ layout: observer.layout$,
225
+ computedData: observer.computedData$,
226
+ fullParams: observer.fullParams$,
227
+ fullChartParams: observer.fullChartParams$
228
+ }).pipe(
229
+ takeUntil(destroy$),
230
+ // 轉換後會退訂前一個未完成的訂閱事件,因此可以取到「同時間」最後一次的訂閱事件
231
+ switchMap(async (d) => d),
232
+ ).subscribe(data => {
233
+
234
+ const shorterSideWith = data.layout.width < data.layout.height ? data.layout.width : data.layout.height
235
+
236
+ // 弧產生器 (d3.arc())
237
+ const arc = makeD3Arc({
238
+ axisWidth: shorterSideWith,
239
+ innerRadius: 0,
240
+ outerRadius: data.fullParams.outerRadius,
241
+ padAngle: 0,
242
+ cornerRadius: 0
243
+ })
244
+
245
+ const arcMouseover = makeD3Arc({
246
+ axisWidth: shorterSideWith,
247
+ innerRadius: 0,
248
+ outerRadius: data.fullParams.outerMouseoverRadius, // 外半徑變化
249
+ padAngle: 0,
250
+ cornerRadius: 0
251
+ })
252
+
253
+ const pieData = makePieData({
254
+ computedDataSeries: data.computedData,
255
+ startAngle: data.fullParams.startAngle,
256
+ endAngle: data.fullParams.endAngle
257
+ })
258
+
259
+ renderData = makeRenderData(pieData, arc, arcMouseover, data.fullParams.labelCentroid)
260
+
261
+ const labelSelection = renderLabel(graphicSelection, renderData, data.fullParams, data.fullChartParams)
262
+
263
+ labelSelection$.next(labelSelection)
264
+
265
+ })
266
+
267
+ // const highlight$ = highlightObservable({ datumList$: computedData$, fullChartParams$, event$: store.event$ })
268
+ const highlightSubscription = observer.seriesHighlight$.subscribe()
269
+
270
+ combineLatest({
271
+ labelSelection: labelSelection$,
272
+ highlight: observer.seriesHighlight$,
273
+ fullChartParams: observer.fullChartParams$,
274
+ }).pipe(
275
+ takeUntil(destroy$),
276
+ switchMap(async d => d)
277
+ ).subscribe(data => {
278
+ highlight({
279
+ labelSelection: data.labelSelection,
280
+ ids: data.highlight,
281
+ fullChartParams: data.fullChartParams,
282
+ })
283
+ })
284
+
285
+ return () => {
286
+ destroy$.next(undefined)
287
+ highlightSubscription.unsubscribe()
288
+ }
289
+ })
File without changes
@@ -0,0 +1,51 @@
1
+ import * as d3 from 'd3'
2
+ import type { ComputedDataSeries, ComputedDatumSeries, EventName, EventSeries, HighlightTarget } from '@orbcharts/core'
3
+ // import type { D3PieDatum, PieDatum } from '../types'
4
+
5
+ // 由d3.pie()建出來的資料格式
6
+ export interface D3PieDatum {
7
+ data: any
8
+ index: number,
9
+ value: number,
10
+ startAngle: number,
11
+ endAngle: number,
12
+ padAngle: number,
13
+ }
14
+
15
+ export interface PieDatum extends D3PieDatum {
16
+ data: ComputedDatumSeries
17
+ id: string
18
+ }
19
+
20
+ export function makePieData ({ computedDataSeries, startAngle, endAngle }: {
21
+ computedDataSeries: ComputedDataSeries
22
+ startAngle: number
23
+ endAngle: number
24
+ // itemLabels: string[]
25
+ // arcLabels: string[]
26
+ }): PieDatum[] {
27
+ let pie = d3.pie<any, any>()
28
+ .startAngle(startAngle)
29
+ // .endAngle(startAngle + (endAngle - startAngle) * t)
30
+ .endAngle(endAngle)
31
+ .value((d) => d.visible == false ? 0 : d.value)
32
+ // .sort(null) // 不要排序
33
+ .sort((a, b) => a.sortedIndex - b.sortedIndex)
34
+ // .sort((a: any, b: any) => {
35
+ // return b.value - a.value
36
+ // })
37
+ // .sort(d3.ascending)
38
+ const pieData = pie(computedDataSeries)
39
+ return pieData.map((d: D3PieDatum, i: number) => {
40
+ // const itemLabel = d.data.itemLabel
41
+ let _d: any = d
42
+ _d.id = d.data.id
43
+ return _d
44
+ // return {
45
+ // ...d,
46
+ // itemIndex: itemLabels.indexOf(itemLabel),
47
+ // itemLabel,
48
+ // id: d.data.id,
49
+ // }
50
+ })
51
+ }
@@ -0,0 +1,53 @@
1
+ import type { ComputedDatumSeries, EventSeries, EventName, ColorType } from '@orbcharts/core'
2
+
3
+ export type ScaleType = 'area' | 'radius'
4
+
5
+ export interface BubblesPluginParams {
6
+ force: {
7
+ strength: number; // 泡泡引力
8
+ velocityDecay: number; // 衰減數
9
+ collisionSpacing: number // 泡泡間距
10
+ }
11
+ bubbleText: {
12
+ fillRate: number
13
+ lineHeight: number
14
+ lineLengthMin: number
15
+ }
16
+ highlightRIncrease: number
17
+ scaleType: ScaleType
18
+ }
19
+
20
+ export interface PiePluginParams {
21
+ // padding: Padding
22
+ outerRadius: number;
23
+ innerRadius: number;
24
+ outerMouseoverRadius: number;
25
+ // label?: LabelStyle
26
+ enterDuration: number
27
+ startAngle: number
28
+ endAngle: number
29
+ padAngle: number
30
+ // padRadius: number
31
+ cornerRadius: number
32
+ }
33
+
34
+ export interface PieEventTextsPluginParams {
35
+ eventFn: (d: EventSeries, eventName: EventName, t: number) => string[]
36
+ textAttrs: Array<{ [key:string]: string | number }>
37
+ textStyles: Array<{ [key:string]: string | number }>
38
+ }
39
+
40
+ export interface PieLabelsPluginParams {
41
+ // solidColor?: string;
42
+ // colors?: string[];
43
+ outerRadius: number
44
+ outerMouseoverRadius: number
45
+ // innerRadius?: number;
46
+ // enterDuration?: number
47
+ startAngle: number
48
+ endAngle: number
49
+ labelCentroid: number
50
+ // fontSize?: number
51
+ labelFn: ((d: ComputedDatumSeries) => string)
52
+ labelColorType: ColorType
53
+ }
File without changes
File without changes
@@ -0,0 +1,22 @@
1
+ // 取得文字寬度
2
+ export function measureTextWidth (text: string, size: number = 10) {
3
+ const context = document.createElement("canvas").getContext("2d")
4
+ let width = context?.measureText(text)?.width ?? 0
5
+ return width * size / 10 // 以10為基準
6
+ }
7
+
8
+ // 取得最小及最大值 - 數字陣列
9
+ export function getMinAndMax (data: number[]): [number, number] {
10
+ const defaultMinAndMax: [number, number] = [0, 0] // default
11
+ if (!data.length) {
12
+ return defaultMinAndMax
13
+ }
14
+ const minAndMax: [number, number] = data.reduce((prev, current) => {
15
+ // [min, max]
16
+ return [
17
+ current < prev[0] ? current : prev[0],
18
+ current > prev[1] ? current : prev[1]
19
+ ]
20
+ }, [data[0], data[0]])
21
+ return minAndMax
22
+ }