@orbcharts/plugins-basic 3.0.0-beta.3 → 3.0.0-beta.5

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 (147) hide show
  1. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseBarStack.d.ts +2 -2
  2. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseBars.d.ts +2 -2
  3. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseBarsTriangle.d.ts +2 -2
  4. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseDots.d.ts +2 -2
  5. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseGroupAxis.d.ts +2 -2
  6. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseLegend.d.ts +1 -1
  7. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseLineAreas.d.ts +3 -3
  8. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseLines.d.ts +3 -3
  9. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseValueAxis.d.ts +3 -2
  10. package/dist/{src → orbcharts-plugins-basic/src}/grid/defaults.d.ts +1 -1
  11. package/dist/{src → orbcharts-plugins-basic/src}/grid/gridObservables.d.ts +5 -5
  12. package/dist/orbcharts-plugins-basic/src/index.d.ts +6 -0
  13. package/dist/orbcharts-plugins-basic/src/multiValue/defaults.d.ts +9 -0
  14. package/dist/orbcharts-plugins-basic/src/multiValue/index.d.ts +8 -0
  15. package/dist/orbcharts-plugins-basic/src/multiValue/multiValueObservables.d.ts +33 -0
  16. package/dist/orbcharts-plugins-basic/src/multiValue/plugins/MultiValueLegend.d.ts +1 -0
  17. package/dist/orbcharts-plugins-basic/src/multiValue/plugins/MultiValueTooltip.d.ts +1 -0
  18. package/dist/orbcharts-plugins-basic/src/multiValue/plugins/Scatter.d.ts +3 -0
  19. package/dist/orbcharts-plugins-basic/src/multiValue/plugins/ScatterBubbles.d.ts +3 -0
  20. package/dist/orbcharts-plugins-basic/src/multiValue/plugins/XYAux.d.ts +3 -0
  21. package/dist/orbcharts-plugins-basic/src/multiValue/plugins/XYAxes.d.ts +3 -0
  22. package/dist/orbcharts-plugins-basic/src/multiValue/plugins/XYZoom.d.ts +1 -0
  23. package/dist/{src → orbcharts-plugins-basic/src}/series/seriesObservables.d.ts +3 -3
  24. package/dist/{src → orbcharts-plugins-basic/src}/utils/observables.d.ts +1 -1
  25. package/dist/{src → orbcharts-plugins-basic/src}/utils/orbchartsUtils.d.ts +3 -3
  26. package/dist/orbcharts-plugins-basic.es.js +11809 -10185
  27. package/dist/orbcharts-plugins-basic.umd.js +66 -34
  28. package/dist/src/index.d.ts +1 -5
  29. package/package.json +4 -4
  30. package/src/base/BaseBarStack.ts +2 -2
  31. package/src/base/BaseBars.ts +2 -2
  32. package/src/base/BaseBarsTriangle.ts +2 -2
  33. package/src/base/BaseDots.ts +2 -53
  34. package/src/base/BaseGroupAxis.ts +3 -3
  35. package/src/base/BaseLegend.ts +21 -17
  36. package/src/base/BaseLineAreas.ts +3 -3
  37. package/src/base/BaseLines.ts +3 -3
  38. package/src/base/BaseValueAxis.ts +46 -43
  39. package/src/grid/defaults.ts +3 -3
  40. package/src/grid/gridObservables.ts +24 -15
  41. package/src/grid/plugins/GridLegend.ts +2 -2
  42. package/src/grid/plugins/GridZoom.ts +14 -14
  43. package/src/grid/plugins/GroupAux.ts +206 -201
  44. package/src/grid/plugins/ValueAxis.ts +1 -0
  45. package/src/grid/plugins/ValueStackAxis.ts +1 -0
  46. package/src/index.ts +1 -0
  47. package/src/multiGrid/plugins/MultiGridLegend.ts +2 -2
  48. package/src/multiGrid/plugins/MultiValueAxis.ts +1 -0
  49. package/src/multiGrid/plugins/MultiValueStackAxis.ts +1 -0
  50. package/src/multiGrid/plugins/OverlappingValueAxes.ts +1 -0
  51. package/src/multiGrid/plugins/OverlappingValueStackAxes.ts +1 -0
  52. package/src/multiValue/defaults.ts +163 -0
  53. package/src/multiValue/index.ts +9 -0
  54. package/src/multiValue/multiValueObservables.ts +258 -0
  55. package/src/multiValue/plugins/MultiValueLegend.ts +107 -0
  56. package/src/multiValue/plugins/MultiValueTooltip.ts +66 -0
  57. package/src/multiValue/plugins/Scatter.ts +426 -0
  58. package/src/multiValue/plugins/ScatterBubbles.ts +551 -0
  59. package/src/multiValue/plugins/XYAux.ts +682 -0
  60. package/src/multiValue/plugins/XYAxes.ts +677 -0
  61. package/src/multiValue/plugins/XYZoom.ts +300 -0
  62. package/src/series/plugins/Bubbles.ts +4 -4
  63. package/src/series/plugins/Pie.ts +2 -2
  64. package/src/series/plugins/PieEventTexts.ts +2 -2
  65. package/src/series/plugins/PieLabels.ts +2 -2
  66. package/src/series/plugins/Rose.ts +2 -2
  67. package/src/series/plugins/RoseLabels.ts +2 -2
  68. package/src/series/plugins/SeriesLegend.ts +4 -4
  69. package/src/series/seriesObservables.ts +3 -3
  70. package/src/tree/plugins/TreeLegend.ts +3 -10
  71. package/src/utils/d3Utils.ts +2 -1
  72. package/src/utils/observables.ts +2 -2
  73. package/src/utils/orbchartsUtils.ts +7 -6
  74. package/dist/src/base/BaseGroupArea.d.ts +0 -0
  75. package/dist/src/multiValue/plugins/Scatter.d.ts +0 -0
  76. package/dist/src/multiValue/plugins/ScatterAxes.d.ts +0 -0
  77. package/dist/src/relationship/index.d.ts +0 -0
  78. package/src/base/BaseGroupArea.ts +0 -0
  79. package/src/multiValue/plugins/ScatterAxes.ts +0 -0
  80. /package/dist/{lib → orbcharts-plugins-basic/lib}/core-types.d.ts +0 -0
  81. /package/dist/{lib → orbcharts-plugins-basic/lib}/core.d.ts +0 -0
  82. /package/dist/{lib → orbcharts-plugins-basic/lib}/plugins-basic-types.d.ts +0 -0
  83. /package/dist/{src → orbcharts-plugins-basic/src}/base/BaseTooltip.d.ts +0 -0
  84. /package/dist/{src → orbcharts-plugins-basic/src}/base/types.d.ts +0 -0
  85. /package/dist/{src → orbcharts-plugins-basic/src}/const.d.ts +0 -0
  86. /package/dist/{src → orbcharts-plugins-basic/src}/grid/index.d.ts +0 -0
  87. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/BarStack.d.ts +0 -0
  88. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/Bars.d.ts +0 -0
  89. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/BarsPN.d.ts +0 -0
  90. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/BarsTriangle.d.ts +0 -0
  91. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/Dots.d.ts +0 -0
  92. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/GridLegend.d.ts +0 -0
  93. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/GridTooltip.d.ts +0 -0
  94. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/GridZoom.d.ts +0 -0
  95. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/GroupAux.d.ts +0 -0
  96. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/GroupAxis.d.ts +0 -0
  97. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/LineAreas.d.ts +0 -0
  98. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/Lines.d.ts +0 -0
  99. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/ValueAxis.d.ts +0 -0
  100. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/ValueStackAxis.d.ts +0 -0
  101. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/defaults.d.ts +0 -0
  102. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/index.d.ts +0 -0
  103. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/multiGridObservables.d.ts +0 -0
  104. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiBarStack.d.ts +0 -0
  105. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiBars.d.ts +0 -0
  106. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiBarsTriangle.d.ts +0 -0
  107. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiDots.d.ts +0 -0
  108. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiGridLegend.d.ts +0 -0
  109. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiGridTooltip.d.ts +0 -0
  110. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiGroupAxis.d.ts +0 -0
  111. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiLineAreas.d.ts +0 -0
  112. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiLines.d.ts +0 -0
  113. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiValueAxis.d.ts +0 -0
  114. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiValueStackAxis.d.ts +0 -0
  115. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/OverlappingValueAxes.d.ts +0 -0
  116. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/OverlappingValueStackAxes.d.ts +0 -0
  117. /package/dist/{src/grid → orbcharts-plugins-basic/src/multiValue}/plugins/Ranking.d.ts +0 -0
  118. /package/dist/{src/grid → orbcharts-plugins-basic/src/multiValue}/plugins/RankingAxis.d.ts +0 -0
  119. /package/dist/{src → orbcharts-plugins-basic/src}/noneData/defaults.d.ts +0 -0
  120. /package/dist/{src → orbcharts-plugins-basic/src}/noneData/index.d.ts +0 -0
  121. /package/dist/{src → orbcharts-plugins-basic/src}/noneData/plugins/Container.d.ts +0 -0
  122. /package/dist/{src → orbcharts-plugins-basic/src}/noneData/plugins/Tooltip.d.ts +0 -0
  123. /package/dist/{src/multiValue → orbcharts-plugins-basic/src/relationship}/index.d.ts +0 -0
  124. /package/dist/{src → orbcharts-plugins-basic/src}/relationship/plugins/Relationship.d.ts +0 -0
  125. /package/dist/{src → orbcharts-plugins-basic/src}/series/defaults.d.ts +0 -0
  126. /package/dist/{src → orbcharts-plugins-basic/src}/series/index.d.ts +0 -0
  127. /package/dist/{src → orbcharts-plugins-basic/src}/series/plugins/Bubbles.d.ts +0 -0
  128. /package/dist/{src → orbcharts-plugins-basic/src}/series/plugins/Pie.d.ts +0 -0
  129. /package/dist/{src → orbcharts-plugins-basic/src}/series/plugins/PieEventTexts.d.ts +0 -0
  130. /package/dist/{src → orbcharts-plugins-basic/src}/series/plugins/PieLabels.d.ts +0 -0
  131. /package/dist/{src → orbcharts-plugins-basic/src}/series/plugins/Rose.d.ts +0 -0
  132. /package/dist/{src → orbcharts-plugins-basic/src}/series/plugins/RoseLabels.d.ts +0 -0
  133. /package/dist/{src → orbcharts-plugins-basic/src}/series/plugins/SeriesLegend.d.ts +0 -0
  134. /package/dist/{src → orbcharts-plugins-basic/src}/series/plugins/SeriesTooltip.d.ts +0 -0
  135. /package/dist/{src → orbcharts-plugins-basic/src}/series/plugins/Waffle.d.ts +0 -0
  136. /package/dist/{src → orbcharts-plugins-basic/src}/series/seriesUtils.d.ts +0 -0
  137. /package/dist/{src → orbcharts-plugins-basic/src}/tree/defaults.d.ts +0 -0
  138. /package/dist/{src → orbcharts-plugins-basic/src}/tree/index.d.ts +0 -0
  139. /package/dist/{src → orbcharts-plugins-basic/src}/tree/plugins/TreeLegend.d.ts +0 -0
  140. /package/dist/{src → orbcharts-plugins-basic/src}/tree/plugins/TreeMap.d.ts +0 -0
  141. /package/dist/{src → orbcharts-plugins-basic/src}/tree/plugins/TreeTooltip.d.ts +0 -0
  142. /package/dist/{src → orbcharts-plugins-basic/src}/utils/commonUtils.d.ts +0 -0
  143. /package/dist/{src → orbcharts-plugins-basic/src}/utils/d3Graphics.d.ts +0 -0
  144. /package/dist/{src → orbcharts-plugins-basic/src}/utils/d3Utils.d.ts +0 -0
  145. /package/dist/{vite.config.d.ts → orbcharts-plugins-basic/vite.config.d.ts} +0 -0
  146. /package/src/{grid → multiValue}/plugins/Ranking.ts +0 -0
  147. /package/src/{grid → multiValue}/plugins/RankingAxis.ts +0 -0
