@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,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
+ }