@orbcharts/plugins-basic 3.0.0-alpha.68 → 3.0.0-alpha.70

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. package/LICENSE +200 -200
  2. package/dist/orbcharts-plugins-basic.es.js +4838 -4760
  3. package/dist/orbcharts-plugins-basic.umd.js +15 -14
  4. package/dist/src/utils/d3Graphics.d.ts +10 -0
  5. package/package.json +42 -42
  6. package/src/base/BaseBarStack.ts +779 -779
  7. package/src/base/BaseBars.ts +764 -764
  8. package/src/base/BaseBarsTriangle.ts +672 -672
  9. package/src/base/BaseDots.ts +513 -513
  10. package/src/base/BaseGroupAxis.ts +675 -652
  11. package/src/base/BaseLegend.ts +642 -642
  12. package/src/base/BaseLineAreas.ts +628 -628
  13. package/src/base/BaseLines.ts +704 -704
  14. package/src/base/BaseValueAxis.ts +578 -578
  15. package/src/base/types.ts +2 -2
  16. package/src/grid/defaults.ts +128 -128
  17. package/src/grid/gridObservables.ts +543 -543
  18. package/src/grid/index.ts +15 -15
  19. package/src/grid/plugins/BarStack.ts +43 -43
  20. package/src/grid/plugins/Bars.ts +44 -44
  21. package/src/grid/plugins/BarsPN.ts +41 -41
  22. package/src/grid/plugins/BarsTriangle.ts +42 -42
  23. package/src/grid/plugins/Dots.ts +37 -37
  24. package/src/grid/plugins/GridLegend.ts +59 -59
  25. package/src/grid/plugins/GroupAux.ts +1014 -991
  26. package/src/grid/plugins/GroupAxis.ts +36 -36
  27. package/src/grid/plugins/LineAreas.ts +40 -40
  28. package/src/grid/plugins/Lines.ts +40 -40
  29. package/src/grid/plugins/ScalingArea.ts +176 -174
  30. package/src/grid/plugins/ValueAxis.ts +36 -36
  31. package/src/grid/plugins/ValueStackAxis.ts +38 -38
  32. package/src/grid/types.ts +123 -123
  33. package/src/index.ts +9 -9
  34. package/src/multiGrid/defaults.ts +158 -158
  35. package/src/multiGrid/index.ts +13 -13
  36. package/src/multiGrid/multiGridObservables.ts +49 -49
  37. package/src/multiGrid/plugins/MultiBarStack.ts +78 -78
  38. package/src/multiGrid/plugins/MultiBars.ts +77 -77
  39. package/src/multiGrid/plugins/MultiBarsTriangle.ts +77 -77
  40. package/src/multiGrid/plugins/MultiDots.ts +65 -65
  41. package/src/multiGrid/plugins/MultiGridLegend.ts +89 -89
  42. package/src/multiGrid/plugins/MultiGroupAxis.ts +70 -70
  43. package/src/multiGrid/plugins/MultiLineAreas.ts +77 -77
  44. package/src/multiGrid/plugins/MultiLines.ts +77 -77
  45. package/src/multiGrid/plugins/MultiValueAxis.ts +69 -69
  46. package/src/multiGrid/plugins/MultiValueStackAxis.ts +69 -69
  47. package/src/multiGrid/plugins/OverlappingValueAxes.ts +170 -170
  48. package/src/multiGrid/plugins/OverlappingValueStackAxes.ts +169 -169
  49. package/src/multiGrid/types.ts +72 -72
  50. package/src/noneData/defaults.ts +102 -102
  51. package/src/noneData/index.ts +3 -3
  52. package/src/noneData/plugins/Container.ts +10 -10
  53. package/src/noneData/plugins/Tooltip.ts +327 -327
  54. package/src/noneData/types.ts +26 -26
  55. package/src/series/defaults.ts +149 -149
  56. package/src/series/index.ts +9 -9
  57. package/src/series/plugins/Bubbles.ts +545 -545
  58. package/src/series/plugins/Pie.ts +584 -584
  59. package/src/series/plugins/PieEventTexts.ts +262 -262
  60. package/src/series/plugins/PieLabels.ts +604 -598
  61. package/src/series/plugins/Rose.ts +481 -481
  62. package/src/series/plugins/RoseLabels.ts +571 -565
  63. package/src/series/plugins/SeriesLegend.ts +59 -59
  64. package/src/series/seriesObservables.ts +145 -145
  65. package/src/series/seriesUtils.ts +51 -51
  66. package/src/series/types.ts +87 -87
  67. package/src/tree/defaults.ts +23 -23
  68. package/src/tree/index.ts +3 -3
  69. package/src/tree/plugins/TreeLegend.ts +59 -59
  70. package/src/tree/plugins/TreeMap.ts +305 -305
  71. package/src/tree/types.ts +23 -23
  72. package/src/utils/commonUtils.ts +21 -21
  73. package/src/utils/d3Graphics.ts +174 -124
  74. package/src/utils/d3Utils.ts +73 -73
  75. package/src/utils/observables.ts +14 -14
  76. package/src/utils/orbchartsUtils.ts +100 -100
  77. package/tsconfig.base.json +13 -13
  78. package/tsconfig.json +2 -2
  79. package/vite.config.js +22 -22
