@orbcharts/plugins-basic 3.0.0-alpha.49 → 3.0.0-alpha.51

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 (91) hide show
  1. package/LICENSE +200 -200
  2. package/dist/orbcharts-plugins-basic.es.js +6325 -6272
  3. package/dist/orbcharts-plugins-basic.umd.js +8 -8
  4. package/dist/src/base/BaseGroupAxis.d.ts +1 -0
  5. package/dist/src/grid/index.d.ts +1 -1
  6. package/dist/src/grid/plugins/BarsPN.d.ts +1 -0
  7. package/dist/src/grid/types.d.ts +3 -2
  8. package/dist/src/multiGrid/defaults.d.ts +2 -1
  9. package/dist/src/multiGrid/index.d.ts +2 -0
  10. package/dist/src/multiGrid/multiGridObservables.d.ts +1 -1
  11. package/dist/src/multiGrid/plugins/MultiValueStackAxis.d.ts +1 -0
  12. package/dist/src/multiGrid/plugins/OverlappingValueStackAxes.d.ts +1 -0
  13. package/dist/src/multiGrid/types.d.ts +3 -0
  14. package/dist/src/series/types.d.ts +2 -3
  15. package/package.json +42 -42
  16. package/src/base/BaseBarStack.ts +778 -778
  17. package/src/base/BaseBars.ts +764 -764
  18. package/src/base/BaseBarsTriangle.ts +672 -672
  19. package/src/base/BaseDots.ts +513 -502
  20. package/src/base/BaseGroupAxis.ts +562 -496
  21. package/src/base/BaseLegend.ts +641 -641
  22. package/src/base/BaseLineAreas.ts +625 -625
  23. package/src/base/BaseLines.ts +699 -699
  24. package/src/base/BaseValueAxis.ts +478 -478
  25. package/src/base/types.ts +2 -2
  26. package/src/grid/defaults.ts +125 -121
  27. package/src/grid/gridObservables.ts +248 -247
  28. package/src/grid/index.ts +15 -15
  29. package/src/grid/plugins/BarStack.ts +43 -50
  30. package/src/grid/plugins/Bars.ts +44 -51
  31. package/src/grid/plugins/{BarsDiverging.ts → BarsPN.ts} +41 -41
  32. package/src/grid/plugins/BarsTriangle.ts +42 -50
  33. package/src/grid/plugins/Dots.ts +37 -37
  34. package/src/grid/plugins/GridLegend.ts +59 -59
  35. package/src/grid/plugins/GroupAux.ts +645 -645
  36. package/src/grid/plugins/GroupAxis.ts +35 -42
  37. package/src/grid/plugins/LineAreas.ts +39 -39
  38. package/src/grid/plugins/Lines.ts +38 -38
  39. package/src/grid/plugins/ScalingArea.ts +173 -173
  40. package/src/grid/plugins/ValueAxis.ts +36 -43
  41. package/src/grid/plugins/ValueStackAxis.ts +38 -79
  42. package/src/grid/types.ts +122 -120
  43. package/src/index.ts +9 -9
  44. package/src/multiGrid/defaults.ts +152 -147
  45. package/src/multiGrid/index.ts +14 -12
  46. package/src/multiGrid/multiGridObservables.ts +44 -43
  47. package/src/multiGrid/plugins/MultiBarStack.ts +78 -78
  48. package/src/multiGrid/plugins/MultiBars.ts +77 -77
  49. package/src/multiGrid/plugins/MultiBarsTriangle.ts +77 -77
  50. package/src/multiGrid/plugins/MultiDots.ts +65 -65
  51. package/src/multiGrid/plugins/MultiGridLegend.ts +89 -89
  52. package/src/multiGrid/plugins/MultiGroupAxis.ts +69 -69
  53. package/src/multiGrid/plugins/MultiLineAreas.ts +67 -67
  54. package/src/multiGrid/plugins/MultiLines.ts +66 -66
  55. package/src/multiGrid/plugins/MultiValueAxis.ts +69 -69
  56. package/src/multiGrid/plugins/MultiValueStackAxis.ts +69 -0
  57. package/src/multiGrid/plugins/OverlappingValueAxes.ts +166 -173
  58. package/src/multiGrid/plugins/OverlappingValueStackAxes.ts +167 -0
  59. package/src/multiGrid/types.ts +71 -67
  60. package/src/noneData/defaults.ts +64 -64
  61. package/src/noneData/index.ts +3 -3
  62. package/src/noneData/plugins/Container.ts +10 -10
  63. package/src/noneData/plugins/Tooltip.ts +310 -310
  64. package/src/noneData/types.ts +26 -26
  65. package/src/series/defaults.ts +126 -126
  66. package/src/series/index.ts +9 -9
  67. package/src/series/plugins/Bubbles.ts +545 -602
  68. package/src/series/plugins/Pie.ts +576 -576
  69. package/src/series/plugins/PieEventTexts.ts +262 -262
  70. package/src/series/plugins/PieLabels.ts +304 -304
  71. package/src/series/plugins/Rose.ts +472 -472
  72. package/src/series/plugins/RoseLabels.ts +362 -362
  73. package/src/series/plugins/SeriesLegend.ts +59 -59
  74. package/src/series/seriesObservables.ts +145 -145
  75. package/src/series/seriesUtils.ts +51 -51
  76. package/src/series/types.ts +83 -83
  77. package/src/tree/defaults.ts +22 -22
  78. package/src/tree/index.ts +3 -3
  79. package/src/tree/plugins/TreeLegend.ts +59 -59
  80. package/src/tree/plugins/TreeMap.ts +305 -305
  81. package/src/tree/types.ts +23 -23
  82. package/src/utils/commonUtils.ts +21 -21
  83. package/src/utils/d3Graphics.ts +124 -124
  84. package/src/utils/d3Utils.ts +73 -73
  85. package/src/utils/observables.ts +14 -14
  86. package/src/utils/orbchartsUtils.ts +100 -100
  87. package/tsconfig.dev.json +16 -16
  88. package/tsconfig.json +13 -13
  89. package/tsconfig.prod.json +13 -13
  90. package/vite.config.js +49 -49
  91. package/dist/src/grid/plugins/BarsDiverging.d.ts +0 -1