@@ -0,0 +1,677 @@
1
+ import * as d3 from 'd3'
2
+ import {
3
+ Observable,
4
+ Subject,
5
+ combineLatest,
6
+ takeUntil,
7
+ map,
8
+ distinctUntilChanged,
9
+ switchMap,
10
+ shareReplay
11
+ } from 'rxjs'
12
+ import type {
13
+ ColorType,
14
+ ChartParams,
15
+ ComputedDatumMultiValue,
16
+ DataFormatterMultiValue,
17
+ DefinePluginConfig,
18
+ } from '../../../lib/core-types'
19
+ import {
20
+ defineMultiValuePlugin,
21
+ createValueToAxisScale,
22
+ getMinAndMax
23
+ } from '../../../lib/core'
24
+ import type { XYAxesParams
25
+ } from '../../../lib/plugins-basic-types'
26
+ import { DEFAULT_X_Y_AXES_PARAMS } from '../defaults'
27
+ import { LAYER_INDEX_OF_AXIS } from '../../const'
28
+ import { getColor, getDatumColor, getClassName, getUniID } from '../../utils/orbchartsUtils'
29
+ import { parseTickFormatValue } from '../../utils/d3Utils'
30
+ import { filteredMinMaxXYDataObservable } from '../../../../orbcharts-core/src/utils/multiValueObservables'
31
+ // import { multiValueSelectionsObservable } from '../multiValueObservables'
32
+
33
+ interface TextAlign {
34
+ textAnchor: "start" | "middle" | "end"
35
+ dominantBaseline: "middle" | "auto" | "hanging"
36
+ }
37
+
38
+ // interface Axis {
39
+ // labelOffset: [number, number]
40
+ // labelColorType: ColorType
41
+ // axisLineVisible: boolean
42
+ // axisLineColorType: ColorType
43
+ // ticks: number | null
44
+ // tickFormat: string | ((text: d3.NumberValue) => string)
45
+ // tickLineVisible: boolean
46
+ // tickPadding: number
47
+ // tickFullLine: boolean
48
+ // tickFullLineDasharray: string
49
+ // tickColorType: ColorType
50
+ // tickTextColorType: ColorType
51
+ // }
52
+
53
+ const pluginName = 'XYAxes'
54
+
55
+ const defaultTickSize = 6
56
+
57
+ const xTickTextAnchor = 'middle'
58
+ const xTickDominantBaseline = 'hanging'
59
+ const xAxisLabelAnchor = 'start'
60
+ const xAxisLabelDominantBaseline = 'hanging'
61
+ const yTickTextAnchor = 'end'
62
+ const yTickDominantBaseline = 'middle'
63
+ const yAxisLabelAnchor = 'end'
64
+ const yAxisLabelDominantBaseline = 'auto'
65
+
66
+ const pluginConfig: DefinePluginConfig<typeof pluginName, typeof DEFAULT_X_Y_AXES_PARAMS> = {
67
+ name: pluginName,
68
+ defaultParams: DEFAULT_X_Y_AXES_PARAMS,
69
+ layerIndex: LAYER_INDEX_OF_AXIS,
70
+ validator: (params, { validateColumns }) => {
71
+ const result = validateColumns(params, {
72
+ xAxis: {
73
+ toBeTypes: ['object']
74
+ },
75
+ yAxis: {
76
+ toBeTypes: ['object']
77
+ }
78
+ })
79
+ if (params.xAxis) {
80
+ const forceResult = validateColumns(params.xAxis, {
81
+ labelOffset: {
82
+ toBe: '[number, number]',
83
+ test: (value: any) => {
84
+ return Array.isArray(value)
85
+ && value.length === 2
86
+ && typeof value[0] === 'number'
87
+ && typeof value[1] === 'number'
88
+ }
89
+ },
90
+ labelColorType: {
91
+ toBeOption: 'ColorType',
92
+ },
93
+ axisLineVisible: {
94
+ toBeTypes: ['boolean']
95
+ },
96
+ axisLineColorType: {
97
+ toBeOption: 'ColorType',
98
+ },
99
+ ticks: {
100
+ toBeTypes: ['number', 'null']
101
+ },
102
+ tickFormat: {
103
+ toBeTypes: ['string', 'Function']
104
+ },
105
+ tickLineVisible: {
106
+ toBeTypes: ['boolean']
107
+ },
108
+ tickPadding: {
109
+ toBeTypes: ['number']
110
+ },
111
+ tickFullLine: {
112
+ toBeTypes: ['boolean']
113
+ },
114
+ tickFullLineDasharray: {
115
+ toBeTypes: ['string']
116
+ },
117
+ tickColorType: {
118
+ toBeOption: 'ColorType',
119
+ },
120
+ tickTextColorType: {
121
+ toBeOption: 'ColorType',
122
+ }
123
+ })
124
+ if (forceResult.status === 'error') {
125
+ return forceResult
126
+ }
127
+ }
128
+ if (params.yAxis) {
129
+ const forceResult = validateColumns(params.yAxis, {
130
+ labelOffset: {
131
+ toBe: '[number, number]',
132
+ test: (value: any) => {
133
+ return Array.isArray(value)
134
+ && value.length === 2
135
+ && typeof value[0] === 'number'
136
+ && typeof value[1] === 'number'
137
+ }
138
+ },
139
+ labelColorType: {
140
+ toBeOption: 'ColorType',
141
+ },
142
+ axisLineVisible: {
143
+ toBeTypes: ['boolean']
144
+ },
145
+ axisLineColorType: {
146
+ toBeOption: 'ColorType',
147
+ },
148
+ ticks: {
149
+ toBeTypes: ['number', 'null']
150
+ },
151
+ tickFormat: {
152
+ toBeTypes: ['string', 'Function']
153
+ },
154
+ tickLineVisible: {
155
+ toBeTypes: ['boolean']
156
+ },
157
+ tickPadding: {
158
+ toBeTypes: ['number']
159
+ },
160
+ tickFullLine: {
161
+ toBeTypes: ['boolean']
162
+ },
163
+ tickFullLineDasharray: {
164
+ toBeTypes: ['string']
165
+ },
166
+ tickColorType: {
167
+ toBeOption: 'ColorType',
168
+ },
169
+ tickTextColorType: {
170
+ toBeOption: 'ColorType',
171
+ }
172
+ })
173
+ if (forceResult.status === 'error') {
174
+ return forceResult
175
+ }
176
+ }
177
+ return result
178
+ }
179
+ }
180
+
181
+ function renderXAxisLabel ({ selection, xLabelClassName, fullParams, layout, fullDataFormatter, fullChartParams, textReverseTransform }: {
182
+ selection: d3.Selection<SVGGElement, any, any, any>,
183
+ xLabelClassName: string
184
+ fullParams: XYAxesParams
185
+ // axisLabelAlign: TextAlign
186
+ layout: { width: number, height: number }
187
+ fullDataFormatter: DataFormatterMultiValue,
188
+ fullChartParams: ChartParams
189
+ textReverseTransform: string,
190
+ }) {
191
+ const offsetX = fullParams.xAxis.tickPadding + fullParams.xAxis.labelOffset[0]
192
+ const offsetY = fullParams.xAxis.tickPadding + fullParams.xAxis.labelOffset[1]
193
+ let labelX = offsetX
194
+ let labelY = offsetY
195
+
196
+ const axisLabelSelection = selection
197
+ .selectAll<SVGGElement, XYAxesParams>(`g.${xLabelClassName}`)
198
+ .data([fullParams])
199
+ .join('g')
200
+ .classed(xLabelClassName, true)
201
+ .each((d, i, g) => {
202
+ const text = d3.select(g[i])
203
+ .selectAll<SVGTextElement, XYAxesParams>(`text`)
204
+ .data([d])
205
+ .join(
206
+ enter => {
207
+ return enter
208
+ .append('text')
209
+ .style('font-weight', 'bold')
210
+ },
211
+ update => update,
212
+ exit => exit.remove()
213
+ )
214
+ .attr('text-anchor', xAxisLabelAnchor)
215
+ .attr('dominant-baseline', xAxisLabelDominantBaseline)
216
+ .attr('font-size', fullChartParams.styles.textSize)
217
+ .style('fill', getColor(fullParams.xAxis.labelColorType, fullChartParams))
218
+ .style('transform', textReverseTransform)
219
+ // 偏移使用 x, y 而非 transform 才不會受到外層 scale 變形影響
220
+ .attr('x', labelX)
221
+ .attr('y', labelY)
222
+ .text(d => fullDataFormatter.xAxis.label)
223
+ })
224
+ .attr('transform', d => `translate(${layout.width}, ${layout.height})`)
225
+ }
226
+
227
+ function renderYAxisLabel ({ selection, yLabelClassName, fullParams, layout, fullDataFormatter, fullChartParams, textReverseTransform }: {
228
+ selection: d3.Selection<SVGGElement, any, any, any>,
229
+ yLabelClassName: string
230
+ fullParams: XYAxesParams
231
+ // axisLabelAlign: TextAlign
232
+ layout: { width: number, height: number }
233
+ fullDataFormatter: DataFormatterMultiValue,
234
+ fullChartParams: ChartParams
235
+ textReverseTransform: string,
236
+ }) {
237
+ const offsetX = fullParams.yAxis.tickPadding - fullParams.yAxis.labelOffset[0]
238
+ const offsetY = fullParams.yAxis.tickPadding + fullParams.yAxis.labelOffset[1]
239
+ let labelX = - offsetX
240
+ let labelY = - offsetY
241
+
242
+ const axisLabelSelection = selection
243
+ .selectAll<SVGGElement, XYAxesParams>(`g.${yLabelClassName}`)
244
+ .data([fullParams])
245
+ .join('g')
246
+ .classed(yLabelClassName, true)
247
+ .each((d, i, g) => {
248
+ const text = d3.select(g[i])
249
+ .selectAll<SVGTextElement, XYAxesParams>(`text`)
250
+ .data([d])
251
+ .join(
252
+ enter => {
253
+ return enter
254
+ .append('text')
255
+ .style('font-weight', 'bold')
256
+ },
257
+ update => update,
258
+ exit => exit.remove()
259
+ )
260
+ .attr('text-anchor', yAxisLabelAnchor)
261
+ .attr('dominant-baseline', yAxisLabelDominantBaseline)
262
+ .attr('font-size', fullChartParams.styles.textSize)
263
+ .style('fill', getColor(fullParams.yAxis.labelColorType, fullChartParams))
264
+ .style('transform', textReverseTransform)
265
+ // 偏移使用 x, y 而非 transform 才不會受到外層 scale 變形影響
266
+ .attr('x', labelX)
267
+ .attr('y', labelY)
268
+ .text(d => fullDataFormatter.yAxis.label)
269
+ })
270
+ // .attr('transform', d => `translate(0, ${layout.height})`)
271
+ }
272
+
273
+ function renderXAxis ({ selection, xAxisClassName, fullParams, layout, fullDataFormatter, fullChartParams, xScale, textReverseTransform, minMaxXY }: {
274
+ selection: d3.Selection<SVGGElement, any, any, any>,
275
+ xAxisClassName: string
276
+ fullParams: XYAxesParams
277
+ // tickTextAlign: TextAlign
278
+ layout: { width: number, height: number }
279
+ fullDataFormatter: DataFormatterMultiValue,
280
+ fullChartParams: ChartParams
281
+ xScale: d3.ScaleLinear<number, number>
282
+ textReverseTransform: string,
283
+ minMaxXY: {
284
+ minX: number;
285
+ maxX: number;
286
+ minY: number;
287
+ maxY: number;
288
+ }
289
+ }) {
290
+
291
+ const xAxisSelection = selection
292
+ .selectAll<SVGGElement, XYAxesParams>(`g.${xAxisClassName}`)
293
+ .data([fullParams])
294
+ .join('g')
295
+ .classed(xAxisClassName, true)
296
+ .attr('transform', `translate(0, ${layout.height})`)
297
+
298
+ const valueLength = minMaxXY.maxY - minMaxXY.minY
299
+
300
+ // const _xScale = d3.scaleLinear()
301
+ // .domain([0, 150])
302
+ // .range([416.5, 791.349])
303
+
304
+ // 刻度文字偏移
305
+ let tickPadding = fullParams.xAxis.tickPadding
306
+
307
+ // 設定Y軸刻度
308
+ const xAxis = d3.axisBottom(xScale)
309
+ .scale(xScale)
310
+ .ticks(valueLength > fullParams.xAxis.ticks
311
+ ? fullParams.xAxis.ticks
312
+ : ((minMaxXY.minY === 0 && minMaxXY.maxY === 0)
313
+ ? 1
314
+ : Math.ceil(valueLength))) // 刻度分段數量
315
+ .tickFormat(d => parseTickFormatValue(d, fullParams.xAxis.tickFormat))
316
+ .tickSize(fullParams.xAxis.tickFullLine == true
317
+ ? -layout.height
318
+ : defaultTickSize)
319
+ .tickSizeOuter(-layout.height)
320
+ .tickPadding(tickPadding)
321
+
322
+ const xAxisEl = xAxisSelection
323
+ .transition()
324
+ .duration(100)
325
+ .call(xAxis)
326
+
327
+ xAxisEl.selectAll('line')
328
+ .style('fill', 'none')
329
+ .style('stroke', fullParams.xAxis.tickLineVisible == true ? getColor(fullParams.xAxis.tickColorType, fullChartParams) : 'none')
330
+ .style('stroke-dasharray', fullParams.xAxis.tickFullLineDasharray)
331
+ .attr('pointer-events', 'none')
332
+
333
+ xAxisEl.selectAll('path')
334
+ .style('fill', 'none')
335
+ // .style('stroke', this.fullParams.axisLineColor!)
336
+ .style('stroke', fullParams.xAxis.axisLineVisible == true ? getColor(fullParams.xAxis.axisLineColorType, fullChartParams) : 'none')
337
+ .style('shape-rendering', 'crispEdges')
338
+
339
+ // const xText = xAxisEl.selectAll('text')
340
+ const xText = xAxisSelection.selectAll('text')
341
+ // .style('font-family', 'sans-serif')
342
+ .attr('font-size', fullChartParams.styles.textSize)
343
+ .style('color', getColor(fullParams.xAxis.tickTextColorType, fullChartParams))
344
+ .attr('text-anchor', xTickTextAnchor)
345
+ .attr('dominant-baseline', xTickDominantBaseline)
346
+ .attr('dy', 0)
347
+ .attr('y', tickPadding)
348
+ xText.style('transform', textReverseTransform)
349
+
350
+ // // 抵消掉預設的偏移
351
+ // if (fullDataFormatter.grid.valueAxis.position === 'bottom' || fullDataFormatter.grid.valueAxis.position === 'top') {
352
+ // xText.attr('dy', 0)
353
+ // }
354
+
355
+ return xAxisSelection
356
+ }
357
+
358
+ function renderYAxis ({ selection, yAxisClassName, fullParams, layout, fullDataFormatter, fullChartParams, yScale, textReverseTransform, minMaxXY }: {
359
+ selection: d3.Selection<SVGGElement, any, any, any>,
360
+ yAxisClassName: string
361
+ fullParams: XYAxesParams
362
+ // tickTextAlign: TextAlign
363
+ layout: { width: number, height: number }
364
+ fullDataFormatter: DataFormatterMultiValue,
365
+ fullChartParams: ChartParams
366
+ yScale: d3.ScaleLinear<number, number>
367
+ textReverseTransform: string,
368
+ minMaxXY: {
369
+ minX: number;
370
+ maxX: number;
371
+ minY: number;
372
+ maxY: number;
373
+ }
374
+ }) {
375
+
376
+ const yAxisSelection = selection
377
+ .selectAll<SVGGElement, XYAxesParams>(`g.${yAxisClassName}`)
378
+ .data([fullParams])
379
+ .join('g')
380
+ .classed(yAxisClassName, true)
381
+
382
+ const valueLength = minMaxXY.maxY - minMaxXY.minY
383
+
384
+ // const _yScale = d3.scaleLinear()
385
+ // .domain([0, 150])
386
+ // .range([416.5, 791.349])
387
+
388
+ // 刻度文字偏移
389
+ let tickPadding = fullParams.yAxis.tickPadding
390
+
391
+ // 設定Y軸刻度
392
+ const yAxis = d3.axisLeft(yScale)
393
+ .scale(yScale)
394
+ .ticks(valueLength > fullParams.yAxis.ticks
395
+ ? fullParams.yAxis.ticks
396
+ : ((minMaxXY.minY === 0 && minMaxXY.maxY === 0)
397
+ ? 1
398
+ : Math.ceil(valueLength))) // 刻度分段數量
399
+ .tickFormat(d => parseTickFormatValue(d, fullParams.yAxis.tickFormat))
400
+ .tickSize(fullParams.yAxis.tickFullLine == true
401
+ ? -layout.width
402
+ : defaultTickSize)
403
+ .tickPadding(tickPadding)
404
+
405
+ const yAxisEl = yAxisSelection
406
+ .transition()
407
+ .duration(100)
408
+ .call(yAxis)
409
+
410
+ yAxisEl.selectAll('line')
411
+ .style('fill', 'none')
412
+ .style('stroke', fullParams.yAxis.tickLineVisible == true ? getColor(fullParams.yAxis.tickColorType, fullChartParams) : 'none')
413
+ .style('stroke-dasharray', fullParams.yAxis.tickFullLineDasharray)
414
+ .attr('pointer-events', 'none')
415
+
416
+ yAxisEl.selectAll('path')
417
+ .style('fill', 'none')
418
+ // .style('stroke', this.fullParams.axisLineColor!)
419
+ .style('stroke', fullParams.yAxis.axisLineVisible == true ? getColor(fullParams.yAxis.axisLineColorType, fullChartParams) : 'none')
420
+ .style('shape-rendering', 'crispEdges')
421
+
422
+ // const yText = yAxisEl.selectAll('text')
423
+ const yText = yAxisSelection.selectAll('text')
424
+ // .style('font-family', 'sans-serif')
425
+ .attr('font-size', fullChartParams.styles.textSize)
426
+ .style('color', getColor(fullParams.yAxis.tickTextColorType, fullChartParams))
427
+ .attr('text-anchor', yTickTextAnchor)
428
+ .attr('dominant-baseline', yTickDominantBaseline)
429
+ // .attr('dy', 0)
430
+ .attr('x', - tickPadding)
431
+ .attr('dy', 0)
432
+ yText.style('transform', textReverseTransform)
433
+
434
+ // // 抵消掉預設的偏移
435
+ // if (fullDataFormatter.grid.valueAxis.position === 'bottom' || fullDataFormatter.grid.valueAxis.position === 'top') {
436
+ // yText.attr('dy', 0)
437
+ // }
438
+
439
+ return yAxisSelection
440
+ }
441
+
442
+
443
+ export const XYAxes = defineMultiValuePlugin(pluginConfig)(({ selection, name, observer, subject }) => {
444
+
445
+ const destroy$ = new Subject()
446
+
447
+ const containerClassName = getClassName(pluginName, 'container')
448
+ const xAxisGClassName = getClassName(pluginName, 'xAxisG')
449
+ const xAxisClassName = getClassName(pluginName, 'xAxis')
450
+ const yAxisGClassName = getClassName(pluginName, 'yAxisG')
451
+ const yAxisClassName = getClassName(pluginName, 'yAxis')
452
+ const xLabelClassName = getClassName(pluginName, 'xLabel')
453
+ const yLabelClassName = getClassName(pluginName, 'yLabel')
454
+
455
+ const containerSelection$ = combineLatest({
456
+ computedData: observer.computedData$.pipe(
457
+ distinctUntilChanged((a, b) => {
458
+ // 只有當series的數量改變時,才重新計算
459
+ return a.length === b.length
460
+ }),
461
+ ),
462
+ isCategorySeprate: observer.isCategorySeprate$
463
+ }).pipe(
464
+ takeUntil(destroy$),
465
+ switchMap(async (d) => d),
466
+ map(data => {
467
+ return data.isCategorySeprate
468
+ // category分開的時候顯示各別axis
469
+ ? data.computedData
470
+ // category合併的時候只顯示第一個axis
471
+ : [data.computedData[0]]
472
+ }),
473
+ map((computedData, i) => {
474
+ return selection
475
+ .selectAll<SVGGElement, ComputedDatumMultiValue[]>(`g.${containerClassName}`)
476
+ .data(computedData, d => d[0] ? d[0].categoryIndex : i)
477
+ .join('g')
478
+ .classed(containerClassName, true)
479
+ })
480
+ )
481
+
482
+ const axisSelection$ = containerSelection$.pipe(
483
+ takeUntil(destroy$),
484
+ map((containerSelection, i) => {
485
+ return containerSelection
486
+ .selectAll<SVGGElement, ComputedDatumMultiValue[]>(`g.${yAxisGClassName}`)
487
+ .data([yAxisGClassName])
488
+ .join('g')
489
+ .classed(yAxisGClassName, true)
490
+ })
491
+ )
492
+
493
+ combineLatest({
494
+ containerSelection: containerSelection$,
495
+ gridContainerPosition: observer.multiValueContainerPosition$
496
+ }).pipe(
497
+ takeUntil(destroy$),
498
+ switchMap(async d => d)
499
+ ).subscribe(data => {
500
+ data.containerSelection
501
+ .attr('transform', (d, i) => {
502
+ const gridContainerPosition = data.gridContainerPosition[i] ?? data.gridContainerPosition[0]
503
+ const translate = gridContainerPosition.translate
504
+ const scale = gridContainerPosition.scale
505
+ return `translate(${translate[0]}, ${translate[1]}) scale(${scale[0]}, ${scale[1]})`
506
+ })
507
+ // .attr('opacity', 0)
508
+ // .transition()
509
+ // .attr('opacity', 1)
510
+ })
511
+
512
+ const textReverseTransform$ = observer.multiValueContainerPosition$.pipe(
513
+ takeUntil(destroy$),
514
+ switchMap(async (d) => d),
515
+ map(multiValueContainerPosition => {
516
+ // const axesRotateXYReverseValue = `rotateX(${data.gridAxesReverseTransform.rotateX}deg) rotateY(${data.gridAxesReverseTransform.rotateY}deg)`
517
+ // const axesRotateReverseValue = `rotate(${data.gridAxesReverseTransform.rotate}deg)`
518
+ const containerScaleReverseValue = `scale(${1 / multiValueContainerPosition[0].scale[0]}, ${1 / multiValueContainerPosition[0].scale[1]})`
519
+ // 抵消最外層scale
520
+ return `${containerScaleReverseValue}`
521
+ }),
522
+ distinctUntilChanged()
523
+ )
524
+
525
+ // const minAndMax$: Observable<[number, number]> = new Observable(subscriber => {
526
+ // combineLatest({
527
+ // fullDataFormatter: observer.fullDataFormatter$,
528
+ // computedData: observer.computedData$
529
+ // }).pipe(
530
+ // takeUntil(destroy$),
531
+ // switchMap(async (d) => d),
532
+ // ).subscribe(data => {
533
+ // const groupMin = 0
534
+ // const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
535
+ // // const groupScaleDomainMin = data.fullDataFormatter.grid.groupAxis.scaleDomain[0] === 'auto'
536
+ // // ? groupMin - data.fullDataFormatter.grid.groupAxis.scalePadding
537
+ // // : data.fullDataFormatter.grid.groupAxis.scaleDomain[0] as number - data.fullDataFormatter.grid.groupAxis.scalePadding
538
+ // const groupScaleDomainMin = data.fullDataFormatter.grid.groupAxis.scaleDomain[0] - data.fullDataFormatter.grid.groupAxis.scalePadding
539
+ // const groupScaleDomainMax = data.fullDataFormatter.grid.groupAxis.scaleDomain[1] === 'max'
540
+ // ? groupMax + data.fullDataFormatter.grid.groupAxis.scalePadding
541
+ // : data.fullDataFormatter.grid.groupAxis.scaleDomain[1] as number + data.fullDataFormatter.grid.groupAxis.scalePadding
542
+
543
+ // const filteredData = data.computedData.map((d, i) => {
544
+ // return d.filter((_d, _i) => {
545
+ // return _i >= groupScaleDomainMin && _i <= groupScaleDomainMax
546
+ // })
547
+ // })
548
+
549
+ // const filteredMinAndMax = getMinAndMax(filteredData.flat().map(d => d.value[1]))
550
+ // if (filteredMinAndMax[0] === filteredMinAndMax[1]) {
551
+ // filteredMinAndMax[0] = filteredMinAndMax[1] - 1 // 避免最大及最小值相同造成無法計算scale
552
+ // }
553
+ // subscriber.next(filteredMinAndMax)
554
+ // })
555
+ // })
556
+
557
+ const xScale$: Observable<d3.ScaleLinear<number, number>> = new Observable(subscriber => {
558
+ combineLatest({
559
+ fullDataFormatter: observer.fullDataFormatter$,
560
+ layout: observer.layout$,
561
+ // minMaxXY: observer.minMaxXY$
562
+ filteredMinMaxXYData: observer.filteredMinMaxXYData$
563
+ }).pipe(
564
+ takeUntil(destroy$),
565
+ switchMap(async (d) => d),
566
+ ).subscribe(data => {
567
+
568
+ const xScale: d3.ScaleLinear<number, number> = createValueToAxisScale({
569
+ // maxValue: data.minMaxXY.maxX,
570
+ // minValue: data.minMaxXY.minX,
571
+ maxValue: data.filteredMinMaxXYData.maxYDatum.value[1],
572
+ minValue: data.filteredMinMaxXYData.minYDatum.value[0],
573
+ axisWidth: data.layout.width,
574
+ scaleDomain: data.fullDataFormatter.xAxis.scaleDomain,
575
+ scaleRange: data.fullDataFormatter.xAxis.scaleRange,
576
+ })
577
+
578
+ subscriber.next(xScale)
579
+ })
580
+ })
581
+
582
+ const yScale$: Observable<d3.ScaleLinear<number, number>> = new Observable(subscriber => {
583
+ combineLatest({
584
+ fullDataFormatter: observer.fullDataFormatter$,
585
+ layout: observer.layout$,
586
+ // minMaxXY: observer.minMaxXY$
587
+ filteredMinMaxXYData: observer.filteredMinMaxXYData$
588
+ }).pipe(
589
+ takeUntil(destroy$),
590
+ switchMap(async (d) => d),
591
+ ).subscribe(data => {
592
+
593
+ const yScale: d3.ScaleLinear<number, number> = createValueToAxisScale({
594
+ maxValue: data.filteredMinMaxXYData.maxYDatum.value[1],
595
+ minValue: data.filteredMinMaxXYData.minYDatum.value[0],
596
+ axisWidth: data.layout.height,
597
+ scaleDomain: data.fullDataFormatter.yAxis.scaleDomain,
598
+ scaleRange: data.fullDataFormatter.yAxis.scaleRange,
599
+ reverse: true
600
+ })
601
+
602
+ subscriber.next(yScale)
603
+ })
604
+ })
605
+
606
+
607
+ combineLatest({
608
+ axisSelection: axisSelection$,
609
+ fullParams: observer.fullParams$,
610
+ // tickTextAlign: tickTextAlign$,
611
+ // axisLabelAlign: axisLabelAlign$,
612
+ computedData: observer.computedData$,
613
+ layout: observer.layout$,
614
+ fullDataFormatter: observer.fullDataFormatter$,
615
+ fullChartParams: observer.fullChartParams$,
616
+ xScale: xScale$,
617
+ yScale: yScale$,
618
+ textReverseTransform: textReverseTransform$,
619
+ minMaxXY: observer.minMaxXY$
620
+ }).pipe(
621
+ takeUntil(destroy$),
622
+ switchMap(async (d) => d),
623
+ ).subscribe(data => {
624
+
625
+ renderXAxis({
626
+ selection: data.axisSelection,
627
+ xAxisClassName,
628
+ fullParams: data.fullParams,
629
+ // tickTextAlign: data.tickTextAlign,
630
+ layout: data.layout,
631
+ fullDataFormatter: data.fullDataFormatter,
632
+ fullChartParams: data.fullChartParams,
633
+ xScale: data.xScale,
634
+ textReverseTransform: data.textReverseTransform,
635
+ minMaxXY: data.minMaxXY
636
+ })
637
+
638
+ renderYAxis({
639
+ selection: data.axisSelection,
640
+ yAxisClassName,
641
+ fullParams: data.fullParams,
642
+ // tickTextAlign: data.tickTextAlign,
643
+ layout: data.layout,
644
+ fullDataFormatter: data.fullDataFormatter,
645
+ fullChartParams: data.fullChartParams,
646
+ yScale: data.yScale,
647
+ textReverseTransform: data.textReverseTransform,
648
+ minMaxXY: data.minMaxXY
649
+ })
650
+
651
+ renderXAxisLabel({
652
+ selection: data.axisSelection,
653
+ xLabelClassName,
654
+ fullParams: data.fullParams,
655
+ // axisLabelAlign: data.axisLabelAlign,
656
+ layout: data.layout,
657
+ fullDataFormatter: data.fullDataFormatter,
658
+ fullChartParams: data.fullChartParams,
659
+ textReverseTransform: data.textReverseTransform,
660
+ })
661
+
662
+ renderYAxisLabel({
663
+ selection: data.axisSelection,
664
+ yLabelClassName,
665
+ fullParams: data.fullParams,
666
+ // axisLabelAlign: data.axisLabelAlign,
667
+ layout: data.layout,
668
+ fullDataFormatter: data.fullDataFormatter,
669
+ fullChartParams: data.fullChartParams,
670
+ textReverseTransform: data.textReverseTransform,
671
+ })
672
+ })
673
+
674
+ return () => {
675
+ destroy$.next(undefined)
676
+ }
677
+ })