@@ -1,328 +1,328 @@
1
- import * as d3 from 'd3'
2
- import {
3
- combineLatest,
4
- map,
5
- merge,
6
- filter,
7
- switchMap,
8
- first,
9
- iif,
10
- takeUntil,
11
- Subject,
12
- Observable,
13
- distinctUntilChanged } from 'rxjs'
14
- import type {
15
- EventTypeMap, PluginConstructor, ChartType } from '@orbcharts/core'
16
- import { defineNoneDataPlugin } from '@orbcharts/core'
17
- import { getSvgGElementSize, appendSvg } from '../../utils/d3Utils'
18
- import { getColor, getClassName } from '../../utils/orbchartsUtils'
19
- import { TOOLTIP_PARAMS } from '../defaults'
20
- import { textSizePxObservable } from '@orbcharts/core'
21
-
22
- interface TooltipStyle {
23
- backgroundColor: string
24
- backgroundOpacity: number
25
- strokeColor: string
26
- offset: [number, number]
27
- padding: number
28
- textColor: string
29
- textSize: number | string // chartParams上的設定
30
- textSizePx: number
31
- }
32
-
33
- const pluginName = 'Tooltip'
34
- const gClassName = getClassName(pluginName, 'g')
35
- const boxClassName = getClassName(pluginName, 'box')
36
-
37
- function textToSvg (_textArr: string[] | string | null | undefined, textStyle: TooltipStyle) {
38
- const lineHeight = textStyle.textSizePx * 1.5
39
-
40
- const textArr = _textArr == null
41
- ? []
42
- : Array.isArray(_textArr)
43
- ? _textArr
44
- : typeof _textArr === 'string'
45
- ? _textArr.split('\n')
46
- : [_textArr]
47
-
48
- const tspan = textArr
49
- .filter(d => d != '')
50
- .map((text, i) => {
51
- const top = i * lineHeight
52
- return `<tspan x="0" y="${top}">${text}</tspan>`
53
- })
54
- .join('')
55
-
56
- if (tspan) {
57
- return `<text font-size="${textStyle.textSize}" fill="${textStyle.textColor}" x="0" y="0" style="dominant-baseline:text-before-edge">
58
- ${tspan}
59
- </text>`
60
- } else {
61
- return ''
62
- }
63
- }
64
-
65
- function renderTooltip ({ rootSelection, pluginName, rootWidth, rootHeight, svgString, tooltipStyle, event }: {
66
- rootSelection: d3.Selection<any, unknown, any, unknown>
67
- pluginName: string
68
- rootWidth: number
69
- rootHeight: number
70
- svgString: string
71
- tooltipStyle: TooltipStyle
72
- event: MouseEvent
73
- }) {
74
- // if (!svgString) {
75
- // return
76
- // }
77
- // const rootSelection = d3.select('svg.bpcharts__root')
78
- // console.log('tooltip', { selection, rootWidth, rootHeight, svgString, event })
79
- rootSelection.interrupt('fadeout')
80
- const radius = 5
81
-
82
- // data(svg string無值則為空陣列)
83
- const contentData = svgString ? [svgString] : []
84
- const styleData = svgString ? [tooltipStyle] : []
85
- // tooltipG <g>
86
- const tooltipG = rootSelection
87
- .selectAll<SVGGElement, string>(`g.${gClassName}`)
88
- .data(contentData)
89
- .join(
90
- enter => {
91
- return enter
92
- .append('g')
93
- .classed(gClassName, true)
94
- .attr('pointer-events', 'none')
95
- },
96
- update => {
97
- return update
98
- },
99
- exit => {
100
- return exit
101
- // .transition('fadeout')
102
- // .duration(0)
103
- // .delay(500)
104
- .style('opacity', 0)
105
- .remove()
106
- }
107
- )
108
- .attr('transform', () => `translate(${event.offsetX}, ${event.offsetY})`)
109
-
110
- // tooltipBox <g><g>
111
- const tooltipBox = tooltipG
112
- .selectAll<SVGGElement, string>(`g.${boxClassName}`)
113
- .data(styleData)
114
- .join(
115
- enter => {
116
- return enter
117
- .append('g')
118
- .classed(getClassName(pluginName, 'box'), true)
119
- },
120
- )
121
-
122
- // rect <g><g><rect>
123
- const rect = tooltipBox
124
- .selectAll<SVGRectElement, string>('rect')
125
- .data(styleData)
126
- .join(
127
- enter => {
128
- return enter
129
- .append('rect')
130
- .attr('rx', radius)
131
- .attr('ry', radius)
132
- }
133
- )
134
- .attr('fill', d => d.backgroundColor)
135
- .attr('stroke', d => d.strokeColor)
136
- .attr('opacity', d => d.backgroundOpacity)
137
-
138
- // text <g><g><g>
139
- const contentG = tooltipBox
140
- .selectAll<SVGGElement, string>('g')
141
- .data(contentData)
142
- .join(
143
- enter => {
144
- return enter
145
- .append('g')
146
- .classed(getClassName(pluginName, 'content'), true)
147
- .attr('transform', () => `translate(${tooltipStyle.padding}, ${tooltipStyle.padding})`)
148
- }
149
- )
150
- // 使用字串加入svg
151
- if (contentData.length) {
152
- appendSvg(contentG, contentData[0])
153
- }
154
- const contentSize = (contentG?.node()) ? getSvgGElementSize(contentG!) : { width: 0, height: 0 }
155
-
156
- // rect size
157
- rect
158
- .attr('width', contentSize.width + tooltipStyle.padding * 2)
159
- .attr('height', contentSize.height + tooltipStyle.padding * 2)
160
-
161
- // -- tooltipG --
162
- // 取得tooltip <g>的尺寸
163
- const tooltipSize = (tooltipBox?.node()) ? getSvgGElementSize(tooltipBox!) : { width: 0, height: 0 }
164
- // const minX = 0
165
- const maxX = rootWidth - tooltipSize.width
166
- // const minY = 0
167
- const maxY = rootHeight - tooltipSize.height
168
-
169
- // -- 相對游標位置的offset --
170
- const offsetX = (event.offsetX + tooltipStyle.offset[0]) > maxX ? maxX - event.offsetX : tooltipStyle.offset[0]
171
- const offsetY = (event.offsetY + tooltipStyle.offset[1]) > maxY ? maxY - event.offsetY : tooltipStyle.offset[1]
172
- tooltipBox.attr('transform', d => `translate(${offsetX}, ${offsetY})`)
173
- tooltipBox.attr('transform', d => `translate(${offsetX}, ${offsetY})`)
174
-
175
-
176
-
177
- // const minX = containerSize.x
178
- // const maxX = containerSize.x + containerSize.width - tooltipSize.width
179
- // const minY = containerSize.y
180
- // const maxY = containerSize.y + containerSize.height - tooltipSize.height
181
-
182
-
183
- // .style('position', 'absolute')
184
- // .style('z-index', 10000)
185
- // .style('left', (d) => {
186
- // const x = d.x + this.fullParams.offsetX! - containerSize.x
187
- // if (x < minX) {
188
- // return `${minX}px`
189
- // } else if (x > maxX) {
190
- // return `${maxX}px`
191
- // }
192
- // return `${x}px`
193
- // })
194
- // .style('top', (d) => {
195
- // const y = d.y + this.fullParams.offsetY! - containerSize.y
196
- // if (y < minY) {
197
- // return `${minY}px`
198
- // } else if (y > maxY) {
199
- // return `${maxY}px`
200
- // }
201
- // return `${y}px`
202
- // })
203
- // .html((d) => d.contentHtml)
204
-
205
-
206
- }
207
-
208
- export const Tooltip: PluginConstructor<any, string, any> = defineNoneDataPlugin(pluginName, TOOLTIP_PARAMS)(({ selection, rootSelection, name, chartType, observer, subject }) => {
209
- const destroy$ = new Subject()
210
-
211
- // 事件觸發
212
- const eventMouseover$: Observable<EventTypeMap<any>> = subject.event$.pipe(
213
- takeUntil(destroy$),
214
- filter(d => d.eventName === 'mouseover' || d.eventName === 'mousemove'),
215
- // distinctUntilChanged((prev, current) => prev.eventName === current.eventName)
216
- // map(d => d as EventTypeMap<typeof chartType>)
217
- )
218
- const eventMouseout$: Observable<EventTypeMap<any>> = subject.event$.pipe(
219
- takeUntil(destroy$),
220
- filter(d => d.eventName === 'mouseout'),
221
- // distinctUntilChanged((prev, current) => prev.eventName === current.eventName)
222
- // map(d => d as EventTypeMap<typeof chartType>)
223
- )
224
-
225
- const textSizePx$ = textSizePxObservable(observer.fullChartParams$)
226
-
227
- const tooltipStyle$: Observable<TooltipStyle> = combineLatest({
228
- fullChartParams: observer.fullChartParams$,
229
- fullParams: observer.fullParams$,
230
- textSizePx: textSizePx$
231
- }).pipe(
232
- takeUntil(destroy$),
233
- switchMap(async d => d),
234
- map(data => {
235
- return {
236
- backgroundColor: getColor(data.fullParams.backgroundColorType, data.fullChartParams),
237
- backgroundOpacity: data.fullParams.backgroundOpacity,
238
- strokeColor: getColor(data.fullParams.strokeColorType, data.fullChartParams),
239
- offset: data.fullParams.offset,
240
- padding: data.fullParams.padding,
241
- textSize: data.fullChartParams.styles.textSize,
242
- textSizePx: data.textSizePx,
243
- textColor: getColor(data.fullParams.textColorType, data.fullChartParams),
244
- }
245
- })
246
- )
247
-
248
- const contentRenderFn$: Observable<((eventData: EventTypeMap<any>) => string)> = combineLatest({
249
- fullParams: observer.fullParams$,
250
- tooltipStyle: tooltipStyle$
251
- }).pipe(
252
- takeUntil(destroy$),
253
- switchMap(async d => d),
254
- map(data => {
255
- if (data.fullParams.svgRenderFn) {
256
- return data.fullParams.svgRenderFn
257
- }
258
- // 將textRenderFn回傳的資料使用<text>包裝起來
259
- return (eventData: EventTypeMap<any>) => {
260
- const textArr = data.fullParams.textRenderFn(eventData as any)
261
- return textToSvg(textArr, data.tooltipStyle)
262
- }
263
- })
264
- )
265
-
266
- const mouseoverTooltipSvg$ = combineLatest({
267
- event: eventMouseover$,
268
- contentRenderFn: contentRenderFn$
269
- }).pipe(
270
- takeUntil(destroy$),
271
- switchMap(async d => d),
272
- map(d => d.contentRenderFn(d.event))
273
- )
274
-
275
- const mouseoutTooltipSvg$ = eventMouseout$.pipe(
276
- takeUntil(destroy$),
277
- map(d => '')
278
- )
279
-
280
- const event$ = merge(eventMouseover$, eventMouseout$)
281
- .pipe(
282
- takeUntil(destroy$),
283
- // filter(d => {
284
- // return (d.eventName === 'mouseover' || d.eventName === 'mousemove' || d.eventName === 'mouseout')
285
- // && d.event != undefined
286
- // }),
287
- map(d => d.event!),
288
- )
289
-
290
- combineLatest({
291
- svgString: merge(mouseoverTooltipSvg$, mouseoutTooltipSvg$),
292
- event: event$,
293
- layout: observer.layout$,
294
- tooltipStyle: tooltipStyle$,
295
- }).pipe(
296
- takeUntil(destroy$),
297
- switchMap(async d => d),
298
- ).subscribe(data => {
299
- // console.log('svgString', data.svgString)
300
- renderTooltip({
301
- rootSelection,
302
- pluginName: name,
303
- rootWidth: data.layout.rootWidth,
304
- rootHeight: data.layout.rootHeight,
305
- svgString: data.svgString,
306
- tooltipStyle: data.tooltipStyle,
307
- event: data.event
308
- })
309
- })
310
-
311
-
312
-
313
- // const chartType$ = eventMouseover$.pipe(
314
- // takeUntil(destroy$),
315
- // map(d => d.type),
316
- // distinctUntilChanged()
317
- // )
318
-
319
-
320
- // eventMouseover$.subscribe(event => {
321
-
322
- // })
323
-
324
-
325
- return function unsubscribe () {
326
- destroy$.next(undefined)
327
- }
1
+ import * as d3 from 'd3'
2
+ import {
3
+ combineLatest,
4
+ map,
5
+ merge,
6
+ filter,
7
+ switchMap,
8
+ first,
9
+ iif,
10
+ takeUntil,
11
+ Subject,
12
+ Observable,
13
+ distinctUntilChanged } from 'rxjs'
14
+ import type {
15
+ EventTypeMap, PluginConstructor, ChartType } from '@orbcharts/core'
16
+ import { defineNoneDataPlugin } from '@orbcharts/core'
17
+ import { getSvgGElementSize, appendSvg } from '../../utils/d3Utils'
18
+ import { getColor, getClassName } from '../../utils/orbchartsUtils'
19
+ import { TOOLTIP_PARAMS } from '../defaults'
20
+ import { textSizePxObservable } from '@orbcharts/core'
21
+
22
+ interface TooltipStyle {
23
+ backgroundColor: string
24
+ backgroundOpacity: number
25
+ strokeColor: string
26
+ offset: [number, number]
27
+ padding: number
28
+ textColor: string
29
+ textSize: number | string // chartParams上的設定
30
+ textSizePx: number
31
+ }
32
+
33
+ const pluginName = 'Tooltip'
34
+ const gClassName = getClassName(pluginName, 'g')
35
+ const boxClassName = getClassName(pluginName, 'box')
36
+
37
+ function textToSvg (_textArr: string[] | string | null | undefined, textStyle: TooltipStyle) {
38
+ const lineHeight = textStyle.textSizePx * 1.5
39
+
40
+ const textArr = _textArr == null
41
+ ? []
42
+ : Array.isArray(_textArr)
43
+ ? _textArr
44
+ : typeof _textArr === 'string'
45
+ ? _textArr.split('\n')
46
+ : [_textArr]
47
+
48
+ const tspan = textArr
49
+ .filter(d => d != '')
50
+ .map((text, i) => {
51
+ const top = i * lineHeight
52
+ return `<tspan x="0" y="${top}">${text}</tspan>`
53
+ })
54
+ .join('')
55
+
56
+ if (tspan) {
57
+ return `<text font-size="${textStyle.textSize}" fill="${textStyle.textColor}" x="0" y="0" style="dominant-baseline:text-before-edge">
58
+ ${tspan}
59
+ </text>`
60
+ } else {
61
+ return ''
62
+ }
63
+ }
64
+
65
+ function renderTooltip ({ rootSelection, pluginName, rootWidth, rootHeight, svgString, tooltipStyle, event }: {
66
+ rootSelection: d3.Selection<any, unknown, any, unknown>
67
+ pluginName: string
68
+ rootWidth: number
69
+ rootHeight: number
70
+ svgString: string
71
+ tooltipStyle: TooltipStyle
72
+ event: MouseEvent
73
+ }) {
74
+ // if (!svgString) {
75
+ // return
76
+ // }
77
+ // const rootSelection = d3.select('svg.bpcharts__root')
78
+ // console.log('tooltip', { selection, rootWidth, rootHeight, svgString, event })
79
+ rootSelection.interrupt('fadeout')
80
+ const radius = 5
81
+
82
+ // data(svg string無值則為空陣列)
83
+ const contentData = svgString ? [svgString] : []
84
+ const styleData = svgString ? [tooltipStyle] : []
85
+ // tooltipG <g>
86
+ const tooltipG = rootSelection
87
+ .selectAll<SVGGElement, string>(`g.${gClassName}`)
88
+ .data(contentData)
89
+ .join(
90
+ enter => {
91
+ return enter
92
+ .append('g')
93
+ .classed(gClassName, true)
94
+ .attr('pointer-events', 'none')
95
+ },
96
+ update => {
97
+ return update
98
+ },
99
+ exit => {
100
+ return exit
101
+ // .transition('fadeout')
102
+ // .duration(0)
103
+ // .delay(500)
104
+ .style('opacity', 0)
105
+ .remove()
106
+ }
107
+ )
108
+ .attr('transform', () => `translate(${event.offsetX}, ${event.offsetY})`)
109
+
110
+ // tooltipBox <g><g>
111
+ const tooltipBox = tooltipG
112
+ .selectAll<SVGGElement, string>(`g.${boxClassName}`)
113
+ .data(styleData)
114
+ .join(
115
+ enter => {
116
+ return enter
117
+ .append('g')
118
+ .classed(getClassName(pluginName, 'box'), true)
119
+ },
120
+ )
121
+
122
+ // rect <g><g><rect>
123
+ const rect = tooltipBox
124
+ .selectAll<SVGRectElement, string>('rect')
125
+ .data(styleData)
126
+ .join(
127
+ enter => {
128
+ return enter
129
+ .append('rect')
130
+ .attr('rx', radius)
131
+ .attr('ry', radius)
132
+ }
133
+ )
134
+ .attr('fill', d => d.backgroundColor)
135
+ .attr('stroke', d => d.strokeColor)
136
+ .attr('opacity', d => d.backgroundOpacity)
137
+
138
+ // text <g><g><g>
139
+ const contentG = tooltipBox
140
+ .selectAll<SVGGElement, string>('g')
141
+ .data(contentData)
142
+ .join(
143
+ enter => {
144
+ return enter
145
+ .append('g')
146
+ .classed(getClassName(pluginName, 'content'), true)
147
+ .attr('transform', () => `translate(${tooltipStyle.padding}, ${tooltipStyle.padding})`)
148
+ }
149
+ )
150
+ // 使用字串加入svg
151
+ if (contentData.length) {
152
+ appendSvg(contentG, contentData[0])
153
+ }
154
+ const contentSize = (contentG?.node()) ? getSvgGElementSize(contentG!) : { width: 0, height: 0 }
155
+
156
+ // rect size
157
+ rect
158
+ .attr('width', contentSize.width + tooltipStyle.padding * 2)
159
+ .attr('height', contentSize.height + tooltipStyle.padding * 2)
160
+
161
+ // -- tooltipG --
162
+ // 取得tooltip <g>的尺寸
163
+ const tooltipSize = (tooltipBox?.node()) ? getSvgGElementSize(tooltipBox!) : { width: 0, height: 0 }
164
+ // const minX = 0
165
+ const maxX = rootWidth - tooltipSize.width
166
+ // const minY = 0
167
+ const maxY = rootHeight - tooltipSize.height
168
+
169
+ // -- 相對游標位置的offset --
170
+ const offsetX = (event.offsetX + tooltipStyle.offset[0]) > maxX ? maxX - event.offsetX : tooltipStyle.offset[0]
171
+ const offsetY = (event.offsetY + tooltipStyle.offset[1]) > maxY ? maxY - event.offsetY : tooltipStyle.offset[1]
172
+ tooltipBox.attr('transform', d => `translate(${offsetX}, ${offsetY})`)
173
+ tooltipBox.attr('transform', d => `translate(${offsetX}, ${offsetY})`)
174
+
175
+
176
+
177
+ // const minX = containerSize.x
178
+ // const maxX = containerSize.x + containerSize.width - tooltipSize.width
179
+ // const minY = containerSize.y
180
+ // const maxY = containerSize.y + containerSize.height - tooltipSize.height
181
+
182
+
183
+ // .style('position', 'absolute')
184
+ // .style('z-index', 10000)
185
+ // .style('left', (d) => {
186
+ // const x = d.x + this.fullParams.offsetX! - containerSize.x
187
+ // if (x < minX) {
188
+ // return `${minX}px`
189
+ // } else if (x > maxX) {
190
+ // return `${maxX}px`
191
+ // }
192
+ // return `${x}px`
193
+ // })
194
+ // .style('top', (d) => {
195
+ // const y = d.y + this.fullParams.offsetY! - containerSize.y
196
+ // if (y < minY) {
197
+ // return `${minY}px`
198
+ // } else if (y > maxY) {
199
+ // return `${maxY}px`
200
+ // }
201
+ // return `${y}px`
202
+ // })
203
+ // .html((d) => d.contentHtml)
204
+
205
+
206
+ }
207
+
208
+ export const Tooltip: PluginConstructor<any, string, any> = defineNoneDataPlugin(pluginName, TOOLTIP_PARAMS)(({ selection, rootSelection, name, chartType, observer, subject }) => {
209
+ const destroy$ = new Subject()
210
+
211
+ // 事件觸發
212
+ const eventMouseover$: Observable<EventTypeMap<any>> = subject.event$.pipe(
213
+ takeUntil(destroy$),
214
+ filter(d => d.eventName === 'mouseover' || d.eventName === 'mousemove'),
215
+ // distinctUntilChanged((prev, current) => prev.eventName === current.eventName)
216
+ // map(d => d as EventTypeMap<typeof chartType>)
217
+ )
218
+ const eventMouseout$: Observable<EventTypeMap<any>> = subject.event$.pipe(
219
+ takeUntil(destroy$),
220
+ filter(d => d.eventName === 'mouseout'),
221
+ // distinctUntilChanged((prev, current) => prev.eventName === current.eventName)
222
+ // map(d => d as EventTypeMap<typeof chartType>)
223
+ )
224
+
225
+ const textSizePx$ = textSizePxObservable(observer.fullChartParams$)
226
+
227
+ const tooltipStyle$: Observable<TooltipStyle> = combineLatest({
228
+ fullChartParams: observer.fullChartParams$,
229
+ fullParams: observer.fullParams$,
230
+ textSizePx: textSizePx$
231
+ }).pipe(
232
+ takeUntil(destroy$),
233
+ switchMap(async d => d),
234
+ map(data => {
235
+ return {
236
+ backgroundColor: getColor(data.fullParams.backgroundColorType, data.fullChartParams),
237
+ backgroundOpacity: data.fullParams.backgroundOpacity,
238
+ strokeColor: getColor(data.fullParams.strokeColorType, data.fullChartParams),
239
+ offset: data.fullParams.offset,
240
+ padding: data.fullParams.padding,
241
+ textSize: data.fullChartParams.styles.textSize,
242
+ textSizePx: data.textSizePx,
243
+ textColor: getColor(data.fullParams.textColorType, data.fullChartParams),
244
+ }
245
+ })
246
+ )
247
+
248
+ const contentRenderFn$: Observable<((eventData: EventTypeMap<any>) => string)> = combineLatest({
249
+ fullParams: observer.fullParams$,
250
+ tooltipStyle: tooltipStyle$
251
+ }).pipe(
252
+ takeUntil(destroy$),
253
+ switchMap(async d => d),
254
+ map(data => {
255
+ if (data.fullParams.svgRenderFn) {
256
+ return data.fullParams.svgRenderFn
257
+ }
258
+ // 將textRenderFn回傳的資料使用<text>包裝起來
259
+ return (eventData: EventTypeMap<any>) => {
260
+ const textArr = data.fullParams.textRenderFn(eventData as any)
261
+ return textToSvg(textArr, data.tooltipStyle)
262
+ }
263
+ })
264
+ )
265
+
266
+ const mouseoverTooltipSvg$ = combineLatest({
267
+ event: eventMouseover$,
268
+ contentRenderFn: contentRenderFn$
269
+ }).pipe(
270
+ takeUntil(destroy$),
271
+ switchMap(async d => d),
272
+ map(d => d.contentRenderFn(d.event))
273
+ )
274
+
275
+ const mouseoutTooltipSvg$ = eventMouseout$.pipe(
276
+ takeUntil(destroy$),
277
+ map(d => '')
278
+ )
279
+
280
+ const event$ = merge(eventMouseover$, eventMouseout$)
281
+ .pipe(
282
+ takeUntil(destroy$),
283
+ // filter(d => {
284
+ // return (d.eventName === 'mouseover' || d.eventName === 'mousemove' || d.eventName === 'mouseout')
285
+ // && d.event != undefined
286
+ // }),
287
+ map(d => d.event!),
288
+ )
289
+
290
+ combineLatest({
291
+ svgString: merge(mouseoverTooltipSvg$, mouseoutTooltipSvg$),
292
+ event: event$,
293
+ layout: observer.layout$,
294
+ tooltipStyle: tooltipStyle$,
295
+ }).pipe(
296
+ takeUntil(destroy$),
297
+ switchMap(async d => d),
298
+ ).subscribe(data => {
299
+ // console.log('svgString', data.svgString)
300
+ renderTooltip({
301
+ rootSelection,
302
+ pluginName: name,
303
+ rootWidth: data.layout.rootWidth,
304
+ rootHeight: data.layout.rootHeight,
305
+ svgString: data.svgString,
306
+ tooltipStyle: data.tooltipStyle,
307
+ event: data.event
308
+ })
309
+ })
310
+
311
+
312
+
313
+ // const chartType$ = eventMouseover$.pipe(
314
+ // takeUntil(destroy$),
315
+ // map(d => d.type),
316
+ // distinctUntilChanged()
317
+ // )
318
+
319
+
320
+ // eventMouseover$.subscribe(event => {
321
+
322
+ // })
323
+
324
+
325
+ return function unsubscribe () {
326
+ destroy$.next(undefined)
327
+ }
328
328
  })