@orbcharts/plugins-basic 3.0.10 → 3.0.11

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orbcharts/plugins-basic",
3
- "version": "3.0.10",
3
+ "version": "3.0.11",
4
4
  "description": "Plugins for OrbCharts",
5
5
  "author": "Blue Planet Inc.",
6
6
  "license": "Apache-2.0",
@@ -39,9 +39,9 @@
39
39
  "vite-plugin-dts": "^3.7.3"
40
40
  },
41
41
  "dependencies": {
42
- "@orbcharts/core": "^3.0.6",
43
- "@orbcharts/core-types": "^3.0.4",
44
- "@orbcharts/plugins-basic-types": "^3.0.4",
42
+ "@orbcharts/core": "^3.0.7",
43
+ "@orbcharts/core-types": "^3.0.5",
44
+ "@orbcharts/plugins-basic-types": "^3.0.5",
45
45
  "d3": "^7.8.5",
46
46
  "rxjs": "^7.8.1"
47
47
  }
@@ -230,7 +230,9 @@ export const DEFAULT_INDICATOR_PARAMS: IndicatorParams = {
230
230
  startAngle: - Math.PI / 2,
231
231
  endAngle: Math.PI / 2,
232
232
  radius: 0.6,
233
- size: 20,
233
+ indicatorType: 'needle',
234
+ size: 10,
234
235
  colorType: 'label',
235
- value: 0
236
+ // autoHighlight: false,
237
+ value: 0,
236
238
  }
@@ -2,17 +2,22 @@ import * as d3 from 'd3'
2
2
  import {
3
3
  combineLatest,
4
4
  switchMap,
5
+ mergeMap,
6
+ mergeWith,
7
+ concatMap,
5
8
  first,
9
+ filter,
6
10
  map,
7
11
  takeUntil,
8
12
  Observable,
9
13
  distinctUntilChanged,
10
14
  Subject,
11
15
  BehaviorSubject } from 'rxjs'
12
- import type { DefinePluginConfig } from '../../../lib/core-types'
16
+ import type { ContextSubject, DefinePluginConfig } from '../../../lib/core-types'
13
17
  import {
14
18
  defineSeriesPlugin } from '../../../lib/core'
