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

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 (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
  })