@@ -1,497 +1,563 @@
1
- import * as d3 from 'd3'
2
- import {
3
- combineLatest,
4
- switchMap,
5
- distinctUntilChanged,
6
- of,
7
- first,
8
- map,
9
- takeUntil,
10
- Observable,
11
- Subject } from 'rxjs'
12
- import { createAxisPointScale } from '@orbcharts/core'
13
- import type { ColorType, ComputedDataGrid, GridContainerPosition } from '@orbcharts/core'
14
- import type { BasePluginFn } from './types'
15
- import type {
16
- ComputedDatumGrid,
17
- DataFormatterGrid,
18
- ChartParams,
19
- TransformData } from '@orbcharts/core'
20
- import { parseTickFormatValue } from '../utils/d3Utils'
21
- import { getColor, getClassName } from '../utils/orbchartsUtils'
22
-
23
- export interface BaseGroupAxisParams {
24
- // xLabel: string
25
- // labelAnchor: 'start' | 'end'
26
- labelOffset: [number, number]
27
- labelColorType: ColorType
28
- axisLineVisible: boolean
29
- axisLineColorType: ColorType
30
- tickFormat: string | ((text: any) => string)
31
- tickLineVisible: boolean
32
- tickPadding: number
33
- tickFullLine: boolean
34
- tickFullLineDasharray: string
35
- tickColorType: ColorType
36
- // axisLineColor: string
37
- // axisLabelColor: string
38
- tickTextRotate: number
39
- tickTextColorType: ColorType
40
- }
41
-
42
- interface BaseGroupAxisContext {
43
- selection: d3.Selection<any, unknown, any, unknown>
44
- computedData$: Observable<ComputedDataGrid>
45
- fullParams$: Observable<BaseGroupAxisParams>
46
- fullDataFormatter$: Observable<DataFormatterGrid>
47
- fullChartParams$: Observable<ChartParams>
48
- gridAxesTransform$: Observable<TransformData>
49
- gridAxesReverseTransform$: Observable<TransformData>
50
- gridAxesSize$: Observable<{
51
- width: number;
52
- height: number;
53
- }>
54
- gridContainerPosition$: Observable<GridContainerPosition[]>
55
- isSeriesSeprate$: Observable<boolean>
56
- }
57
-
58
- interface TextAlign {
59
- textAnchor: "start" | "middle" | "end"
60
- dominantBaseline: "middle" | "auto" | "hanging"
61
- }
62
-
63
- // const pluginName = 'GroupAxis'
64
- // const containerClassName = getClassName(pluginName, 'container')
65
- // const xAxisGClassName = getClassName(pluginName, 'xAxisG')
66
- // const xAxisClassName = getClassName(pluginName, 'xAxis')
67
- // const groupingLabelClassName = getClassName(pluginName, 'groupingLabel')
68
- const defaultTickSize = 6
69
-
70
- function renderPointAxis ({ selection, xAxisClassName, groupingLabelClassName, params, tickTextAlign, axisLabelAlign, gridAxesSize, fullDataFormatter, chartParams, groupScale, textTransform }: {
71
- selection: d3.Selection<SVGGElement, any, any, any>,
72
- xAxisClassName: string
73
- groupingLabelClassName: string
74
- params: BaseGroupAxisParams
75
- tickTextAlign: TextAlign
76
- axisLabelAlign: TextAlign
77
- gridAxesSize: { width: number, height: number }
78
- fullDataFormatter: DataFormatterGrid,
79
- chartParams: ChartParams
80
- groupScale: d3.ScalePoint<string>
81
- textTransform: string
82
- // tickTextFormatter: string | ((label: any) => string)
83
- }) {
84
-
85
- const xAxisSelection = selection
86
- .selectAll<SVGGElement, BaseGroupAxisParams>(`g.${xAxisClassName}`)
87
- .data([params])
88
- .join('g')
89
- .classed(xAxisClassName, true)
90
-
91
- const axisLabelSelection = selection
92
- .selectAll<SVGGElement, BaseGroupAxisParams>(`g.${groupingLabelClassName}`)
93
- .data([params])
94
- .join('g')
95
- .classed(groupingLabelClassName, true)
96
- .each((d, i, g) => {
97
- const text = d3.select(g[i])
98
- .selectAll<SVGTextElement, BaseGroupAxisParams>('text')
99
- .data([d])
100
- .join(
101
- enter => {
102
- return enter
103
- .append('text')
104
- .style('font-weight', 'bold')
105
- },
106
- update => update,
107
- exit => exit.remove()
108
- )
109
- .attr('text-anchor', axisLabelAlign.textAnchor)
110
- .attr('dominant-baseline', axisLabelAlign.dominantBaseline)
111
- .attr('font-size', chartParams.styles.textSize)
112
- .style('fill', getColor(params.labelColorType, chartParams))
113
- .style('transform', textTransform)
114
- .text(d => fullDataFormatter.grid.groupAxis.label)
115
- })
116
- .attr('transform', d => `translate(${gridAxesSize.width + d.tickPadding + params.labelOffset[0]}, ${- d.tickPadding - defaultTickSize - params.labelOffset[1]})`)
117
-
118
-
119
- // 設定X軸刻度
120
- // const xAxis = d3.axisBottom(groupScale)
121
- const xAxis = d3.axisTop(groupScale)
122
- .scale(groupScale)
123
- .tickSize(params.tickFullLine == true
124
- ? -gridAxesSize.height
125
- : defaultTickSize)
126
- .tickSizeOuter(0)
127
- .tickFormat(d => parseTickFormatValue(d, params.tickFormat))
128
- .tickPadding(params.tickPadding)
129
-
130
- const xAxisEl = xAxisSelection
131
- .transition()
132
- .duration(100)
133
- .call(xAxis)
134
- // .attr('text-anchor', () => params.tickTextRotate !== false ? 'end' : 'middle')
135
- // .attr('text-anchor', () => 'middle')
136
-
137
- xAxisEl.selectAll('line')
138
- .style('fill', 'none')
139
- .style('stroke', params.tickLineVisible == true ? getColor(params.tickColorType, chartParams) : 'none')
140
- .style('stroke-dasharray', params.tickFullLineDasharray)
141
- .attr('pointer-events', 'none')
142
-
143
- xAxisEl.selectAll('path')
144
- .style('fill', 'none')
145
- .style('stroke', params.axisLineVisible == true ? getColor(params.axisLineColorType, chartParams) : 'none')
146
- .style('shape-rendering', 'crispEdges')
147
-
148
- // const xText = xAxisEl.selectAll('text')
149
- // xAxisSelection.each((d, i, g) => {
150
- // d3.select(g[i])
151
- // .selectAll('text')
152
- // .data([d])
153
- // .join('text')
154
- // .style('font-family', 'sans-serif')
155
- // .style('font-size', `${chartParams.styles.textSize}px`)
156
- // // .style('font-weight', 'bold')
157
- // .style('color', getColor(params.tickTextColorType, chartParams))
158
- // .attr('text-anchor', tickTextAlign.textAnchor)
159
- // .attr('dominant-baseline', tickTextAlign.dominantBaseline)
160
- // .attr('transform-origin', `0 -${params.tickPadding + defaultTickSize}`)
161
- // .style('transform', textTransform)
162
- // })
163
- const xText = xAxisSelection.selectAll('text')
164
- // .style('font-family', 'sans-serif')
165
- .attr('font-size', chartParams.styles.textSize)
166
- // .style('font-weight', 'bold')
167
- .style('color', getColor(params.tickTextColorType, chartParams))
168
- .attr('text-anchor', tickTextAlign.textAnchor)
169
- .attr('dominant-baseline', tickTextAlign.dominantBaseline)
170
- .attr('transform-origin', `0 -${params.tickPadding + defaultTickSize}`)
171
- .style('transform', textTransform)
172
-
173
-
174
- return xAxisSelection
175
- }
176
-
177
-
178
- export const createBaseGroupAxis: BasePluginFn<BaseGroupAxisContext> = ((pluginName: string, {
179
- selection,
180
- computedData$,
181
- fullParams$,
182
- fullDataFormatter$,
183
- fullChartParams$,
184
- gridAxesTransform$,
185
- gridAxesReverseTransform$,
186
- gridAxesSize$,
187
- gridContainerPosition$,
188
- isSeriesSeprate$,
189
- }) => {
190
-
191
- const destroy$ = new Subject()
192
-
193
- const containerClassName = getClassName(pluginName, 'container')
194
- const xAxisGClassName = getClassName(pluginName, 'xAxisG')
195
- const xAxisClassName = getClassName(pluginName, 'xAxis')
196
- const groupingLabelClassName = getClassName(pluginName, 'groupingLabel')
197
-
198
- const containerSelection$ = combineLatest({
199
- computedData: computedData$.pipe(
200
- distinctUntilChanged((a, b) => {
201
- // 只有當series的數量改變時,才重新計算
202
- return a.length === b.length
203
- }),
204
- ),
205
- isSeriesSeprate: isSeriesSeprate$
206
- }).pipe(
207
- takeUntil(destroy$),
208
- switchMap(async (d) => d),
209
- map(data => {
210
- return data.isSeriesSeprate
211
- // series分開的時候顯示各別axis
212
- ? data.computedData
213
- // series合併的時候只顯示第一個axis
214
- : [data.computedData[0]]
215
- }),
216
- map((computedData, i) => {
217
- return selection
218
- .selectAll<SVGGElement, ComputedDatumGrid[]>(`g.${containerClassName}`)
219
- .data(computedData, d => d[0] ? d[0].seriesIndex : i)
220
- .join('g')
221
- .classed(containerClassName, true)
222
- })
223
- )
224
-
225
- const axisSelection$ = containerSelection$.pipe(
226
- takeUntil(destroy$),
227
- map((containerSelection, i) => {
228
- return containerSelection
229
- .selectAll<SVGGElement, ComputedDatumGrid[]>(`g.${xAxisGClassName}`)
230
- .data([xAxisGClassName])
231
- .join('g')
232
- .classed(xAxisGClassName, true)
233
- })
234
- )
235
-
236
- combineLatest({
237
- containerSelection: containerSelection$,
238
- gridContainerPosition: gridContainerPosition$
239
- }).pipe(
240
- takeUntil(destroy$),
241
- switchMap(async d => d)
242
- ).subscribe(data => {
243
- data.containerSelection
244
- .attr('transform', (d, i) => {
245
- const gridContainerPosition = data.gridContainerPosition[i] ?? data.gridContainerPosition[0]
246
- const translate = gridContainerPosition.translate
247
- const scale = gridContainerPosition.scale
248
- return `translate(${translate[0]}, ${translate[1]}) scale(${scale[0]}, ${scale[1]})`
249
- })
250
- // .attr('opacity', 0)
251
- // .transition()
252
- // .attr('opacity', 1)
253
- })
254
-
255
- combineLatest({
256
- axisSelection: axisSelection$,
257
- gridAxesTransform: gridAxesTransform$,
258
- }).pipe(
259
- takeUntil(destroy$),
260
- switchMap(async d => d)
261
- ).subscribe(data => {
262
- data.axisSelection
263
- .style('transform', data.gridAxesTransform.value)
264
- // .attr('opacity', 0)
265
- // .transition()
266
- // .attr('opacity', 1)
267
-
268
- })
269
-
270
-
271
- // const gridAxesSize$ = gridAxisSizeObservable({
272
- // fullDataFormatter$,
273
- // layout$
274
- // })
275
-
276
-
277
- // const tickTextFormatter$ = fullDataFormatter$
278
- // .pipe(
279
- // map(d => {
280
- // return d.grid.seriesDirection === 'row' ? d.grid.columnLabelFormat : d.grid.rowLabelFormat
281
- // })
282
- // )
283
-
284
- // const textTransform$: Observable<string> = new Observable(subscriber => {
285
- // combineLatest({
286
- // params: fullParams$,
287
- // gridAxesTransform: gridAxesTransform$
288
- // }).pipe(
289
- // takeUntil(destroy$),
290
- // // 轉換後會退訂前一個未完成的訂閱事件,因此可以取到「同時間」最後一次的訂閱事件
291
- // switchMap(async (d) => d),
292
- // ).subscribe(data => {
293
-
294
- // const transformData = Object.assign({}, data.gridAxesTransform)
295
-
296
- // const value = getAxesTransformValue({
297
- // translate: [0, 0],
298
- // scale: [transformData.scale[0] * -1, transformData.scale[1] * -1],
299
- // rotate: transformData.rotate * -1 + data.params.tickTextRotate,
300
- // rotateX: transformData.rotateX * -1,
301
- // rotateY: transformData.rotateY * -1
302
- // })
303
-
304
- // subscriber.next(value)
305
- // })
306
- // })
307
- // const reverseTransform$: Observable<TransformData> = gridAxesTransform$.pipe(
308
- // takeUntil(destroy$),
309
- // map(d => {
310
- // const translate: [number, number] = [d.translate[0] * -1, d.translate[1] * -1]
311
- // const scale: [number, number] = [d.scale[0] * -1, d.scale[1] * -1]
312
- // const rotate = d.rotate * -1
313
- // const rotateX = d.rotateX * -1
314
- // const rotateY = d.rotateY * -1
315
- // return {
316
- // translate,
317
- // scale,
318
- // rotate,
319
- // rotateX,
320
- // rotateY,
321
- // value: ''
322
- // }
323
- // }),
324
- // )
325
- const textTransform$ = combineLatest({
326
- fullParams: fullParams$,
327
- fullDataFormatter: fullDataFormatter$,
328
- gridAxesReverseTransform: gridAxesReverseTransform$,
329
- gridContainerPosition: gridContainerPosition$
330
- }).pipe(
331
- takeUntil(destroy$),
332
- switchMap(async (d) => d),
333
- map(data => {
334
- const axisReverseTranslateValue = `translate(${data.gridAxesReverseTransform.translate[0]}px, ${data.gridAxesReverseTransform.translate[1]}px)`
335
- const axisReverseRotateValue = `rotate(${data.gridAxesReverseTransform.rotate}deg) rotateX(${data.gridAxesReverseTransform.rotateX}deg) rotateY(${data.gridAxesReverseTransform.rotateY}deg)`
336
- const containerScaleReverseScaleValue = `scale(${1 / data.gridContainerPosition[0].scale[0]}, ${1 / data.gridContainerPosition[0].scale[1]})`
337
- const tickTextRotateDeg = (data.fullDataFormatter.grid.groupAxis.position === 'left' && data.fullDataFormatter.grid.valueAxis.position === 'top')
338
- || (data.fullDataFormatter.grid.groupAxis.position === 'right' && data.fullDataFormatter.grid.valueAxis.position === 'bottom')
339
- ? data.fullParams.tickTextRotate + 180 // 修正文字倒轉
340
- : data.fullParams.tickTextRotate
341
-
342
- const textRotateValue = `rotate(${tickTextRotateDeg}deg)`
343
-
344
- // 必須按照順序(先抵消外層rotate,再抵消最外層scale,最後再做本身的rotate)
345
- return `${axisReverseTranslateValue} ${axisReverseRotateValue} ${containerScaleReverseScaleValue} ${textRotateValue}`
346
- }),
347
- distinctUntilChanged()
348
- )
349
-
350
- const groupScale$: Observable<d3.ScalePoint<string>> = new Observable(subscriber => {
351
- combineLatest({
352
- fullDataFormatter: fullDataFormatter$,
353
- gridAxesSize: gridAxesSize$,
354
- computedData: computedData$
355
- }).pipe(
356
- takeUntil(destroy$),
357
- switchMap(async (d) => d),
358
- ).subscribe(data => {
359
- const groupMin = 0
360
- const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
361
- const groupScaleDomainMin = data.fullDataFormatter.grid.groupAxis.scaleDomain[0] === 'auto'
362
- ? groupMin - data.fullDataFormatter.grid.groupAxis.scalePadding
363
- : data.fullDataFormatter.grid.groupAxis.scaleDomain[0] as number - data.fullDataFormatter.grid.groupAxis.scalePadding
364
- const groupScaleDomainMax = data.fullDataFormatter.grid.groupAxis.scaleDomain[1] === 'auto'
365
- ? groupMax + data.fullDataFormatter.grid.groupAxis.scalePadding
366
- : data.fullDataFormatter.grid.groupAxis.scaleDomain[1] as number + data.fullDataFormatter.grid.groupAxis.scalePadding
367
-
368
- const groupingLength = data.computedData[0]
369
- ? data.computedData[0].length
370
- : 0
371
-
372
- let _labels = (data.computedData[0] ?? []).map(d => d.groupLabel)
373
-
374
- const axisLabels = new Array(groupingLength).fill(0)
375
- .map((d, i) => {
376
- return _labels[i] != null
377
- ? _labels[i]
378
- : String(i) // 沒有label則用序列號填充
379
- })
380
- .filter((d, i) => {
381
- return i >= groupScaleDomainMin && i <= groupScaleDomainMax
382
- })
383
-
384
-
385
- const padding = data.fullDataFormatter.grid.groupAxis.scalePadding
386
-
387
- const groupScale = createAxisPointScale({
388
- axisLabels,
389
- axisWidth: data.gridAxesSize.width,
390
- padding
391
- })
392
-
393
- subscriber.next(groupScale)
394
- })
395
- })
396
-
397
- const tickTextAlign$: Observable<TextAlign> = combineLatest({
398
- fullDataFormatter: fullDataFormatter$,
399
- fullParams: fullParams$
400
- }).pipe(
401
- takeUntil(destroy$),
402
- switchMap(async (d) => d),
403
- map(data => {
404
- let textAnchor: 'start' | 'middle' | 'end' = 'middle'
405
- let dominantBaseline: 'auto' | 'middle' | 'hanging' = 'hanging'
406
-
407
- if (data.fullDataFormatter.grid.groupAxis.position === 'bottom') {
408
- textAnchor = data.fullParams.tickTextRotate
409
- ? 'end'
410
- : 'middle'
411
- dominantBaseline = 'hanging'
412
- } else if (data.fullDataFormatter.grid.groupAxis.position === 'top') {
413
- textAnchor = data.fullParams.tickTextRotate
414
- ? 'end'
415
- : 'middle'
416
- dominantBaseline = 'auto'
417
- } else if (data.fullDataFormatter.grid.groupAxis.position === 'left') {
418
- textAnchor = 'end'
419
- dominantBaseline = 'middle'
420
- } else if (data.fullDataFormatter.grid.groupAxis.position === 'right') {
421
- textAnchor = 'start'
422
- dominantBaseline = 'middle'
423
- }
424
- return {
425
- textAnchor,
426
- dominantBaseline
427
- }
428
- })
429
- )
430
-
431
- const axisLabelAlign$: Observable<TextAlign> = fullDataFormatter$.pipe(
432
- takeUntil(destroy$),
433
- map(d => {
434
- let textAnchor: 'start' | 'middle' | 'end' = 'start'
435
- let dominantBaseline: 'auto' | 'middle' | 'hanging' = 'hanging'
436
-
437
- if (d.grid.groupAxis.position === 'bottom') {
438
- dominantBaseline = 'hanging'
439
- } else if (d.grid.groupAxis.position === 'top') {
440
- dominantBaseline = 'auto'
441
- } else if (d.grid.groupAxis.position === 'left') {
442
- textAnchor = 'end'
443
- } else if (d.grid.groupAxis.position === 'right') {
444
- textAnchor = 'start'
445
- }
446
- if (d.grid.valueAxis.position === 'left') {
447
- textAnchor = 'start'
448
- } else if (d.grid.valueAxis.position === 'right') {
449
- textAnchor = 'end'
450
- } else if (d.grid.valueAxis.position === 'bottom') {
451
- dominantBaseline = 'auto'
452
- } else if (d.grid.valueAxis.position === 'top') {
453
- dominantBaseline = 'hanging'
454
- }
455
- return {
456
- textAnchor,
457
- dominantBaseline
458
- }
459
- })
460
- )
461
-
462
- combineLatest({
463
- axisSelection: axisSelection$,
464
- params: fullParams$,
465
- tickTextAlign: tickTextAlign$,
466
- axisLabelAlign: axisLabelAlign$,
467
- gridAxesSize: gridAxesSize$,
468
- fullDataFormatter: fullDataFormatter$,
469
- chartParams: fullChartParams$,
470
- groupScale: groupScale$,
471
- textTransform: textTransform$,
472
- // tickTextFormatter: tickTextFormatter$
473
- }).pipe(
474
- takeUntil(destroy$),
475
- switchMap(async (d) => d),
476
- ).subscribe(data => {
477
-
478
- renderPointAxis({
479
- selection: data.axisSelection,
480
- xAxisClassName,
481
- groupingLabelClassName,
482
- params: data.params,
483
- tickTextAlign: data.tickTextAlign,
484
- axisLabelAlign: data.axisLabelAlign,
485
- gridAxesSize: data.gridAxesSize,
486
- fullDataFormatter: data.fullDataFormatter,
487
- chartParams: data.chartParams,
488
- groupScale: data.groupScale,
489
- textTransform: data.textTransform,
490
- // tickTextFormatter: data.tickTextFormatter
491
- })
492
- })
493
-
494
- return () => {
495
- destroy$.next(undefined)
496
- }
1
+ import * as d3 from 'd3'
2
+ import {
3
+ combineLatest,
4
+ switchMap,
5
+ distinctUntilChanged,
6
+ of,
7
+ first,
8
+ map,
9
+ takeUntil,
10
+ shareReplay,
11
+ Observable,
12
+ Subject } from 'rxjs'
13
+ import { createAxisPointScale, createAxisLinearScale } from '@orbcharts/core'
14
+ import type { ColorType, ComputedDataGrid, GridContainerPosition } from '@orbcharts/core'
15
+ import type { BasePluginFn } from './types'
16
+ import type {
17
+ ComputedDatumGrid,
18
+ DataFormatterGrid,
19
+ ChartParams,
20
+ TransformData } from '@orbcharts/core'
21
+ import { parseTickFormatValue } from '../utils/d3Utils'
22
+ import { getColor, getClassName } from '../utils/orbchartsUtils'
23
+
24
+ export interface BaseGroupAxisParams {
25
+ // xLabel: string
26
+ // labelAnchor: 'start' | 'end'
27
+ labelOffset: [number, number]
28
+ labelColorType: ColorType
29
+ axisLineVisible: boolean
30
+ axisLineColorType: ColorType
31
+ ticks: number | null | 'all'
32
+ tickFormat: string | ((text: any) => string)
33
+ tickLineVisible: boolean
34
+ tickPadding: number
35
+ tickFullLine: boolean
36
+ tickFullLineDasharray: string
37
+ tickColorType: ColorType
38
+ // axisLineColor: string
39
+ // axisLabelColor: string
40
+ tickTextRotate: number
41
+ tickTextColorType: ColorType
42
+ }
43
+
44
+ interface BaseGroupAxisContext {
45
+ selection: d3.Selection<any, unknown, any, unknown>
46
+ computedData$: Observable<ComputedDataGrid>
47
+ fullParams$: Observable<BaseGroupAxisParams>
48
+ fullDataFormatter$: Observable<DataFormatterGrid>
49
+ fullChartParams$: Observable<ChartParams>
50
+ gridAxesTransform$: Observable<TransformData>
51
+ gridAxesReverseTransform$: Observable<TransformData>
52
+ gridAxesSize$: Observable<{
53
+ width: number;
54
+ height: number;
55
+ }>
56
+ gridContainerPosition$: Observable<GridContainerPosition[]>
57
+ isSeriesSeprate$: Observable<boolean>
58
+ }
59
+
60
+ interface TextAlign {
61
+ textAnchor: "start" | "middle" | "end"
62
+ dominantBaseline: "middle" | "auto" | "hanging"
63
+ }
64
+
65
+ // const pluginName = 'GroupAxis'
66
+ // const containerClassName = getClassName(pluginName, 'container')
67
+ // const xAxisGClassName = getClassName(pluginName, 'xAxisG')
68
+ // const xAxisClassName = getClassName(pluginName, 'xAxis')
69
+ // const groupingLabelClassName = getClassName(pluginName, 'groupingLabel')
70
+ const defaultTickSize = 6
71
+
72
+ function renderAxis ({ selection, xAxisClassName, groupingLabelClassName, params, tickTextAlign, axisLabelAlign, gridAxesSize, fullDataFormatter, chartParams, groupScale, groupScaleDomain, groupLabels, textTransform }: {
73
+ selection: d3.Selection<SVGGElement, any, any, any>,
74
+ xAxisClassName: string
75
+ groupingLabelClassName: string
76
+ params: BaseGroupAxisParams
77
+ tickTextAlign: TextAlign
78
+ axisLabelAlign: TextAlign
79
+ gridAxesSize: { width: number, height: number }
80
+ fullDataFormatter: DataFormatterGrid,
81
+ chartParams: ChartParams
82
+ // groupScale: d3.ScalePoint<string>
83
+ groupScale: d3.ScaleLinear<number, number>
84
+ groupScaleDomain: number[]
85
+ groupLabels: string[]
86
+ textTransform: string
87
+ // tickTextFormatter: string | ((label: any) => string)
88
+ }) {
89
+
90
+ const xAxisSelection = selection
91
+ .selectAll<SVGGElement, BaseGroupAxisParams>(`g.${xAxisClassName}`)
92
+ .data([params])
93
+ .join('g')
94
+ .classed(xAxisClassName, true)
95
+
96
+ const axisLabelSelection = selection
97
+ .selectAll<SVGGElement, BaseGroupAxisParams>(`g.${groupingLabelClassName}`)
98
+ .data([params])
99
+ .join('g')
100
+ .classed(groupingLabelClassName, true)
101
+ .each((d, i, g) => {
102
+ const text = d3.select(g[i])
103
+ .selectAll<SVGTextElement, BaseGroupAxisParams>('text')
104
+ .data([d])
105
+ .join(
106
+ enter => {
107
+ return enter
108
+ .append('text')
109
+ .style('font-weight', 'bold')
110
+ },
111
+ update => update,
112
+ exit => exit.remove()
113
+ )
114
+ .attr('text-anchor', axisLabelAlign.textAnchor)
115
+ .attr('dominant-baseline', axisLabelAlign.dominantBaseline)
116
+ .attr('font-size', chartParams.styles.textSize)
117
+ .style('fill', getColor(params.labelColorType, chartParams))
118
+ .style('transform', textTransform)
119
+ .text(d => fullDataFormatter.grid.groupAxis.label)
120
+ })
121
+ .attr('transform', d => `translate(${gridAxesSize.width + d.tickPadding + params.labelOffset[0]}, ${- d.tickPadding - defaultTickSize - params.labelOffset[1]})`)
122
+
123
+ // 計算所有範圍內groupLabels數量(顯示所有刻度)
124
+ const allTicksAmount = Math.floor(groupScaleDomain[1]) - Math.ceil(groupScaleDomain[0]) + 1
125
+ console.log(params.ticks, params.ticks === 'all'
126
+ ? allTicksAmount
127
+ : params.ticks > allTicksAmount
128
+ ? allTicksAmount // 不顯示超過groupLabels數量的刻度
129
+ : params.ticks)
130
+ // 設定X軸刻度
131
+ // const xAxis = d3.axisBottom(groupScale)
132
+ const xAxis = d3.axisTop(groupScale)
133
+ .scale(groupScale)
134
+ .ticks(params.ticks === 'all'
135
+ ? allTicksAmount
136
+ : params.ticks > allTicksAmount
137
+ ? allTicksAmount // 不顯示超過groupLabels數量的刻度
138
+ : params.ticks)
139
+ .tickSize(params.tickFullLine == true
140
+ ? -gridAxesSize.height
141
+ : defaultTickSize)
142
+ .tickSizeOuter(0)
143
+ .tickFormat((groupIndex: number) => {
144
+ // 用index對應到groupLabel
145
+ const groupLabel = groupLabels[groupIndex] ?? '' // 非整數index不顯示
146
+ return parseTickFormatValue(groupLabel, params.tickFormat)
147
+ })
148
+ .tickPadding(params.tickPadding)
149
+
150
+ const xAxisEl = xAxisSelection
151
+ .transition()
152
+ .duration(100)
153
+ .call(xAxis)
154
+ // .attr('text-anchor', () => params.tickTextRotate !== false ? 'end' : 'middle')
155
+ // .attr('text-anchor', () => 'middle')
156
+
157
+ xAxisEl.selectAll('line')
158
+ .style('fill', 'none')
159
+ .style('stroke', params.tickLineVisible == true ? getColor(params.tickColorType, chartParams) : 'none')
160
+ .style('stroke-dasharray', params.tickFullLineDasharray)
161
+ .attr('pointer-events', 'none')
162
+
163
+ xAxisEl.selectAll('path')
164
+ .style('fill', 'none')
165
+ .style('stroke', params.axisLineVisible == true ? getColor(params.axisLineColorType, chartParams) : 'none')
166
+ .style('shape-rendering', 'crispEdges')
167
+
168
+ // const xText = xAxisEl.selectAll('text')
169
+ // xAxisSelection.each((d, i, g) => {
170
+ // d3.select(g[i])
171
+ // .selectAll('text')
172
+ // .data([d])
173
+ // .join('text')
174
+ // .style('font-family', 'sans-serif')
175
+ // .style('font-size', `${chartParams.styles.textSize}px`)
176
+ // // .style('font-weight', 'bold')
177
+ // .style('color', getColor(params.tickTextColorType, chartParams))
178
+ // .attr('text-anchor', tickTextAlign.textAnchor)
179
+ // .attr('dominant-baseline', tickTextAlign.dominantBaseline)
180
+ // .attr('transform-origin', `0 -${params.tickPadding + defaultTickSize}`)
181
+ // .style('transform', textTransform)
182
+ // })
183
+ const xText = xAxisSelection.selectAll('text')
184
+ // .style('font-family', 'sans-serif')
185
+ .attr('font-size', chartParams.styles.textSize)
186
+ // .style('font-weight', 'bold')
187
+ .style('color', getColor(params.tickTextColorType, chartParams))
188
+ .attr('text-anchor', tickTextAlign.textAnchor)
189
+ .attr('dominant-baseline', tickTextAlign.dominantBaseline)
190
+ .attr('transform-origin', `0 -${params.tickPadding + defaultTickSize}`)
191
+ .style('transform', textTransform)
192
+
193
+
194
+ return xAxisSelection
195
+ }
196
+
197
+
198
+ export const createBaseGroupAxis: BasePluginFn<BaseGroupAxisContext> = ((pluginName: string, {
199
+ selection,
200
+ computedData$,
201
+ fullParams$,
202
+ fullDataFormatter$,
203
+ fullChartParams$,
204
+ gridAxesTransform$,
205
+ gridAxesReverseTransform$,
206
+ gridAxesSize$,
207
+ gridContainerPosition$,
208
+ isSeriesSeprate$,
209
+ }) => {
210
+
211
+ const destroy$ = new Subject()
212
+
213
+ const containerClassName = getClassName(pluginName, 'container')
214
+ const xAxisGClassName = getClassName(pluginName, 'xAxisG')
215
+ const xAxisClassName = getClassName(pluginName, 'xAxis')
216
+ const groupingLabelClassName = getClassName(pluginName, 'groupingLabel')
217
+
218
+ const containerSelection$ = combineLatest({
219
+ computedData: computedData$.pipe(
220
+ distinctUntilChanged((a, b) => {
221
+ // 只有當series的數量改變時,才重新計算
222
+ return a.length === b.length
223
+ }),
224
+ ),
225
+ isSeriesSeprate: isSeriesSeprate$
226
+ }).pipe(
227
+ takeUntil(destroy$),
228
+ switchMap(async (d) => d),
229
+ map(data => {
230
+ return data.isSeriesSeprate
231
+ // series分開的時候顯示各別axis
232
+ ? data.computedData
233
+ // series合併的時候只顯示第一個axis
234
+ : [data.computedData[0]]
235
+ }),
236
+ map((computedData, i) => {
237
+ return selection
238
+ .selectAll<SVGGElement, ComputedDatumGrid[]>(`g.${containerClassName}`)
239
+ .data(computedData, d => d[0] ? d[0].seriesIndex : i)
240
+ .join('g')
241
+ .classed(containerClassName, true)
242
+ })
243
+ )
244
+
245
+ const axisSelection$ = containerSelection$.pipe(
246
+ takeUntil(destroy$),
247
+ map((containerSelection, i) => {
248
+ return containerSelection
249
+ .selectAll<SVGGElement, ComputedDatumGrid[]>(`g.${xAxisGClassName}`)
250
+ .data([xAxisGClassName])
251
+ .join('g')
252
+ .classed(xAxisGClassName, true)
253
+ })
254
+ )
255
+
256
+ combineLatest({
257
+ containerSelection: containerSelection$,
258
+ gridContainerPosition: gridContainerPosition$
259
+ }).pipe(
260
+ takeUntil(destroy$),
261
+ switchMap(async d => d)
262
+ ).subscribe(data => {
263
+ data.containerSelection
264
+ .attr('transform', (d, i) => {
265
+ const gridContainerPosition = data.gridContainerPosition[i] ?? data.gridContainerPosition[0]
266
+ const translate = gridContainerPosition.translate
267
+ const scale = gridContainerPosition.scale
268
+ return `translate(${translate[0]}, ${translate[1]}) scale(${scale[0]}, ${scale[1]})`
269
+ })
270
+ // .attr('opacity', 0)
271
+ // .transition()
272
+ // .attr('opacity', 1)
273
+ })
274
+
275
+ combineLatest({
276
+ axisSelection: axisSelection$,
277
+ gridAxesTransform: gridAxesTransform$,
278
+ }).pipe(
279
+ takeUntil(destroy$),
280
+ switchMap(async d => d)
281
+ ).subscribe(data => {
282
+ data.axisSelection
283
+ .style('transform', data.gridAxesTransform.value)
284
+ // .attr('opacity', 0)
285
+ // .transition()
286
+ // .attr('opacity', 1)
287
+
288
+ })
289
+
290
+
291
+ // const gridAxesSize$ = gridAxisSizeObservable({
292
+ // fullDataFormatter$,
293
+ // layout$
294
+ // })
295
+
296
+
297
+ // const tickTextFormatter$ = fullDataFormatter$
298
+ // .pipe(
299
+ // map(d => {
300
+ // return d.grid.seriesDirection === 'row' ? d.grid.columnLabelFormat : d.grid.rowLabelFormat
301
+ // })
302
+ // )
303
+
304
+ // const textTransform$: Observable<string> = new Observable(subscriber => {
305
+ // combineLatest({
306
+ // params: fullParams$,
307
+ // gridAxesTransform: gridAxesTransform$
308
+ // }).pipe(
309
+ // takeUntil(destroy$),
310
+ // // 轉換後會退訂前一個未完成的訂閱事件,因此可以取到「同時間」最後一次的訂閱事件
311
+ // switchMap(async (d) => d),
312
+ // ).subscribe(data => {
313
+
314
+ // const transformData = Object.assign({}, data.gridAxesTransform)
315
+
316
+ // const value = getAxesTransformValue({
317
+ // translate: [0, 0],
318
+ // scale: [transformData.scale[0] * -1, transformData.scale[1] * -1],
319
+ // rotate: transformData.rotate * -1 + data.params.tickTextRotate,
320
+ // rotateX: transformData.rotateX * -1,
321
+ // rotateY: transformData.rotateY * -1
322
+ // })
323
+
324
+ // subscriber.next(value)
325
+ // })
326
+ // })
327
+ // const reverseTransform$: Observable<TransformData> = gridAxesTransform$.pipe(
328
+ // takeUntil(destroy$),
329
+ // map(d => {
330
+ // const translate: [number, number] = [d.translate[0] * -1, d.translate[1] * -1]
331
+ // const scale: [number, number] = [d.scale[0] * -1, d.scale[1] * -1]
332
+ // const rotate = d.rotate * -1
333
+ // const rotateX = d.rotateX * -1
334
+ // const rotateY = d.rotateY * -1
335
+ // return {
336
+ // translate,
337
+ // scale,
338
+ // rotate,
339
+ // rotateX,
340
+ // rotateY,
341
+ // value: ''
342
+ // }
343
+ // }),
344
+ // )
345
+ const textTransform$ = combineLatest({
346
+ fullParams: fullParams$,
347
+ fullDataFormatter: fullDataFormatter$,
348
+ gridAxesReverseTransform: gridAxesReverseTransform$,
349
+ gridContainerPosition: gridContainerPosition$
350
+ }).pipe(
351
+ takeUntil(destroy$),
352
+ switchMap(async (d) => d),
353
+ map(data => {
354
+ const axisReverseTranslateValue = `translate(${data.gridAxesReverseTransform.translate[0]}px, ${data.gridAxesReverseTransform.translate[1]}px)`
355
+ const axisReverseRotateValue = `rotate(${data.gridAxesReverseTransform.rotate}deg) rotateX(${data.gridAxesReverseTransform.rotateX}deg) rotateY(${data.gridAxesReverseTransform.rotateY}deg)`
356
+ const containerScaleReverseScaleValue = `scale(${1 / data.gridContainerPosition[0].scale[0]}, ${1 / data.gridContainerPosition[0].scale[1]})`
357
+ const tickTextRotateDeg = (data.fullDataFormatter.grid.groupAxis.position === 'left' && data.fullDataFormatter.grid.valueAxis.position === 'top')
358
+ || (data.fullDataFormatter.grid.groupAxis.position === 'right' && data.fullDataFormatter.grid.valueAxis.position === 'bottom')
359
+ ? data.fullParams.tickTextRotate + 180 // 修正文字倒轉
360
+ : data.fullParams.tickTextRotate
361
+
362
+ const textRotateValue = `rotate(${tickTextRotateDeg}deg)`
363
+
364
+ // 必須按照順序(先抵消外層rotate,再抵消最外層scale,最後再做本身的rotate)
365
+ return `${axisReverseTranslateValue} ${axisReverseRotateValue} ${containerScaleReverseScaleValue} ${textRotateValue}`
366
+ }),
367
+ distinctUntilChanged()
368
+ )
369
+
370
+
371
+ // 使用pointScale計算非連續性比例尺
372
+ // const groupScale$: Observable<d3.ScalePoint<string>> = new Observable(subscriber => {
373
+ // combineLatest({
374
+ // fullDataFormatter: fullDataFormatter$,
375
+ // gridAxesSize: gridAxesSize$,
376
+ // computedData: computedData$
377
+ // }).pipe(
378
+ // takeUntil(destroy$),
379
+ // switchMap(async (d) => d),
380
+ // ).subscribe(data => {
381
+ // const groupMin = 0
382
+ // const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
383
+ // const groupScaleDomainMin = data.fullDataFormatter.grid.groupAxis.scaleDomain[0] === 'auto'
384
+ // ? groupMin - data.fullDataFormatter.grid.groupAxis.scalePadding
385
+ // : data.fullDataFormatter.grid.groupAxis.scaleDomain[0] as number - data.fullDataFormatter.grid.groupAxis.scalePadding
386
+ // const groupScaleDomainMax = data.fullDataFormatter.grid.groupAxis.scaleDomain[1] === 'auto'
387
+ // ? groupMax + data.fullDataFormatter.grid.groupAxis.scalePadding
388
+ // : data.fullDataFormatter.grid.groupAxis.scaleDomain[1] as number + data.fullDataFormatter.grid.groupAxis.scalePadding
389
+
390
+ // const groupingLength = data.computedData[0]
391
+ // ? data.computedData[0].length
392
+ // : 0
393
+
394
+ // let _labels = (data.computedData[0] ?? []).map(d => d.groupLabel)
395
+
396
+ // const axisLabels = new Array(groupingLength).fill(0)
397
+ // .map((d, i) => {
398
+ // return _labels[i] != null
399
+ // ? _labels[i]
400
+ // : String(i) // 沒有label則用序列號填充
401
+ // })
402
+ // .filter((d, i) => {
403
+ // return i >= groupScaleDomainMin && i <= groupScaleDomainMax
404
+ // })
405
+
406
+
407
+ // const padding = data.fullDataFormatter.grid.groupAxis.scalePadding
408
+
409
+ // const groupScale = createAxisPointScale({
410
+ // axisLabels,
411
+ // axisWidth: data.gridAxesSize.width,
412
+ // padding
413
+ // })
414
+
415
+ // subscriber.next(groupScale)
416
+ // })
417
+ // })
418
+
419
+ const groupScaleDomain$ = combineLatest({
420
+ fullDataFormatter: fullDataFormatter$,
421
+ gridAxesSize: gridAxesSize$,
422
+ computedData: computedData$
423
+ }).pipe(
424
+ takeUntil(destroy$),
425
+ switchMap(async (d) => d),
426
+ map(data => {
427
+ const groupMin = 0
428
+ const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
429
+ const groupScaleDomainMin = data.fullDataFormatter.grid.groupAxis.scaleDomain[0] === 'auto'
430
+ ? groupMin - data.fullDataFormatter.grid.groupAxis.scalePadding
431
+ : data.fullDataFormatter.grid.groupAxis.scaleDomain[0] as number - data.fullDataFormatter.grid.groupAxis.scalePadding
432
+ const groupScaleDomainMax = data.fullDataFormatter.grid.groupAxis.scaleDomain[1] === 'auto'
433
+ ? groupMax + data.fullDataFormatter.grid.groupAxis.scalePadding
434
+ : data.fullDataFormatter.grid.groupAxis.scaleDomain[1] as number + data.fullDataFormatter.grid.groupAxis.scalePadding
435
+
436
+ return [groupScaleDomainMin, groupScaleDomainMax]
437
+ }),
438
+ shareReplay(1)
439
+ )
440
+
441
+ const groupScale$ = combineLatest({
442
+ groupScaleDomain: groupScaleDomain$,
443
+ gridAxesSize: gridAxesSize$
444
+ }).pipe(
445
+ takeUntil(destroy$),
446
+ switchMap(async (d) => d),
447
+ map(data => {
448
+ const groupScale: d3.ScaleLinear<number, number> = d3.scaleLinear()
449
+ .domain(data.groupScaleDomain)
450
+ .range([0, data.gridAxesSize.width])
451
+ return groupScale
452
+ })
453
+ )
454
+
455
+ const groupLabels$ = computedData$.pipe(
456
+ map(computedData => (computedData[0] ?? []).map(d => d.groupLabel))
457
+ )
458
+
459
+ const tickTextAlign$: Observable<TextAlign> = combineLatest({
460
+ fullDataFormatter: fullDataFormatter$,
461
+ fullParams: fullParams$
462
+ }).pipe(
463
+ takeUntil(destroy$),
464
+ switchMap(async (d) => d),
465
+ map(data => {
466
+ let textAnchor: 'start' | 'middle' | 'end' = 'middle'
467
+ let dominantBaseline: 'auto' | 'middle' | 'hanging' = 'hanging'
468
+
469
+ if (data.fullDataFormatter.grid.groupAxis.position === 'bottom') {
470
+ textAnchor = data.fullParams.tickTextRotate
471
+ ? 'end'
472
+ : 'middle'
473
+ dominantBaseline = 'hanging'
474
+ } else if (data.fullDataFormatter.grid.groupAxis.position === 'top') {
475
+ textAnchor = data.fullParams.tickTextRotate
476
+ ? 'end'
477
+ : 'middle'
478
+ dominantBaseline = 'auto'
479
+ } else if (data.fullDataFormatter.grid.groupAxis.position === 'left') {
480
+ textAnchor = 'end'
481
+ dominantBaseline = 'middle'
482
+ } else if (data.fullDataFormatter.grid.groupAxis.position === 'right') {
483
+ textAnchor = 'start'
484
+ dominantBaseline = 'middle'
485
+ }
486
+ return {
487
+ textAnchor,
488
+ dominantBaseline
489
+ }
490
+ })
491
+ )
492
+
493
+ const axisLabelAlign$: Observable<TextAlign> = fullDataFormatter$.pipe(
494
+ takeUntil(destroy$),
495
+ map(d => {
496
+ let textAnchor: 'start' | 'middle' | 'end' = 'start'
497
+ let dominantBaseline: 'auto' | 'middle' | 'hanging' = 'hanging'
498
+
499
+ if (d.grid.groupAxis.position === 'bottom') {
500
+ dominantBaseline = 'hanging'
501
+ } else if (d.grid.groupAxis.position === 'top') {
502
+ dominantBaseline = 'auto'
503
+ } else if (d.grid.groupAxis.position === 'left') {
504
+ textAnchor = 'end'
505
+ } else if (d.grid.groupAxis.position === 'right') {
506
+ textAnchor = 'start'
507
+ }
508
+ if (d.grid.valueAxis.position === 'left') {
509
+ textAnchor = 'start'
510
+ } else if (d.grid.valueAxis.position === 'right') {
511
+ textAnchor = 'end'
512
+ } else if (d.grid.valueAxis.position === 'bottom') {
513
+ dominantBaseline = 'auto'
514
+ } else if (d.grid.valueAxis.position === 'top') {
515
+ dominantBaseline = 'hanging'
516
+ }
517
+ return {
518
+ textAnchor,
519
+ dominantBaseline
520
+ }
521
+ })
522
+ )
523
+
524
+ combineLatest({
525
+ axisSelection: axisSelection$,
526
+ params: fullParams$,
527
+ tickTextAlign: tickTextAlign$,
528
+ axisLabelAlign: axisLabelAlign$,
529
+ gridAxesSize: gridAxesSize$,
530
+ fullDataFormatter: fullDataFormatter$,
531
+ chartParams: fullChartParams$,
532
+ groupScale: groupScale$,
533
+ groupScaleDomain: groupScaleDomain$,
534
+ groupLabels: groupLabels$,
535
+ textTransform: textTransform$,
536
+ // tickTextFormatter: tickTextFormatter$
537
+ }).pipe(
538
+ takeUntil(destroy$),
539
+ switchMap(async (d) => d),
540
+ ).subscribe(data => {
541
+
542
+ renderAxis({
543
+ selection: data.axisSelection,
544
+ xAxisClassName,
545
+ groupingLabelClassName,
546
+ params: data.params,
547
+ tickTextAlign: data.tickTextAlign,
548
+ axisLabelAlign: data.axisLabelAlign,
549
+ gridAxesSize: data.gridAxesSize,
550
+ fullDataFormatter: data.fullDataFormatter,
551
+ chartParams: data.chartParams,
552
+ groupScale: data.groupScale,
553
+ groupScaleDomain: data.groupScaleDomain,
554
+ groupLabels: data.groupLabels,
555
+ textTransform: data.textTransform,
556
+ // tickTextFormatter: data.tickTextFormatter
557
+ })
558
+ })
559
+
560
+ return () => {
561
+ destroy$.next(undefined)
562
+ }
497
563
  })