15
19
  import type {
20
+ ComputedDataSeries,
16
21
  ComputedDatumSeries,
17
22
  ContainerPosition,
18
23
  EventSeries,
@@ -23,16 +28,33 @@ import type { IndicatorParams } from '../../../lib/plugins-basic-types'
23
28
  import { DEFAULT_INDICATOR_PARAMS } from '../defaults'
24
29
  import { getDatumColor, getClassName } from '../../utils/orbchartsUtils'
25
30
  import { seriesCenterSelectionObservable } from '../seriesObservables'
26
- import { LAYER_INDEX_OF_LABEL } from '../../const'
31
+ import { LAYER_INDEX_OF_GRAPHIC_COVER } from '../../const'
32
+
33
+ interface RenderParams {
34
+ containerSelection: d3.Selection<SVGGElement, any, any, unknown>
35
+ angle: number
36
+ value: number
37
+ datum: ComputedDatumSeries | null
38
+ computedData: ComputedDataSeries
39
+ SeriesDataMap: Map<string, ComputedDatumSeries[]>
40
+ pointerDistance: number
41
+ fullParams: IndicatorParams
42
+ fullChartParams: ChartParams
43
+ graphicColor: string
44
+ event$: Subject<EventSeries>
45
+ }
27
46
 
28
47
  const pluginName = 'Indicator'
29
48
  const indicatorGClassName = getClassName(pluginName, 'indicator-g')
30
49
  const triangleGClassName = getClassName(pluginName, 'triangle-g')
50
+ const lineGClassName = getClassName(pluginName, 'line-g')
51
+ const needleGClassName = getClassName(pluginName, 'needle-g')
52
+ const pinGClassName = getClassName(pluginName, 'pin-g')
31
53
 
32
54
  const pluginConfig: DefinePluginConfig<typeof pluginName, typeof DEFAULT_INDICATOR_PARAMS> = {
33
55
  name: pluginName,
34
56
  defaultParams: DEFAULT_INDICATOR_PARAMS,
35
- layerIndex: LAYER_INDEX_OF_LABEL,
57
+ layerIndex: LAYER_INDEX_OF_GRAPHIC_COVER,
36
58
  validator: (params, { validateColumns }) => {
37
59
  const result = validateColumns(params, {
38
60
  startAngle: {
@@ -44,12 +66,19 @@ const pluginConfig: DefinePluginConfig<typeof pluginName, typeof DEFAULT_INDICAT
44
66
  radius: {
45
67
  toBeTypes: ['number'],
46
68
  },
69
+ indicatorType: {
70
+ toBe: '"line" | "needle" | "pin" | "triangle"',
71
+ test: (value: any) => ['line', 'needle', 'pin', 'triangle'].includes(value)
72
+ },
47
73
  size: {
48
74
  toBeTypes: ['number'],
49
75
  },
50
76
  colorType: {
51
77
  toBeOption: 'ColorType'
52
78
  },
79
+ // autoHighlight: {
80
+ // toBeTypes: ['boolean'],
81
+ // },
53
82
  value: {
54
83
  toBeTypes: ['number'],
55
84
  },
@@ -58,15 +87,17 @@ const pluginConfig: DefinePluginConfig<typeof pluginName, typeof DEFAULT_INDICAT
58
87
  }
59
88
  }
60
89
 
61
- function renderIndicatorTriangle ({ containerSelection, angle, pointerDistance, fullParams, fullChartParams, graphicColor }: {
90
+ function createIndicatorG({ containerSelection, angle, datum, value, computedData, SeriesDataMap, fullParams, fullChartParams, event$ }: {
62
91
  containerSelection: d3.Selection<SVGGElement, any, any, unknown>
63
92
  angle: number
64
- pointerDistance: number
93
+ datum: ComputedDatumSeries | null
94
+ value: number
95
+ computedData: ComputedDataSeries
96
+ SeriesDataMap: Map<string, ComputedDatumSeries[]>
65
97
  fullParams: IndicatorParams
66
98
  fullChartParams: ChartParams
67
- graphicColor: string
99
+ event$: Subject<EventSeries>
68
100
  }) {
69
-
70
101
  const indicatorG = containerSelection.selectAll(`g.${indicatorGClassName}`)
71
102
  .data([angle])
72
103
  .join(
@@ -77,11 +108,56 @@ function renderIndicatorTriangle ({ containerSelection, angle, pointerDistance,
77
108
  exit => exit.remove()
78
109
  )
79
110
 
80
- indicatorG
111
+ const transitionG = indicatorG
81
112
  .transition()
82
113
  .duration(fullChartParams.transitionDuration)
83
114
  .attr('transform', `rotate(${angle})`)
84
115
 
116
+ const series = SeriesDataMap.get(datum.seriesLabel)!
117
+
118
+ // work around(暫時使用這個方式來共享value)
119
+ transitionG
120
+ .tween('move', (self, t) => {
121
+ return (t) => {
122
+ event$.next({
123
+ type: 'series',
124
+ pluginName,
125
+ eventName: 'transitionMove',
126
+ event: undefined,
127
+ highlightTarget: fullChartParams.highlightTarget,
128
+ datum: datum,
129
+ series: series,
130
+ seriesIndex: datum.seriesIndex,
131
+ seriesLabel: datum.seriesLabel,
132
+ data: computedData,
133
+ mark: value, // work around
134
+ tween: t
135
+ })
136
+ }
137
+ })
138
+ .on('end', (self, t) => {
139
+ event$.next({
140
+ type: 'series',
141
+ pluginName,
142
+ eventName: 'transitionEnd',
143
+ event: undefined,
144
+ highlightTarget: fullChartParams.highlightTarget,
145
+ datum: datum,
146
+ series: series,
147
+ seriesIndex: datum.seriesIndex,
148
+ seriesLabel: datum.seriesLabel,
149
+ data: computedData,
150
+ mark: value // work around
151
+ })
152
+ })
153
+
154
+ return indicatorG
155
+ }
156
+
157
+ function renderIndicatorTriangle ({ containerSelection, angle, value, datum, computedData, SeriesDataMap, pointerDistance, fullParams, fullChartParams, graphicColor, event$ }: RenderParams) {
158
+
159
+ const indicatorG = createIndicatorG({ containerSelection, angle, value, datum, computedData, SeriesDataMap, fullParams, fullChartParams, event$ })
160
+
85
161
  indicatorG
86
162
  .selectAll(`g.${triangleGClassName}`)
87
163
  .data([pointerDistance])
@@ -98,16 +174,96 @@ function renderIndicatorTriangle ({ containerSelection, angle, pointerDistance,
98
174
  .attr('fill', graphicColor)
99
175
  }
100
176
 
177
+ function renderIndicatorLine ({ containerSelection, angle, value, datum, computedData, SeriesDataMap, pointerDistance, fullParams, fullChartParams, graphicColor, event$ }: RenderParams) {
178
+ const indicatorG = createIndicatorG({ containerSelection, angle, value, datum, computedData, SeriesDataMap, fullParams, fullChartParams, event$ })
179
+
180
+ indicatorG
181
+ .selectAll(`g.${lineGClassName}`)
182
+ .data([pointerDistance])
183
+ .join('g')
184
+ .attr('class', lineGClassName)
185
+ .selectAll('rect')
186
+ .data([fullParams.size])
187
+ .join('rect')
188
+ .attr('x', -fullParams.size / 2) // 水平置中
189
+ .attr('y', -pointerDistance) // 從中心向上延伸
190
+ .attr('width', fullParams.size) // 寬度為 size
191
+ .attr('height', pointerDistance) // 長度為 pointerDistance
192
+ .attr('fill', graphicColor)
193
+ }
194
+
195
+ function renderIndicatorNeedle ({ containerSelection, angle, value, datum, computedData, SeriesDataMap, pointerDistance, fullParams, fullChartParams, graphicColor, event$ }: RenderParams) {
196
+ const indicatorG = createIndicatorG({ containerSelection, angle, value, datum, computedData, SeriesDataMap, fullParams, fullChartParams, event$ })
197
+
198
+ indicatorG
199
+ .selectAll(`g.${needleGClassName}`)
200
+ .data([pointerDistance])
201
+ .join('g')
202
+ .attr('class', needleGClassName)
203
+ .selectAll('path')
204
+ .data([fullParams.size])
205
+ .join('path')
206
+ .attr('d', () => {
207
+ const width = fullParams.size
208
+
209
+ // 建構4角菱形路徑
210
+ const points = [
211
+ [0, -pointerDistance], // 頂點(針尖)
212
+ [width / 2, 0], // 右側最寬點
213
+ [0, width / 2], // 尾部
214
+ [-width / 2, 0] // 左側最寬點
215
+ ]
216
+
217
+ return `M${points.map(p => p.join(',')).join('L')}Z`
218
+ })
219
+ .attr('fill', graphicColor)
220
+ }
221
+
222
+ function renderIndicatorPin ({ containerSelection, angle, value, datum, computedData, SeriesDataMap, pointerDistance, fullParams, fullChartParams, graphicColor, event$ }: RenderParams) {
223
+ const indicatorG = createIndicatorG({ containerSelection, angle, value, datum, computedData, SeriesDataMap, fullParams, fullChartParams, event$ })
224
+
225
+ const pinG = indicatorG
226
+ .selectAll(`g.${pinGClassName}`)
227
+ .data([pointerDistance])
228
+ .join('g')
229
+ .attr('class', pinGClassName)
230
+
231
+ // 繪製大頭針的針身(細線)- 從中心向外延伸
232
+ pinG
233
+ .selectAll('line.pin-shaft')
234
+ .data([1])
235
+ .join('line')
236
+ .attr('class', 'pin-shaft')
237
+ .attr('x1', 0)
238
+ .attr('y1', 0) // 從中心開始
239
+ .attr('x2', 0)
240
+ .attr('y2', -pointerDistance) // 向外延伸到 pointerDistance
241
+ .attr('stroke', graphicColor)
242
+ .attr('stroke-width', Math.min(fullParams.size * 0.2, 2)) // 針身較細
243
+ .attr('stroke-linecap', 'round')
244
+
245
+ // 繪製大頭針的頭部(圓形)- 放在中心位置
246
+ pinG
247
+ .selectAll('circle.pin-head')
248
+ .data([fullParams.size])
249
+ .join('circle')
250
+ .attr('class', 'pin-head')
251
+ .attr('cx', 0)
252
+ .attr('cy', 0) // 頭部在中心
253
+ .attr('r', fullParams.size / 2) // 頭部半徑為 size 的一半
254
+ .attr('fill', graphicColor)
255
+ }
256
+
101
257
  function createEachGraphic (pluginName: string, context: {
102
258
  containerSelection: d3.Selection<SVGGElement, any, any, unknown>
103
- // computedData$: Observable<ComputedDatumSeries[][]>
259
+ renderFn: (params: RenderParams) => void
104
260
  containerVisibleComputedSortedData$: Observable<ComputedDatumSeries[]>
105
- // SeriesDataMap$: Observable<Map<string, ComputedDatumSeries[]>>
106
261
  fullParams$: Observable<IndicatorParams>
107
262
  fullChartParams$: Observable<ChartParams>
108
- // textSizePx$: Observable<number>
109
- // seriesHighlight$: Observable<ComputedDatumSeries[]>
110
263
  seriesContainerPosition$: Observable<ContainerPosition>
264
+ computedData$: Observable<ComputedDatumSeries[][]>
265
+ SeriesDataMap$: Observable<Map<string, ComputedDatumSeries[]>>
266
+ subject: ContextSubject<"series">
111
267
  event$: Subject<EventSeries>
112
268
  }) {
113
269
  const destroy$ = new Subject()
@@ -119,7 +275,7 @@ function createEachGraphic (pluginName: string, context: {
119
275
  distinctUntilChanged()
120
276
  )
121
277
 
122
- const valueToAngle$ = combineLatest({
278
+ const valueToAngleScale$ = combineLatest({
123
279
  fullParams: context.fullParams$,
124
280
  containerValueSum: containerValueSum$,
125
281
  }).pipe(
@@ -138,14 +294,14 @@ function createEachGraphic (pluginName: string, context: {
138
294
 
139
295
  const angle$ = combineLatest({
140
296
  value: value$,
141
- valueToAngle: valueToAngle$,
297
+ valueToAngleScale: valueToAngleScale$,
142
298
  containerValueSum: containerValueSum$,
143
299
  }).pipe(
144
300
  switchMap(async d => d),
145
- map(({ value, valueToAngle, containerValueSum }) => {
301
+ map(({ value, valueToAngleScale, containerValueSum }) => {
146
302
  // value 限制在 0 ~ containerValueSum 之間
147
303
  const validValue = Math.max(Math.min(value, containerValueSum), 0)
148
- return valueToAngle(validValue)
304
+ return valueToAngleScale(validValue)
149
305
  }),
150
306
  distinctUntilChanged()
151
307
  )
@@ -165,33 +321,38 @@ function createEachGraphic (pluginName: string, context: {
165
321
  )
166
322
 
167
323
  // indicator 的 value 對應到 data 區間
168
- const valueSeriesIndex$ = combineLatest({
324
+ const datum$ = combineLatest({
169
325
  value: value$,
170
326
  containerVisibleComputedSortedData: context.containerVisibleComputedSortedData$,
171
327
  }).pipe(
172
328
  switchMap(async d => d),
173
329
  map(({ value, containerVisibleComputedSortedData }) => {
174
- let seriesIndex = 0
330
+ // let seriesIndex = 0
331
+ let datum: ComputedDatumSeries | null = null
175
332
  let stackedValue = 0
176
333
  for (let i = 0; i < containerVisibleComputedSortedData.length; i++) {
177
334
  const datumValue = containerVisibleComputedSortedData[i].value ?? 0
178
335
  stackedValue += datumValue
179
336
  if (stackedValue >= value) {
180
- seriesIndex = containerVisibleComputedSortedData[i].seriesIndex
337
+ // seriesIndex = containerVisibleComputedSortedData[i].seriesIndex
338
+ datum = containerVisibleComputedSortedData[i]
181
339
  break
182
340
  }
183
341
  if (i === containerVisibleComputedSortedData.length - 1) {
184
- seriesIndex = containerVisibleComputedSortedData[i].seriesIndex
342
+ // seriesIndex = containerVisibleComputedSortedData[i].seriesIndex
343
+ datum = containerVisibleComputedSortedData[i]
185
344
  }
186
345
  }
187
- return seriesIndex
346
+ return datum
188
347
  }),
189
348
  distinctUntilChanged()
190
349
  )
191
350
 
192
351
  const graphicColor$ = combineLatest({
193
352
  value: value$,
194
- valueSeriesIndex: valueSeriesIndex$,
353
+ valueSeriesIndex: datum$.pipe(
354
+ map(d => d ? d.seriesIndex : 0),
355
+ ),
195
356
  // containerVisibleComputedSortedData: context.containerVisibleComputedSortedData$,
196
357
  fullParams: context.fullParams$,
197
358
  fullChartParams: context.fullChartParams$,
@@ -217,20 +378,71 @@ function createEachGraphic (pluginName: string, context: {
217
378
  distinctUntilChanged()
218
379
  )
219
380
 
381
+ // 紀錄目前的 chartParams
382
+ let chartParamsRef: ChartParams | null = null
383
+ context.fullChartParams$
384
+ .pipe(takeUntil(destroy$))
385
+ .subscribe(params => {
386
+ chartParamsRef = params
387
+ })
388
+
389
+ // const newDefaultHighlight$ = combineLatest({
390
+ // datum: datum$,
391
+ // fullChartParams: context.fullChartParams$,
392
+ // }).pipe(
393
+ // switchMap(async d => d),
394
+ // map(data => {
395
+ // return data.fullChartParams.highlightTarget === 'datum'
396
+ // ? data.datum.id
397
+ // : data.fullChartParams.highlightTarget === 'series'
398
+ // ? data.datum.seriesLabel
399
+ // : null
400
+ // }),
401
+ // distinctUntilChanged()
402
+ // )
403
+
404
+ // autoHighlight
405
+ // context.fullParams$.pipe(
406
+ // map(params => params.autoHighlight),
407
+ // filter(autoHighlight => autoHighlight === true),
408
+ // mergeWith(context.event$.pipe(
409
+ // filter(event => event.eventName === 'mouseout'),
410
+ // )),
411
+ // switchMap(() => newDefaultHighlight$),
412
+ // takeUntil(destroy$),
413
+ // ).subscribe(newDefaultHighlight => {
414
+ // if (!newDefaultHighlight) {
415
+ // return
416
+ // }
417
+ // context.subject.chartParams$.next({
418
+ // ...chartParamsRef,
419
+ // highlightDefault: newDefaultHighlight
420
+ // })
421
+ // })
422
+
220
423
  combineLatest({
221
424
  fullParams: context.fullParams$,
222
425
  fullChartParams: context.fullChartParams$,
223
426
  angle: angle$,
224
427
  pointerDistance: pointerDistance$,
225
428
  graphicColor: graphicColor$,
429
+ value: value$,
430
+ datum: datum$,
431
+ computedData: context.computedData$,
432
+ SeriesDataMap: context.SeriesDataMap$,
226
433
  }).subscribe(data => {
227
- renderIndicatorTriangle({
434
+ context.renderFn({
228
435
  containerSelection: context.containerSelection,
229
436
  angle: data.angle,
437
+ value: data.fullParams.value,
438
+ datum: data.datum,
439
+ computedData: data.computedData,
440
+ SeriesDataMap: data.SeriesDataMap,
230
441
  pointerDistance: data.pointerDistance,
231
442
  fullParams: data.fullParams,
232
443
  fullChartParams: data.fullChartParams,
233
444
  graphicColor: data.graphicColor,
445
+ event$: context.event$
234
446
  })
235
447
  })
236
448
 
@@ -242,7 +454,15 @@ function createEachGraphic (pluginName: string, context: {
242
454
 
243
455
 
244
456
  export const Indicator = defineSeriesPlugin(pluginConfig)(({ selection, observer, subject }) => {
245
-
457
+ subject.plugins$
458
+ // .pipe(
459
+ // map(plugins => plugins.find(p => p.name === 'Indicator')),
460
+ // filter(p => !!p),
461
+ // switchMap(p => p.params$)
462
+ // )
463
+ .subscribe(params => {
464
+ console.log('Indicator params', params)
465
+ })
246
466
  const destroy$ = new Subject()
247
467
 
248
468
  const { seriesCenterSelection$ } = seriesCenterSelectionObservable({
@@ -254,43 +474,61 @@ export const Indicator = defineSeriesPlugin(pluginConfig)(({ selection, observer
254
474
 
255
475
  const unsubscribeFnArr: (() => void)[] = []
256
476
 
257
- seriesCenterSelection$
258
- .pipe(
259
- takeUntil(destroy$)
260
- )
261
- .subscribe(seriesCenterSelection => {
262
- // 每次重新計算時,清除之前的訂閱
263
- unsubscribeFnArr.forEach(fn => fn())
264
-
265
- seriesCenterSelection.each((d, containerIndex, g) => {
266
-
267
- const containerSelection = d3.select(g[containerIndex])
268
-
269
- const containerVisibleComputedSortedData$ = observer.visibleComputedSortedData$.pipe(
270
- takeUntil(destroy$),
271
- map(data => data[containerIndex] ?? data[0])
272
- )
273
-
274
- const containerPosition$ = observer.seriesContainerPosition$.pipe(
275
- takeUntil(destroy$),
276
- map(data => data[containerIndex] ?? data[0])
277
- )
278
-
279
- unsubscribeFnArr[containerIndex] = createEachGraphic(pluginName, {
280
- containerSelection: containerSelection,
281
- // computedData$: observer.computedData$,
282
- containerVisibleComputedSortedData$: containerVisibleComputedSortedData$,
283
- // SeriesDataMap$: observer.SeriesDataMap$,
284
- fullParams$: observer.fullParams$,
285
- fullChartParams$: observer.fullChartParams$,
286
- // textSizePx$: observer.textSizePx$,
287
- // seriesHighlight$: observer.seriesHighlight$,
288
- seriesContainerPosition$: containerPosition$,
289
- event$: subject.event$,
290
- })
477
+ const renderFn$ = observer.fullParams$.pipe(
478
+ map(params => {
479
+ if (params.indicatorType === 'triangle') {
480
+ return renderIndicatorTriangle
481
+ } else if (params.indicatorType === 'line') {
482
+ return renderIndicatorLine
483
+ } else if (params.indicatorType === 'needle') {
484
+ return renderIndicatorNeedle
485
+ } else if (params.indicatorType === 'pin') {
486
+ return renderIndicatorPin
487
+ } else {
488
+ return renderIndicatorTriangle
489
+ }
490
+ }),
491
+ )
291
492
 
493
+ combineLatest({
494
+ seriesCenterSelection: seriesCenterSelection$,
495
+ renderFn: renderFn$,
496
+ }).pipe(
497
+ switchMap(async d => d),
498
+ takeUntil(destroy$)
499
+ ).subscribe(data => {
500
+ // 每次重新計算時,清除之前的訂閱
501
+ unsubscribeFnArr.forEach(fn => fn())
502
+
503
+ data.seriesCenterSelection.each((d, containerIndex, g) => {
504
+
505
+ const containerSelection = d3.select(g[containerIndex])
506
+
507
+ const containerVisibleComputedSortedData$ = observer.visibleComputedSortedData$.pipe(
508
+ takeUntil(destroy$),
509
+ map(data => data[containerIndex] ?? data[0])
510
+ )
511
+
512
+ const containerPosition$ = observer.seriesContainerPosition$.pipe(
513
+ takeUntil(destroy$),
514
+ map(data => data[containerIndex] ?? data[0])
515
+ )
516
+
517
+ unsubscribeFnArr[containerIndex] = createEachGraphic(pluginName, {
518
+ containerSelection: containerSelection,
519
+ renderFn: data.renderFn,
520
+ containerVisibleComputedSortedData$: containerVisibleComputedSortedData$,
521
+ fullParams$: observer.fullParams$,
522
+ fullChartParams$: observer.fullChartParams$,
523
+ seriesContainerPosition$: containerPosition$,
524
+ computedData$: observer.computedData$,
525
+ SeriesDataMap$: observer.SeriesDataMap$,
526
+ subject,
527
+ event$: subject.event$,
292
528
  })
529
+
293
530
  })
531
+ })
294
532
 
295
533
  return () => {
296
534
  destroy$.next(undefined)
@@ -151,6 +151,93 @@ function renderPie ({ selection, data, arc, pathClassName, fullParams, fullChart
151
151
  return pathSelection
152
152
  }
153
153
 
154
+ // function renderGauge ({ selection, data, arc, pathClassName, fullParams, fullChartParams, axisWidth }: {
155
+ // selection: d3.Selection<SVGGElement, unknown, any, unknown>
156
+ // data: PieDatum[]
157
+ // arc: d3.Arc<any, d3.DefaultArcObject>
158
+ // pathClassName: string
159
+ // fullParams: PieParams
160
+ // fullChartParams: ChartParams
161
+ // axisWidth: number
162
+ // }) {
163
+ // const gaugeClassName = getClassName('Gauge', 'tick')
164
+ // const gaugeLabelClassName = getClassName('Gauge', 'label')
165
+
166
+ // // 計算總角度範圍
167
+ // const totalAngle = fullParams.endAngle - fullParams.startAngle
168
+ // const totalTicks = 20 // 總刻度數量
169
+ // const tickInterval = totalAngle / totalTicks
170
+
171
+ // // 計算刻度資料
172
+ // const tickData = Array.from({ length: totalTicks + 1 }, (_, i) => {
173
+ // const angle = fullParams.startAngle + (i * tickInterval)
174
+ // const isLongTick = i % 5 === 0 // 每5格一個長線
175
+ // return {
176
+ // angle,
177
+ // isLongTick,
178
+ // value: Math.round((i / totalTicks) * 100) // 0-100的數值
179
+ // }
180
+ // })
181
+
182
+ // // 計算實際像素半徑
183
+ // const arcScale = d3.scaleLinear()
184
+ // .domain([0, 1])
185
+ // .range([0, axisWidth / 2])
186
+
187
+ // const outerRadius = arcScale(fullParams.outerRadius)
188
+ // const innerRadius = arcScale(fullParams.innerRadius)
189
+ // const longTickLength = (outerRadius - innerRadius) * 0.3
190
+ // const shortTickLength = (outerRadius - innerRadius) * 0.15
191
+
192
+ // // 繪製刻度線
193
+ // const tickSelection = selection
194
+ // .selectAll(`line.${gaugeClassName}`)
195
+ // .data(tickData)
196
+ // .join('line')
197
+ // .classed(gaugeClassName, true)
198
+ // .attr('x1', d => {
199
+ // const radius = outerRadius
200
+ // return Math.cos(d.angle - Math.PI / 2) * radius
201
+ // })
202
+ // .attr('y1', d => {
203
+ // const radius = outerRadius
204
+ // return Math.sin(d.angle - Math.PI / 2) * radius
205
+ // })
206
+ // .attr('x2', d => {
207
+ // const radius = outerRadius - (d.isLongTick ? longTickLength : shortTickLength)
208
+ // return Math.cos(d.angle - Math.PI / 2) * radius
209
+ // })
210
+ // .attr('y2', d => {
211
+ // const radius = outerRadius - (d.isLongTick ? longTickLength : shortTickLength)
212
+ // return Math.sin(d.angle - Math.PI / 2) * radius
213
+ // })
214
+ // .attr('stroke', fullChartParams.colors[fullChartParams.colorScheme].primary)
215
+ // .attr('stroke-width', d => d.isLongTick ? 2 : 1)
216
+ // .attr('stroke-linecap', 'round')
217
+
218
+ // // 繪製數字標籤(只在長線上)
219
+ // const labelSelection = selection
220
+ // .selectAll(`text.${gaugeLabelClassName}`)
221
+ // .data(tickData.filter(d => d.isLongTick))
222
+ // .join('text')
223
+ // .classed(gaugeLabelClassName, true)
224
+ // .attr('x', d => {
225
+ // const radius = outerRadius + 15 // 稍微往外一點
226
+ // return Math.cos(d.angle - Math.PI / 2) * radius
227
+ // })
228
+ // .attr('y', d => {
229
+ // const radius = outerRadius + 15
230
+ // return Math.sin(d.angle - Math.PI / 2) * radius
231
+ // })
232
+ // .attr('text-anchor', 'middle')
233
+ // .attr('dominant-baseline', 'middle')
234
+ // .attr('fill', fullChartParams.colors[fullChartParams.colorScheme].primary)
235
+ // .attr('font-size', '12px')
236
+ // .text(d => d.value)
237
+
238
+ // return { tickSelection, labelSelection }
239
+ // }
240
+
154
241
  function highlight ({ pathSelection, ids, fullChartParams, arc, arcHighlight }: {
155
242
  pathSelection: d3.Selection<SVGPathElement, PieDatum, any, any>
156
243
  ids: string[]
@@ -314,6 +401,14 @@ function createEachPie (pluginName: string, context: {
314
401
  distinctUntilChanged()
315
402
  )
316
403
 
404
+ // highlight的對象(不做成observable是因為要避免觸發監聽)
405
+ let seriesHighlight: ComputedDatumSeries[] = []
406
+ context.seriesHighlight$
407
+ .pipe(
408
+ takeUntil(destroy$)
409
+ )
410
+ .subscribe(d => seriesHighlight = d)
411
+
317
412
  const pathSelection$ = new Observable<d3.Selection<SVGPathElement, PieDatum, any, any>>(subscriber => {
318
413
  combineLatest({
319
414
  pieData: pieData$,
@@ -359,6 +454,16 @@ function createEachPie (pluginName: string, context: {
359
454
  fullParams: data.fullParams,
360
455
  fullChartParams: data.fullChartParams,
361
456
  })
457
+
458
+ // renderGauge({
459
+ // selection: context.containerSelection,
460
+ // data: tweenData,
461
+ // arc: data.arc,
462
+ // pathClassName,
463
+ // fullParams: data.fullParams,
464
+ // fullChartParams: data.fullChartParams,
465
+ // axisWidth: 500
466
+ // })
362
467
 
363
468
  // @Q@ 想盡量減清效能負擔所以取消掉
364
469
  // context.event$.next({
@@ -422,13 +527,12 @@ function createEachPie (pluginName: string, context: {
422
527
  eventName: 'transitionEnd',
423
528
  event: undefined,
424
529
  highlightTarget: data.highlightTarget,
425
- datum: null,
530
+ data: data.computedData,
426
531
  series: [],
427
532
  seriesIndex: -1,
428
533
  seriesLabel: '',
429
- data: data.computedData
534
+ datum: null
430
535
  })
431
-
432
536
 
433
537
  })
434
538