@orbcharts/plugins-basic 3.0.0-alpha.62 → 3.0.0-alpha.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orbcharts/plugins-basic",
3
- "version": "3.0.0-alpha.62",
3
+ "version": "3.0.0-alpha.64",
4
4
  "description": "plugins for OrbCharts",
5
5
  "author": "Blue Planet Inc.",
6
6
  "license": "Apache-2.0",
@@ -35,7 +35,7 @@
35
35
  "vite-plugin-dts": "^3.7.3"
36
36
  },
37
37
  "dependencies": {
38
- "@orbcharts/core": "^3.0.0-alpha.57",
38
+ "@orbcharts/core": "^3.0.0-alpha.58",
39
39
  "d3": "^7.8.5",
40
40
  "rxjs": "^7.8.1"
41
41
  }
@@ -69,33 +69,54 @@ interface TextAlign {
69
69
  // const groupingLabelClassName = getClassName(pluginName, 'groupingLabel')
70
70
  const defaultTickSize = 6
71
71
 
72
- function renderAxis ({ selection, xAxisClassName, groupingLabelClassName, params, tickTextAlign, axisLabelAlign, gridAxesSize, fullDataFormatter, chartParams, groupScale, groupScaleDomain, groupLabels, textTransform }: {
72
+ function renderAxisLabel ({ selection, groupingLabelClassName, fullParams, axisLabelAlign, gridAxesSize, fullDataFormatter, chartParams, textReverseTransform }: {
73
73
  selection: d3.Selection<SVGGElement, any, any, any>,
74
- xAxisClassName: string
75
74
  groupingLabelClassName: string
76
- params: BaseGroupAxisParams
77
- tickTextAlign: TextAlign
75
+ fullParams: BaseGroupAxisParams
78
76
  axisLabelAlign: TextAlign
79
77
  gridAxesSize: { width: number, height: number }
80
78
  fullDataFormatter: DataFormatterGrid,
81
79
  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)
80
+ textReverseTransform: string
88
81
  }) {
89
-
90
- const xAxisSelection = selection
91
- .selectAll<SVGGElement, BaseGroupAxisParams>(`g.${xAxisClassName}`)
92
- .data([params])
93
- .join('g')
94
- .classed(xAxisClassName, true)
82
+
83
+ const offsetX = fullParams.tickPadding + fullParams.labelOffset[0]
84
+ const offsetY = fullParams.tickPadding + fullParams.labelOffset[1]
85
+ let labelX = 0
86
+ let labelY = 0
87
+ if (fullDataFormatter.grid.groupAxis.position === 'bottom') {
88
+ labelY = offsetY
89
+ if (fullDataFormatter.grid.valueAxis.position === 'left') {
90
+ labelX = offsetX
91
+ } else if (fullDataFormatter.grid.valueAxis.position === 'right') {
92
+ labelX = - offsetX
93
+ }
94
+ } else if (fullDataFormatter.grid.groupAxis.position === 'top') {
95
+ labelY = - offsetY
96
+ if (fullDataFormatter.grid.valueAxis.position === 'left') {
97
+ labelX = offsetX
98
+ } else if (fullDataFormatter.grid.valueAxis.position === 'right') {
99
+ labelX = - offsetX
100
+ }
101
+ } else if (fullDataFormatter.grid.groupAxis.position === 'left') {
102
+ labelX = - offsetX
103
+ if (fullDataFormatter.grid.valueAxis.position === 'bottom') {
104
+ labelY = - offsetY
105
+ } else if (fullDataFormatter.grid.valueAxis.position === 'top') {
106
+ labelY = offsetY
107
+ }
108
+ } else if (fullDataFormatter.grid.groupAxis.position === 'right') {
109
+ labelX = offsetX
110
+ if (fullDataFormatter.grid.valueAxis.position === 'bottom') {
111
+ labelY = - offsetY
112
+ } else if (fullDataFormatter.grid.valueAxis.position === 'top') {
113
+ labelY = offsetY
114
+ }
115
+ }
95
116
 
96
117
  const axisLabelSelection = selection
97
118
  .selectAll<SVGGElement, BaseGroupAxisParams>(`g.${groupingLabelClassName}`)
98
- .data([params])
119
+ .data([fullParams])
99
120
  .join('g')
100
121
  .classed(groupingLabelClassName, true)
101
122
  .each((d, i, g) => {
@@ -114,34 +135,76 @@ function renderAxis ({ selection, xAxisClassName, groupingLabelClassName, params
114
135
  .attr('text-anchor', axisLabelAlign.textAnchor)
115
136
  .attr('dominant-baseline', axisLabelAlign.dominantBaseline)
116
137
  .attr('font-size', chartParams.styles.textSize)
117
- .style('fill', getColor(params.labelColorType, chartParams))
118
- // .style('transform', textTransform)
138
+ .style('fill', getColor(fullParams.labelColorType, chartParams))
139
+ .style('transform', textReverseTransform)
140
+ // 偏移使用 x, y 而非 transform 才不會受到外層 scale 變形影響
141
+ .attr('x', labelX)
142
+ .attr('y', labelY)
119
143
  .text(d => fullDataFormatter.grid.groupAxis.label)
120
144
  })
121
- .attr('transform', d => `translate(${gridAxesSize.width + d.tickPadding + params.labelOffset[0]}, ${- d.tickPadding - defaultTickSize - params.labelOffset[1]})`)
145
+ .attr('transform', d => `translate(${gridAxesSize.width}, 0)`)
146
+ // .attr('transform', d => `translate(${gridAxesSize.width + d.tickPadding + fullParams.labelOffset[0]}, ${- d.tickPadding - fullParams.labelOffset[1]})`)
147
+
148
+ }
149
+
150
+ function renderAxis ({ selection, xAxisClassName, fullParams, tickTextAlign, gridAxesSize, fullDataFormatter, chartParams, groupScale, groupScaleDomain, groupLabels, textReverseTransformWithRotate }: {
151
+ selection: d3.Selection<SVGGElement, any, any, any>,
152
+ xAxisClassName: string
153
+ fullParams: BaseGroupAxisParams
154
+ tickTextAlign: TextAlign
155
+ gridAxesSize: { width: number, height: number }
156
+ fullDataFormatter: DataFormatterGrid,
157
+ chartParams: ChartParams
158
+ groupScale: d3.ScaleLinear<number, number>
159
+ groupScaleDomain: number[]
160
+ groupLabels: string[]
161
+ textReverseTransformWithRotate: string
162
+ }) {
163
+
164
+ const xAxisSelection = selection
165
+ .selectAll<SVGGElement, BaseGroupAxisParams>(`g.${xAxisClassName}`)
166
+ .data([fullParams])
167
+ .join('g')
168
+ .classed(xAxisClassName, true)
122
169
 
123
170
  // 計算所有範圍內groupLabels數量(顯示所有刻度)
124
171
  const allTicksAmount = Math.floor(groupScaleDomain[1]) - Math.ceil(groupScaleDomain[0]) + 1
125
172
 
173
+ // 刻度文字偏移
174
+ let tickPadding = 0
175
+ let textX = 0
176
+ if (fullDataFormatter.grid.groupAxis.position === 'left') {
177
+ tickPadding = 0
178
+ textX = - fullParams.tickPadding
179
+ } else if (fullDataFormatter.grid.groupAxis.position === 'right') {
180
+ tickPadding = 0
181
+ textX = fullParams.tickPadding
182
+ } else if (fullDataFormatter.grid.groupAxis.position === 'bottom') {
183
+ tickPadding = fullParams.tickPadding
184
+ textX = 0
185
+ } else if (fullDataFormatter.grid.groupAxis.position === 'top') {
186
+ tickPadding = - fullParams.tickPadding
187
+ textX = - 0
188
+ }
189
+
126
190
  // 設定X軸刻度
127
- // const xAxis = d3.axisBottom(groupScale)
128
- const xAxis = d3.axisTop(groupScale)
191
+ const xAxis = d3.axisBottom(groupScale)
129
192
  .scale(groupScale)
130
- .ticks(params.ticks === 'all'
193
+ .ticks(fullParams.ticks === 'all'
131
194
  ? allTicksAmount
132
- : params.ticks > allTicksAmount
195
+ : fullParams.ticks > allTicksAmount
133
196
  ? allTicksAmount // 不顯示超過groupLabels數量的刻度
134
- : params.ticks)
135
- .tickSize(params.tickFullLine == true
136
- ? -gridAxesSize.height
137
- : defaultTickSize)
197
+ : fullParams.ticks)
198
+ .tickSize(fullParams.tickFullLine == true
199
+ ? gridAxesSize.height
200
+ : - defaultTickSize)
138
201
  .tickSizeOuter(0)
139
202
  .tickFormat((groupIndex: number) => {
140
203
  // 用index對應到groupLabel
141
204
  const groupLabel = groupLabels[groupIndex] ?? '' // 非整數index不顯示
142
- return parseTickFormatValue(groupLabel, params.tickFormat)
205
+ return parseTickFormatValue(groupLabel, fullParams.tickFormat)
143
206
  })
144
- .tickPadding(params.tickPadding)
207
+ .tickPadding(tickPadding)
145
208
 
146
209
  const xAxisEl = xAxisSelection
147
210
  .transition()
@@ -152,13 +215,13 @@ function renderAxis ({ selection, xAxisClassName, groupingLabelClassName, params
152
215
 
153
216
  xAxisEl.selectAll('line')
154
217
  .style('fill', 'none')
155
- .style('stroke', params.tickLineVisible == true ? getColor(params.tickColorType, chartParams) : 'none')
156
- .style('stroke-dasharray', params.tickFullLineDasharray)
218
+ .style('stroke', fullParams.tickLineVisible == true ? getColor(fullParams.tickColorType, chartParams) : 'none')
219
+ .style('stroke-dasharray', fullParams.tickFullLineDasharray)
157
220
  .attr('pointer-events', 'none')
158
221
 
159
222
  xAxisEl.selectAll('path')
160
223
  .style('fill', 'none')
161
- .style('stroke', params.axisLineVisible == true ? getColor(params.axisLineColorType, chartParams) : 'none')
224
+ .style('stroke', fullParams.axisLineVisible == true ? getColor(fullParams.axisLineColorType, chartParams) : 'none')
162
225
  .style('shape-rendering', 'crispEdges')
163
226
 
164
227
  // const xText = xAxisEl.selectAll('text')
@@ -174,18 +237,22 @@ function renderAxis ({ selection, xAxisClassName, groupingLabelClassName, params
174
237
  // .attr('text-anchor', tickTextAlign.textAnchor)
175
238
  // .attr('dominant-baseline', tickTextAlign.dominantBaseline)
176
239
  // .attr('transform-origin', `0 -${params.tickPadding + defaultTickSize}`)
177
- // .style('transform', textTransform)
240
+ // .style('transform', textReverseTransform)
178
241
  // })
179
242
  const xText = xAxisSelection.selectAll('text')
180
243
  // .style('font-family', 'sans-serif')
181
244
  .attr('font-size', chartParams.styles.textSize)
182
245
  // .style('font-weight', 'bold')
183
- .style('color', getColor(params.tickTextColorType, chartParams))
246
+ .style('color', getColor(fullParams.tickTextColorType, chartParams))
184
247
  .attr('text-anchor', tickTextAlign.textAnchor)
185
248
  .attr('dominant-baseline', tickTextAlign.dominantBaseline)
186
- .attr('transform-origin', `0 -${params.tickPadding + defaultTickSize}`)
187
- .style('transform', textTransform)
188
-
249
+ .attr('x', textX)
250
+ .style('transform', textReverseTransformWithRotate)
251
+
252
+ // 抵消掉預設的偏移
253
+ // if (fullDataFormatter.grid.groupAxis.position === 'left' || fullDataFormatter.grid.groupAxis.position === 'right') {
254
+ xText.attr('dy', 0)
255
+ // }
189
256
 
190
257
  return xAxisSelection
191
258
  }
@@ -297,7 +364,7 @@ export const createBaseGroupAxis: BasePluginFn<BaseGroupAxisContext> = ((pluginN
297
364
  // })
298
365
  // )
299
366
 
300
- // const textTransform$: Observable<string> = new Observable(subscriber => {
367
+ // const textReverseTransform$: Observable<string> = new Observable(subscriber => {
301
368
  // combineLatest({
302
369
  // params: fullParams$,
303
370
  // gridAxesTransform: gridAxesTransform$
@@ -338,31 +405,34 @@ export const createBaseGroupAxis: BasePluginFn<BaseGroupAxisContext> = ((pluginN
338
405
  // }
339
406
  // }),
340
407
  // )
341
- const textTransform$ = combineLatest({
342
- fullParams: fullParams$,
343
- fullDataFormatter: fullDataFormatter$,
408
+ const textReverseTransform$ = combineLatest({
344
409
  gridAxesReverseTransform: gridAxesReverseTransform$,
345
410
  gridContainerPosition: gridContainerPosition$
346
411
  }).pipe(
347
412
  takeUntil(destroy$),
348
413
  switchMap(async (d) => d),
349
414
  map(data => {
350
- const axisReverseTranslateValue = `translate(${data.gridAxesReverseTransform.translate[0]}px, ${data.gridAxesReverseTransform.translate[1]}px)`
351
- const axisReverseRotateValue = `rotate(${data.gridAxesReverseTransform.rotate}deg) rotateX(${data.gridAxesReverseTransform.rotateX}deg) rotateY(${data.gridAxesReverseTransform.rotateY}deg)`
352
- const containerScaleReverseScaleValue = `scale(${1 / data.gridContainerPosition[0].scale[0]}, ${1 / data.gridContainerPosition[0].scale[1]})`
353
- const tickTextRotateDeg = (data.fullDataFormatter.grid.groupAxis.position === 'left' && data.fullDataFormatter.grid.valueAxis.position === 'top')
354
- || (data.fullDataFormatter.grid.groupAxis.position === 'right' && data.fullDataFormatter.grid.valueAxis.position === 'bottom')
355
- ? data.fullParams.tickTextRotate + 180 // 修正文字倒轉
356
- : data.fullParams.tickTextRotate
357
-
358
- const textRotateValue = `rotate(${tickTextRotateDeg}deg)`
359
-
360
- // 必須按照順序(先抵消外層rotate,再抵消最外層scale,最後再做本身的rotate)
361
- return `${axisReverseTranslateValue} ${axisReverseRotateValue} ${containerScaleReverseScaleValue} ${textRotateValue}`
415
+ // const axisReverseTranslateValue = `translate(${data.gridAxesReverseTransform.translate[0]}px, ${data.gridAxesReverseTransform.translate[1]}px)`
416
+ const axesRotateXYReverseValue = `rotateX(${data.gridAxesReverseTransform.rotateX}deg) rotateY(${data.gridAxesReverseTransform.rotateY}deg)`
417
+ const axesRotateReverseValue = `rotate(${data.gridAxesReverseTransform.rotate}deg)`
418
+ const containerScaleReverseValue = `scale(${1 / data.gridContainerPosition[0].scale[0]}, ${1 / data.gridContainerPosition[0].scale[1]})`
419
+ // 必須按照順序(先抵消外層rotate,再抵消最外層scale)
420
+ return `${axesRotateXYReverseValue} ${axesRotateReverseValue} ${containerScaleReverseValue}`
362
421
  }),
363
422
  distinctUntilChanged()
364
423
  )
365
424
 
425
+ const textReverseTransformWithRotate$ = combineLatest({
426
+ textReverseTransform: textReverseTransform$,
427
+ fullParams: fullParams$,
428
+ }).pipe(
429
+ takeUntil(destroy$),
430
+ switchMap(async (d) => d),
431
+ map(data => {
432
+ // 必須按照順序(先抵消外層rotate,再抵消最外層scale,最後再做本身的rotate)
433
+ return `${data.textReverseTransform} rotate(${data.fullParams.tickTextRotate}deg)`
434
+ })
435
+ )
366
436
 
367
437
  // 使用pointScale計算非連續性比例尺
368
438
  // const groupScale$: Observable<d3.ScalePoint<string>> = new Observable(subscriber => {
@@ -469,7 +539,7 @@ export const createBaseGroupAxis: BasePluginFn<BaseGroupAxisContext> = ((pluginN
469
539
  dominantBaseline = 'hanging'
470
540
  } else if (data.fullDataFormatter.grid.groupAxis.position === 'top') {
471
541
  textAnchor = data.fullParams.tickTextRotate
472
- ? 'end'
542
+ ? 'start'
473
543
  : 'middle'
474
544
  dominantBaseline = 'auto'
475
545
  } else if (data.fullDataFormatter.grid.groupAxis.position === 'left') {
@@ -519,7 +589,7 @@ export const createBaseGroupAxis: BasePluginFn<BaseGroupAxisContext> = ((pluginN
519
589
 
520
590
  combineLatest({
521
591
  axisSelection: axisSelection$,
522
- params: fullParams$,
592
+ fullParams: fullParams$,
523
593
  tickTextAlign: tickTextAlign$,
524
594
  axisLabelAlign: axisLabelAlign$,
525
595
  gridAxesSize: gridAxesSize$,
@@ -528,7 +598,8 @@ export const createBaseGroupAxis: BasePluginFn<BaseGroupAxisContext> = ((pluginN
528
598
  groupScale: groupScale$,
529
599
  groupScaleDomain: groupScaleDomain$,
530
600
  groupLabels: groupLabels$,
531
- textTransform: textTransform$,
601
+ textReverseTransform: textReverseTransform$,
602
+ textReverseTransformWithRotate: textReverseTransformWithRotate$,
532
603
  // tickTextFormatter: tickTextFormatter$
533
604
  }).pipe(
534
605
  takeUntil(destroy$),
@@ -538,18 +609,26 @@ export const createBaseGroupAxis: BasePluginFn<BaseGroupAxisContext> = ((pluginN
538
609
  renderAxis({
539
610
  selection: data.axisSelection,
540
611
  xAxisClassName,
541
- groupingLabelClassName,
542
- params: data.params,
612
+ fullParams: data.fullParams,
543
613
  tickTextAlign: data.tickTextAlign,
544
- axisLabelAlign: data.axisLabelAlign,
545
614
  gridAxesSize: data.gridAxesSize,
546
615
  fullDataFormatter: data.fullDataFormatter,
547
616
  chartParams: data.chartParams,
548
617
  groupScale: data.groupScale,
549
618
  groupScaleDomain: data.groupScaleDomain,
550
619
  groupLabels: data.groupLabels,
551
- textTransform: data.textTransform,
552
- // tickTextFormatter: data.tickTextFormatter
620
+ textReverseTransformWithRotate: data.textReverseTransformWithRotate,
621
+ })
622
+
623
+ renderAxisLabel({
624
+ selection: data.axisSelection,
625
+ groupingLabelClassName,
626
+ fullParams: data.fullParams,
627
+ axisLabelAlign: data.axisLabelAlign,
628
+ gridAxesSize: data.gridAxesSize,
629
+ fullDataFormatter: data.fullDataFormatter,
630
+ chartParams: data.chartParams,
631
+ textReverseTransform: data.textReverseTransform,
553
632
  })
554
633
  })
555
634
 
@@ -637,6 +637,7 @@ export const createBaseLegend: BasePluginFn<BaseLegendContext> = (pluginName: st
637
637
  itemSelection$.subscribe()
638
638
 
639
639
  return () => {
640
+ rootSelection.select(`g.${rootPositionClassName}`).remove()
640
641
  destroy$.next(undefined)
641
642
  }
642
643
  }
@@ -66,26 +66,52 @@ interface TextAlign {
66
66
  // const textClassName = getClassName(pluginName, 'text')
67
67
  const defaultTickSize = 6
68
68
 
69
- function renderAxis ({ selection, yAxisClassName, textClassName, fullParams, tickTextAlign, axisLabelAlign, gridAxesSize, fullDataFormatter, fullChartParams, valueScale, textTransform, minAndMax }: {
69
+ function renderAxisLabel ({ selection, textClassName, fullParams, axisLabelAlign, gridAxesSize, fullDataFormatter, fullChartParams, textReverseTransform }: {
70
70
  selection: d3.Selection<SVGGElement, any, any, any>,
71
- yAxisClassName: string
72
71
  textClassName: string
73
72
  fullParams: BaseValueAxisParams
74
- tickTextAlign: TextAlign
75
73
  axisLabelAlign: TextAlign
76
74
  gridAxesSize: { width: number, height: number }
77
75
  fullDataFormatter: DataFormatterGrid,
78
76
  fullChartParams: ChartParams
79
- valueScale: d3.ScaleLinear<number, number>
80
- textTransform: string,
81
- minAndMax: [number, number]
77
+ textReverseTransform: string,
82
78
  }) {
83
-
84
- const yAxisSelection = selection
85
- .selectAll<SVGGElement, BaseValueAxisParams>(`g.${yAxisClassName}`)
86
- .data([fullParams])
87
- .join('g')
88
- .classed(yAxisClassName, true)
79
+ const offsetX = fullParams.tickPadding - fullParams.labelOffset[0]
80
+ const offsetY = fullParams.tickPadding + fullParams.labelOffset[1]
81
+ let labelX = 0
82
+ let labelY = 0
83
+ if (fullDataFormatter.grid.groupAxis.position === 'bottom') {
84
+ // labelY = - gridAxesSize.height - offsetY
85
+ labelY = - offsetY
86
+ if (fullDataFormatter.grid.valueAxis.position === 'left') {
87
+ labelX = - offsetX
88
+ } else if (fullDataFormatter.grid.valueAxis.position === 'right') {
89
+ labelX = offsetX
90
+ }
91
+ } else if (fullDataFormatter.grid.groupAxis.position === 'top') {
92
+ // labelY = gridAxesSize.height + offsetY
93
+ labelY = offsetY
94
+ if (fullDataFormatter.grid.valueAxis.position === 'left') {
95
+ labelX = - offsetX
96
+ } else if (fullDataFormatter.grid.valueAxis.position === 'right') {
97
+ labelX = offsetX
98
+ }
99
+ } else if (fullDataFormatter.grid.groupAxis.position === 'left') {
100
+ // labelX = gridAxesSize.width + offsetX
101
+ labelX = offsetX
102
+ if (fullDataFormatter.grid.valueAxis.position === 'bottom') {
103
+ labelY = offsetY
104
+ } else if (fullDataFormatter.grid.valueAxis.position === 'top') {
105
+ labelY = - offsetY
106
+ }
107
+ } else if (fullDataFormatter.grid.groupAxis.position === 'right') {
108
+ labelX = - offsetX
109
+ if (fullDataFormatter.grid.valueAxis.position === 'bottom') {
110
+ labelY = offsetY
111
+ } else if (fullDataFormatter.grid.valueAxis.position === 'top') {
112
+ labelY = - offsetY
113
+ }
114
+ }
89
115
 
90
116
  const axisLabelSelection = selection
91
117
  .selectAll<SVGGElement, BaseValueAxisParams>(`g.${textClassName}`)
@@ -109,10 +135,36 @@ function renderAxis ({ selection, yAxisClassName, textClassName, fullParams, tic
109
135
  .attr('dominant-baseline', axisLabelAlign.dominantBaseline)
110
136
  .attr('font-size', fullChartParams.styles.textSize)
111
137
  .style('fill', getColor(fullParams.labelColorType, fullChartParams))
112
- // .style('transform', textTransform)
138
+ .style('transform', textReverseTransform)
139
+ // 偏移使用 x, y 而非 transform 才不會受到外層 scale 變形影響
140
+ .attr('x', labelX)
141
+ .attr('y', labelY)
113
142
  .text(d => fullDataFormatter.grid.valueAxis.label)
114
143
  })
115
- .attr('transform', d => `translate(${- d.tickPadding + fullParams.labelOffset[0]}, ${gridAxesSize.height + d.tickPadding + fullParams.labelOffset[1]})`)
144
+ .attr('transform', d => `translate(0, ${gridAxesSize.height})`)
145
+ // .attr('transform', d => `translate(${- fullParams.tickPadding + fullParams.labelOffset[0]}, ${gridAxesSize.height + fullParams.tickPadding + fullParams.labelOffset[1]})`)
146
+
147
+
148
+ }
149
+
150
+ function renderAxis ({ selection, yAxisClassName, fullParams, tickTextAlign, gridAxesSize, fullDataFormatter, fullChartParams, valueScale, textReverseTransformWithRotate, minAndMax }: {
151
+ selection: d3.Selection<SVGGElement, any, any, any>,
152
+ yAxisClassName: string
153
+ fullParams: BaseValueAxisParams
154
+ tickTextAlign: TextAlign
155
+ gridAxesSize: { width: number, height: number }
156
+ fullDataFormatter: DataFormatterGrid,
157
+ fullChartParams: ChartParams
158
+ valueScale: d3.ScaleLinear<number, number>
159
+ textReverseTransformWithRotate: string,
160
+ minAndMax: [number, number]
161
+ }) {
162
+
163
+ const yAxisSelection = selection
164
+ .selectAll<SVGGElement, BaseValueAxisParams>(`g.${yAxisClassName}`)
165
+ .data([fullParams])
166
+ .join('g')
167
+ .classed(yAxisClassName, true)
116
168
 
117
169
  const valueLength = minAndMax[1] - minAndMax[0]
118
170
 
@@ -120,6 +172,23 @@ function renderAxis ({ selection, yAxisClassName, textClassName, fullParams, tic
120
172
  // .domain([0, 150])
121
173
  // .range([416.5, 791.349])
122
174
 
175
+ // 刻度文字偏移
176
+ let tickPadding = 0
177
+ let textY = 0
178
+ if (fullDataFormatter.grid.valueAxis.position === 'left') {
179
+ tickPadding = fullParams.tickPadding
180
+ textY = 0
181
+ } else if (fullDataFormatter.grid.valueAxis.position === 'right') {
182
+ tickPadding = - fullParams.tickPadding
183
+ textY = 0
184
+ } else if (fullDataFormatter.grid.valueAxis.position === 'bottom') {
185
+ tickPadding = 0
186
+ textY = fullParams.tickPadding
187
+ } else if (fullDataFormatter.grid.valueAxis.position === 'top') {
188
+ tickPadding = 0
189
+ textY = - fullParams.tickPadding
190
+ }
191
+
123
192
  // 設定Y軸刻度
124
193
  const yAxis = d3.axisLeft(valueScale)
125
194
  .scale(valueScale)
@@ -132,7 +201,7 @@ function renderAxis ({ selection, yAxisClassName, textClassName, fullParams, tic
132
201
  .tickSize(fullParams.tickFullLine == true
133
202
  ? -gridAxesSize.width
134
203
  : defaultTickSize)
135
- .tickPadding(fullParams.tickPadding)
204
+ .tickPadding(tickPadding)
136
205
 
137
206
  const yAxisEl = yAxisSelection
138
207
  .transition()
@@ -158,8 +227,14 @@ function renderAxis ({ selection, yAxisClassName, textClassName, fullParams, tic
158
227
  .style('color', getColor(fullParams.tickTextColorType, fullChartParams))
159
228
  .attr('text-anchor', tickTextAlign.textAnchor)
160
229
  .attr('dominant-baseline', tickTextAlign.dominantBaseline)
161
- .attr('transform-origin', `-${fullParams.tickPadding + defaultTickSize} 0`)
162
- yText.style('transform', textTransform)
230
+ // .attr('dy', 0)
231
+ .attr('y', textY)
232
+ yText.style('transform', textReverseTransformWithRotate)
233
+
234
+ // 抵消掉預設的偏移
235
+ if (fullDataFormatter.grid.valueAxis.position === 'bottom' || fullDataFormatter.grid.valueAxis.position === 'top') {
236
+ yText.attr('dy', 0)
237
+ }
163
238
 
164
239
  return yAxisSelection
165
240
  }
@@ -263,7 +338,7 @@ export const createBaseValueAxis: BasePluginFn<BaseLinesContext> = (pluginName:
263
338
  // layout$
264
339
  // })
265
340
 
266
- // const textTransform$: Observable<string> = new Observable(subscriber => {
341
+ // const textReverseTransform$: Observable<string> = new Observable(subscriber => {
267
342
  // combineLatest({
268
343
  // fullParams: fullParams$,
269
344
  // layout: layout$
@@ -304,31 +379,35 @@ export const createBaseValueAxis: BasePluginFn<BaseLinesContext> = (pluginName:
304
379
  // }
305
380
  // }),
306
381
  // )
307
- const textTransform$ = combineLatest({
308
- fullParams: fullParams$,
309
- fullDataFormatter: fullDataFormatter$,
382
+ const textReverseTransform$ = combineLatest({
310
383
  gridAxesReverseTransform: gridAxesReverseTransform$,
311
384
  gridContainerPosition: gridContainerPosition$
312
385
  }).pipe(
313
386
  takeUntil(destroy$),
314
387
  switchMap(async (d) => d),
315
388
  map(data => {
316
- const axisReverseTranslateValue = `translate(${data.gridAxesReverseTransform.translate[0]}px, ${data.gridAxesReverseTransform.translate[1]}px)`
317
- const axisReverseRotateValue = `rotate(${data.gridAxesReverseTransform.rotate}deg) rotateX(${data.gridAxesReverseTransform.rotateX}deg) rotateY(${data.gridAxesReverseTransform.rotateY}deg)`
318
- const containerScaleReverseScaleValue = `scale(${1 / data.gridContainerPosition[0].scale[0]}, ${1 / data.gridContainerPosition[0].scale[1]})`
319
- const tickTextRotateDeg = (data.fullDataFormatter.grid.groupAxis.position === 'left' && data.fullDataFormatter.grid.valueAxis.position === 'top')
320
- || (data.fullDataFormatter.grid.groupAxis.position === 'right' && data.fullDataFormatter.grid.valueAxis.position === 'bottom')
321
- ? data.fullParams.tickTextRotate + 180 // 修正文字倒轉
322
- : data.fullParams.tickTextRotate
323
-
324
- const textRotateValue = `rotate(${tickTextRotateDeg}deg)`
325
-
326
- // 必須按照順序(先抵消外層rotate,再抵消最外層scale,最後再做本身的rotate)
327
- return `${axisReverseTranslateValue} ${axisReverseRotateValue} ${containerScaleReverseScaleValue} ${textRotateValue}`
389
+ // const axisReverseTranslateValue = `translate(${data.gridAxesReverseTransform.translate[0]}px, ${data.gridAxesReverseTransform.translate[1]}px)`
390
+ const axesRotateXYReverseValue = `rotateX(${data.gridAxesReverseTransform.rotateX}deg) rotateY(${data.gridAxesReverseTransform.rotateY}deg)`
391
+ const axesRotateReverseValue = `rotate(${data.gridAxesReverseTransform.rotate}deg)`
392
+ const containerScaleReverseValue = `scale(${1 / data.gridContainerPosition[0].scale[0]}, ${1 / data.gridContainerPosition[0].scale[1]})`
393
+ // 必須按照順序(先抵消外層rotate,再抵消最外層scale)
394
+ return `${axesRotateXYReverseValue} ${axesRotateReverseValue} ${containerScaleReverseValue}`
328
395
  }),
329
396
  distinctUntilChanged()
330
397
  )
331
398
 
399
+ const textReverseTransformWithRotate$ = combineLatest({
400
+ textReverseTransform: textReverseTransform$,
401
+ fullParams: fullParams$,
402
+ }).pipe(
403
+ takeUntil(destroy$),
404
+ switchMap(async (d) => d),
405
+ map(data => {
406
+ // 必須按照順序(先抵消外層rotate,再抵消最外層scale,最後再做本身的rotate)
407
+ return `${data.textReverseTransform} rotate(${data.fullParams.tickTextRotate}deg)`
408
+ })
409
+ )
410
+
332
411
  const minAndMax$: Observable<[number, number]> = new Observable(subscriber => {
333
412
  combineLatest({
334
413
  fullDataFormatter: fullDataFormatter$,
@@ -383,23 +462,31 @@ export const createBaseValueAxis: BasePluginFn<BaseLinesContext> = (pluginName:
383
462
  })
384
463
  })
385
464
 
386
- const tickTextAlign$: Observable<TextAlign> = fullDataFormatter$.pipe(
465
+ const tickTextAlign$: Observable<TextAlign> = combineLatest({
466
+ fullDataFormatter: fullDataFormatter$,
467
+ fullParams: fullParams$
468
+ }).pipe(
387
469
  takeUntil(destroy$),
388
- map(d => {
470
+ switchMap(async (d) => d),
471
+ map(data => {
389
472
  let textAnchor: 'start' | 'middle' | 'end' = 'start'
390
473
  let dominantBaseline: 'auto' | 'middle' | 'hanging' = 'hanging'
391
474
 
392
- if (d.grid.valueAxis.position === 'left') {
475
+ if (data.fullDataFormatter.grid.valueAxis.position === 'left') {
393
476
  textAnchor = 'end'
394
477
  dominantBaseline = 'middle'
395
- } else if (d.grid.valueAxis.position === 'right') {
478
+ } else if (data.fullDataFormatter.grid.valueAxis.position === 'right') {
396
479
  textAnchor = 'start'
397
480
  dominantBaseline = 'middle'
398
- } else if (d.grid.valueAxis.position === 'bottom') {
399
- textAnchor = 'middle'
481
+ } else if (data.fullDataFormatter.grid.valueAxis.position === 'bottom') {
482
+ textAnchor = data.fullParams.tickTextRotate
483
+ ? 'end'
484
+ : 'middle'
400
485
  dominantBaseline = 'hanging'
401
- } else if (d.grid.valueAxis.position === 'top') {
402
- textAnchor = 'middle'
486
+ } else if (data.fullDataFormatter.grid.valueAxis.position === 'top') {
487
+ textAnchor = data.fullParams.tickTextRotate
488
+ ? 'start'
489
+ : 'middle'
403
490
  dominantBaseline = 'auto'
404
491
  }
405
492
  return {
@@ -451,7 +538,8 @@ export const createBaseValueAxis: BasePluginFn<BaseLinesContext> = (pluginName:
451
538
  fullDataFormatter: fullDataFormatter$,
452
539
  fullChartParams: fullChartParams$,
453
540
  valueScale: valueScale$,
454
- textTransform: textTransform$,
541
+ textReverseTransform: textReverseTransform$,
542
+ textReverseTransformWithRotate: textReverseTransformWithRotate$,
455
543
  minAndMax: minAndMax$
456
544
  }).pipe(
457
545
  takeUntil(destroy$),
@@ -461,17 +549,26 @@ export const createBaseValueAxis: BasePluginFn<BaseLinesContext> = (pluginName:
461
549
  renderAxis({
462
550
  selection: data.axisSelection,
463
551
  yAxisClassName,
464
- textClassName,
465
552
  fullParams: data.fullParams,
466
553
  tickTextAlign: data.tickTextAlign,
467
- axisLabelAlign: data.axisLabelAlign,
468
554
  gridAxesSize: data.gridAxesSize,
469
555
  fullDataFormatter: data.fullDataFormatter,
470
556
  fullChartParams: data.fullChartParams,
471
557
  valueScale: data.valueScale,
472
- textTransform: data.textTransform,
558
+ textReverseTransformWithRotate: data.textReverseTransformWithRotate,
473
559
  minAndMax: data.minAndMax
474
560
  })
561
+
562
+ renderAxisLabel({
563
+ selection: data.axisSelection,
564
+ textClassName,
565
+ fullParams: data.fullParams,
566
+ axisLabelAlign: data.axisLabelAlign,
567
+ gridAxesSize: data.gridAxesSize,
568
+ fullDataFormatter: data.fullDataFormatter,
569
+ fullChartParams: data.fullChartParams,
570
+ textReverseTransform: data.textReverseTransform,
571
+ })
475
572
  })
476
573
 
477
574
  return () => {
@@ -39,7 +39,7 @@ export const DEFAULT_GROUP_AREA_PARAMS: GroupAuxParams = {
39
39
  labelColorType: 'primary',
40
40
  labelTextColorType: 'background',
41
41
  labelTextFormat: text => text,
42
- labelPadding: 24,
42
+ labelPadding: 20,
43
43
  labelRotate: 0
44
44
  }
45
45
  DEFAULT_GROUP_AREA_PARAMS.labelTextFormat.toString = () => `text => text`