@orbcharts/plugins-basic 3.0.0-alpha.26 → 3.0.0-alpha.27

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,40 @@
1
+ import * as d3 from 'd3'
2
+ import {
3
+ combineLatest,
4
+ map,
5
+ switchMap,
6
+ takeUntil,
7
+ Observable,
8
+ Subject } from 'rxjs'
9
+ import {
10
+ defineGridPlugin } from '@orbcharts/core'
11
+ import { DEFAULT_GRID_LEGEND_PARAMS } from '../defaults'
12
+ import { createBaseLegend } from '../../base/BaseLegend'
13
+
14
+ const pluginName = 'GridLegend'
15
+
16
+ export const GridLegend = defineGridPlugin(pluginName, DEFAULT_GRID_LEGEND_PARAMS)(({ selection, rootSelection, observer, subject }) => {
17
+
18
+ const destroy$ = new Subject()
19
+
20
+ const seriesLabels$: Observable<string[]> = observer.SeriesDataMap$.pipe(
21
+ takeUntil(destroy$),
22
+ map(data => {
23
+ return Array.from(data.keys())
24
+ })
25
+ )
26
+
27
+ const unsubscribeBaseLegend = createBaseLegend(pluginName, {
28
+ rootSelection,
29
+ seriesLabels$,
30
+ fullParams$: observer.fullParams$,
31
+ layout$: observer.layout$,
32
+ fullChartParams$: observer.fullChartParams$
33
+ })
34
+
35
+ return () => {
36
+ destroy$.next(undefined)
37
+ unsubscribeBaseLegend()
38
+ }
39
+ })
40
+
package/src/grid/types.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { ColorType } from '@orbcharts/core'
2
+ import type { BaseLegendParams } from '../base/BaseLegend'
2
3
 
3
4
  // export type LineType = 'line' | 'area' | 'gradientArea'
4
5
  // export type BarType = 'rect' | 'triangle'
@@ -100,3 +101,4 @@ export interface ScalingAreaParams {
100
101
 
101
102
  }
102
103
 
104
+ export interface GridLegendParams extends BaseLegendParams {}
@@ -6,6 +6,7 @@ import type {
6
6
  PieLabelsPluginParams,
7
7
  SeriesLegendParams } from './types'
8
8
 
9
+
9
10
  export const DEFAULT_BUBBLES_PLUGIN_PARAMS: BubblesPluginParams = {
10
11
  force: {
11
12
  strength: 0.03, // 泡泡引力
@@ -87,7 +88,11 @@ export const DEFAULT_SERIES_LEGEND_PARAMS: SeriesLegendParams = {
87
88
  justify: 'end',
88
89
  padding: 28,
89
90
  // offset: [0, 0],
91
+ backgroundFill: 'none',
92
+ backgroundStroke: 'none',
90
93
  gap: 10,
91
- rectRadius: 0,
94
+ listRectWidth: 14,
95
+ listRectHeight: 14,
96
+ listRectRadius: 0,
92
97
  // highlightEvent: false
93
98
  }
@@ -8,90 +8,10 @@ import {
8
8
  Subject } from 'rxjs'
9
9
  import {
10
10
  defineSeriesPlugin } from '@orbcharts/core'
11
- import type {
12
- ChartParams } from '@orbcharts/core'
13
- import type { SeriesLegendParams } from '../types'
14
11
  import { DEFAULT_SERIES_LEGEND_PARAMS } from '../defaults'
15
- import { getSeriesColor, getClassName } from '../../utils/orbchartsUtils'
16
- import { measureTextWidth } from '../../utils/commonUtils'
17
-
18
-
19
- // 第1層 - 定位的容器(絕對位置)
20
- interface Position {
21
- x:number
22
- y:number
23
- }
24
-
25
- // 第2層 - 圖例列表
26
- interface LegendList {
27
- direction: 'row' | 'column'
28
- width: number
29
- height: number
30
- translateX:number
31
- translateY:number
32
- list: LegendItem[][]
33
- }
34
-
35
- // 第3層 - 圖例項目
36
- interface LegendItem {
37
- id: string // seriesLabel
38
- seriesLabel: string
39
- seriesIndex: number
40
- lineIndex: number
41
- itemIndex: number // 行內的item
42
- text: string
43
- itemWidth: number
44
- translateX: number
45
- translateY: number
46
- color: string
47
- // fontSize: number
48
- // rectRadius: number
49
- }
12
+ import { createBaseLegend } from '../../base/BaseLegend'
50
13
 
51
14
  const pluginName = 'SeriesLegend'
52
- const boxClassName = getClassName(pluginName, 'box')
53
- const legendListClassName = getClassName(pluginName, 'legend-list')
54
- const itemClassName = getClassName(pluginName, 'item')
55
-
56
- function renderSeriesLegend ({ itemSelection, lengendList, seriesLabel, fullParams, fullChartParams }: {
57
- itemSelection: d3.Selection<SVGGElement, LegendItem, any, any>
58
- lengendList: LegendList
59
- seriesLabel: string[]
60
- fullParams: SeriesLegendParams
61
- fullChartParams: ChartParams
62
- }) {
63
- itemSelection
64
- .each((d, i, g) => {
65
- // 方塊
66
- d3.select(g[i])
67
- .selectAll('rect')
68
- .data([d])
69
- .join('rect')
70
- .attr('width', fullChartParams.styles.textSize)
71
- .attr('height', fullChartParams.styles.textSize)
72
- .attr('fill', _d => _d.color)
73
- .attr('rx', fullParams.rectRadius)
74
- // 文字
75
- d3.select(g[i])
76
- .selectAll('text')
77
- .data([d])
78
- .join(
79
- enter => {
80
- return enter
81
- .append('text')
82
- .attr('dominant-baseline', 'hanging')
83
- },
84
- update => {
85
- return update
86
- },
87
- exit => exit.remove()
88
- )
89
- .attr('x', fullChartParams.styles.textSize * 1.5)
90
- .attr('font-size', fullChartParams.styles.textSize)
91
- .text(d => d.text)
92
- })
93
- }
94
-
95
15
 
96
16
  export const SeriesLegend = defineSeriesPlugin(pluginName, DEFAULT_SERIES_LEGEND_PARAMS)(({ selection, rootSelection, observer, subject }) => {
97
17
 
@@ -104,354 +24,17 @@ export const SeriesLegend = defineSeriesPlugin(pluginName, DEFAULT_SERIES_LEGEND
104
24
  })
105
25
  )
106
26
 
107
- const lineDirection$ = observer.fullParams$.pipe(
108
- takeUntil(destroy$),
109
- map(data => {
110
- return data.position === 'bottom' || data.position === 'top'
111
- ? 'row'
112
- : 'column'
113
- })
114
- )
115
-
116
- const lineMaxSize$ = combineLatest({
117
- fullParams: observer.fullParams$,
118
- layout: observer.layout$
119
- }).pipe(
120
- takeUntil(destroy$),
121
- map(data => {
122
- return data.fullParams.position === 'bottom' || data.fullParams.position === 'top'
123
- ? data.layout.rootWidth - 2 // 減2是避免完全貼到邊線上
124
- : data.layout.rootHeight - 2
125
- })
126
- )
127
-
128
- const boxPosition$ = combineLatest({
129
- layout: observer.layout$,
130
- fullParams: observer.fullParams$,
131
- }).pipe(
132
- takeUntil(destroy$),
133
- switchMap(async d => d),
134
- map(data => {
135
- let x = 0
136
- let y = 0
137
- if (data.fullParams.position === 'bottom') {
138
- y = data.layout.rootHeight
139
- if (data.fullParams.justify === 'start') {
140
- x = 0
141
- } else if (data.fullParams.justify === 'center') {
142
- x = data.layout.rootWidth / 2
143
- } else if (data.fullParams.justify === 'end') {
144
- x = data.layout.rootWidth
145
- }
146
- } else if (data.fullParams.position === 'right') {
147
- x = data.layout.rootWidth
148
- if (data.fullParams.justify === 'start') {
149
- y = 0
150
- } else if (data.fullParams.justify === 'center') {
151
- y = data.layout.rootHeight / 2
152
- } else if (data.fullParams.justify === 'end') {
153
- y = data.layout.rootHeight
154
- }
155
- } else if (data.fullParams.position === 'top') {
156
- y = 0
157
- if (data.fullParams.justify === 'start') {
158
- x = 0
159
- } else if (data.fullParams.justify === 'center') {
160
- x = data.layout.rootWidth / 2
161
- } else if (data.fullParams.justify === 'end') {
162
- x = data.layout.rootWidth
163
- }
164
- } else if (data.fullParams.position === 'left') {
165
- x = 0
166
- if (data.fullParams.justify === 'start') {
167
- y = 0
168
- } else if (data.fullParams.justify === 'center') {
169
- y = data.layout.rootHeight / 2
170
- } else if (data.fullParams.justify === 'end') {
171
- y = data.layout.rootHeight
172
- }
173
- }
174
-
175
- return {
176
- x,
177
- y
178
- }
179
- })
180
- )
181
-
182
- const boxSelection$: Observable<d3.Selection<SVGGElement, Position, any, any>> = boxPosition$.pipe(
183
- takeUntil(destroy$),
184
- map(data => {
185
-
186
- return rootSelection
187
- .selectAll<SVGGElement, Position>(`g.${boxClassName}`)
188
- .data([data])
189
- .join(
190
- enter => {
191
- return enter
192
- .append('g')
193
- .classed(boxClassName, true)
194
- .attr('transform', d => `translate(${d.x}, ${d.y})`)
195
- },
196
- update => {
197
- return update
198
- .transition()
199
- .attr('transform', d => `translate(${d.x}, ${d.y})`)
200
- },
201
- exit => exit.remove()
202
- )
203
- })
204
- )
205
-
206
- const lengendList$: Observable<LegendList> = combineLatest({
207
- layout: observer.layout$,
208
- fullParams: observer.fullParams$,
209
- fullChartParams: observer.fullChartParams$,
210
- seriesLabels: seriesLabels$,
211
- lineDirection: lineDirection$,
212
- lineMaxSize: lineMaxSize$
213
- }).pipe(
214
- takeUntil(destroy$),
215
- switchMap(async d => d),
216
- map(data => {
217
- const list: LegendItem[][] = data.seriesLabels.reduce((prev: LegendItem[][], current, currentIndex) => {
218
- const textWidth = measureTextWidth(current, data.fullChartParams.styles.textSize)
219
- const itemWidth = (data.fullChartParams.styles.textSize * 1.5) + textWidth
220
- const color = getSeriesColor(currentIndex, data.fullChartParams)
221
- const lastItem: LegendItem | null = prev[0] && prev[0][0]
222
- ? prev[prev.length - 1][prev[prev.length - 1].length - 1]
223
- : null
224
-
225
- const { translateX, translateY, lineIndex, itemIndex } = ((_data, _prev, _lastItem) => {
226
- let translateX = 0
227
- let translateY = 0
228
- let lineIndex = 0
229
- let itemIndex = 0
230
-
231
- if (_data.lineDirection === 'column') {
232
- let tempTranslateY = _lastItem
233
- ? _lastItem.translateY + _data.fullChartParams.styles.textSize + _data.fullParams.gap
234
- : 0
235
-
236
- if ((tempTranslateY + _data.fullChartParams.styles.textSize) > _data.lineMaxSize) {
237
- // 換行
238
- lineIndex = _lastItem.lineIndex + 1
239
- itemIndex = 0
240
- translateY = 0
241
- // 前一行最寬寬度
242
- const maxItemWidthInLastLine = _prev[_prev.length - 1].reduce((p, c) => {
243
- return c.itemWidth > p ? c.itemWidth : p
244
- }, 0)
245
- translateX = _lastItem.translateX + maxItemWidthInLastLine + _data.fullParams.gap
246
- } else {
247
- lineIndex = _lastItem ? _lastItem.lineIndex : 0
248
- itemIndex = _lastItem ? _lastItem.itemIndex + 1 : 0
249
- translateY = tempTranslateY
250
- translateX = _lastItem ? _lastItem.translateX : 0
251
- }
252
- } else {
253
- let tempTranslateX = _lastItem
254
- ? _lastItem.translateX + _lastItem.itemWidth + _data.fullParams.gap
255
- : 0
256
- if ((tempTranslateX + itemWidth) > _data.lineMaxSize) {
257
- // 換行
258
- lineIndex = _lastItem.lineIndex + 1
259
- itemIndex = 0
260
- translateX = 0
261
- } else {
262
- lineIndex = _lastItem ? _lastItem.lineIndex : 0
263
- itemIndex = _lastItem ? _lastItem.itemIndex + 1 : 0
264
- translateX = tempTranslateX
265
- }
266
- translateY = (_data.fullChartParams.styles.textSize + _data.fullParams.gap) * lineIndex
267
- }
268
-
269
- return { translateX, translateY, lineIndex, itemIndex }
270
- })(data, prev, lastItem)
271
-
272
- if (!prev[lineIndex]) {
273
- prev[lineIndex] = []
274
- }
275
-
276
- prev[lineIndex].push({
277
- id: current,
278
- seriesLabel: current,
279
- seriesIndex: currentIndex,
280
- lineIndex,
281
- itemIndex,
282
- text: current,
283
- itemWidth,
284
- translateX,
285
- translateY,
286
- color,
287
- })
288
-
289
- return prev
290
- }, [])
291
-
292
- // 依list計算出來的排序位置來計算整體的偏移位置
293
- const { width, height, translateX, translateY } = ((_data, _list) => {
294
- let width = 0
295
- let height = 0
296
- let translateX = 0
297
- let translateY = 0
298
-
299
- if (!_list.length || !_list[0].length) {
300
- return { width, height, translateX, translateY }
301
- }
302
-
303
- const firstLineLastItem = _list[0][_list[0].length - 1]
304
- if (_data.lineDirection === 'column') {
305
- width = _list.reduce((p, c) => {
306
- const maxWidthInLine = c.reduce((_p, _c) => {
307
- // 找出最寬的寬度
308
- return _c.itemWidth > _p ? _c.itemWidth : _p
309
- }, 0)
310
- // 每行寬度加總
311
- return p + maxWidthInLine
312
- }, 0)
313
- height = firstLineLastItem.translateY + _data.fullChartParams.styles.textSize + _data.fullParams.gap
314
- } else {
315
- width = firstLineLastItem.translateX + firstLineLastItem.itemWidth
316
- height = (_data.fullChartParams.styles.textSize * _list.length) + (_data.fullParams.gap * (_list.length - 1))
317
- }
318
-
319
- if (_data.fullParams.position === 'left') {
320
- if (_data.fullParams.justify === 'start') {
321
- translateX = _data.fullParams.padding
322
- translateY = _data.fullParams.padding
323
- } else if (_data.fullParams.justify === 'center') {
324
- translateX = _data.fullParams.padding
325
- translateY = - height / 2
326
- } else if (_data.fullParams.justify === 'end') {
327
- translateX = _data.fullParams.padding
328
- translateY = - height - _data.fullParams.padding
329
- }
330
- } else if (_data.fullParams.position === 'right') {
331
- if (_data.fullParams.justify === 'start') {
332
- translateX = - width - _data.fullParams.padding
333
- translateY = _data.fullParams.padding
334
- } else if (_data.fullParams.justify === 'center') {
335
- translateX = - width - _data.fullParams.padding
336
- translateY = - height / 2
337
- } else if (_data.fullParams.justify === 'end') {
338
- translateX = - width - _data.fullParams.padding
339
- translateY = - height - _data.fullParams.padding
340
- }
341
- } else if (_data.fullParams.position === 'top') {
342
- if (_data.fullParams.justify === 'start') {
343
- translateX = _data.fullParams.padding
344
- translateY = _data.fullParams.padding
345
- } else if (_data.fullParams.justify === 'center') {
346
- translateX = - width / 2
347
- translateY = _data.fullParams.padding
348
- } else if (_data.fullParams.justify === 'end') {
349
- translateX = - width - _data.fullParams.padding
350
- translateY = _data.fullParams.padding
351
- }
352
- } else {
353
- if (_data.fullParams.justify === 'start') {
354
- translateX = _data.fullParams.padding
355
- translateY = - height - _data.fullParams.padding
356
- } else if (_data.fullParams.justify === 'center') {
357
- translateX = - width / 2
358
- translateY = - height - _data.fullParams.padding
359
- } else if (_data.fullParams.justify === 'end') {
360
- translateX = - width - _data.fullParams.padding
361
- translateY = - height - _data.fullParams.padding
362
- }
363
- }
364
-
365
- // translateX += _data.fullParams.offset[0]
366
- // translateY += _data.fullParams.offset[1]
367
-
368
- return { width, height, translateX, translateY }
369
- })(data, list)
370
-
371
- return {
372
- direction: data.lineDirection,
373
- width,
374
- height,
375
- translateX,
376
- translateY,
377
- list
378
- }
379
- })
380
- )
381
-
382
- const lengendListSelection$ = combineLatest({
383
- boxSelection: boxSelection$,
384
- fullParams: observer.fullParams$,
385
- lengendList: lengendList$
386
- }).pipe(
387
- takeUntil(destroy$),
388
- switchMap(async d => d),
389
- map(data => {
390
- return data.boxSelection
391
- .selectAll<SVGGElement, SeriesLegendParams>('g')
392
- .data([data.lengendList])
393
- .join(
394
- enter => {
395
- return enter
396
- .append('g')
397
- .classed(legendListClassName, true)
398
- .attr('transform', d => `translate(${d.translateX}, ${d.translateY})`)
399
- },
400
- update => {
401
- return update
402
- .transition()
403
- .attr('transform', d => `translate(${d.translateX}, ${d.translateY})`)
404
- },
405
- exit => exit.remove()
406
- )
407
- })
408
- )
409
-
410
- const itemSelection$ = lengendListSelection$.pipe(
411
- takeUntil(destroy$),
412
- map(lengendListSelection => {
413
- const legendListData = lengendListSelection.data()
414
- const data = legendListData[0] ? legendListData[0].list.flat() : []
415
-
416
- return lengendListSelection
417
- .selectAll<SVGGElement, string>(`g.${itemClassName}`)
418
- .data(data)
419
- .join(
420
- enter => {
421
- return enter
422
- .append('g')
423
- .classed(itemClassName, true)
424
- .attr('cursor', 'default')
425
- },
426
- update => update,
427
- exit => exit.remove()
428
- )
429
- .attr('transform', (d, i) => {
430
- return `translate(${d.translateX}, ${d.translateY})`
431
- })
432
- })
433
- )
434
-
435
- combineLatest({
436
- itemSelection: itemSelection$,
437
- lengendList: lengendList$,
438
- seriesLabels: seriesLabels$,
439
- fullParams: observer.fullParams$,
440
- fullChartParams: observer.fullChartParams$
441
- }).pipe(
442
- takeUntil(destroy$),
443
- switchMap(async d => d),
444
- ).subscribe(data => {
445
- renderSeriesLegend({
446
- itemSelection: data.itemSelection,
447
- lengendList: data.lengendList,
448
- seriesLabel: data.seriesLabels,
449
- fullParams: data.fullParams,
450
- fullChartParams: data.fullChartParams
451
- })
27
+ const unsubscribeBaseLegend = createBaseLegend(pluginName, {
28
+ rootSelection,
29
+ seriesLabels$,
30
+ fullParams$: observer.fullParams$,
31
+ layout$: observer.layout$,
32
+ fullChartParams$: observer.fullChartParams$
452
33
  })
453
34
 
454
35
  return () => {
455
36
  destroy$.next(undefined)
37
+ unsubscribeBaseLegend()
456
38
  }
457
39
  })
40
+
@@ -1,4 +1,5 @@
1
1
  import type { ComputedDatumSeries, EventSeries, EventName, ColorType } from '@orbcharts/core'
2
+ import type { BaseLegendParams } from '../base/BaseLegend'
2
3
 
3
4
  export type ScaleType = 'area' | 'radius'
4
5
 
@@ -52,12 +53,4 @@ export interface PieLabelsPluginParams {
52
53
  labelColorType: ColorType
53
54
  }
54
55
 
55
- export interface SeriesLegendParams {
56
- position: 'top' | 'bottom' | 'left' | 'right'
57
- justify: 'start' | 'center' | 'end'
58
- padding: number
59
- // offset: [number, number]
60
- gap: number
61
- rectRadius: number
62
- // highlightEvent: boolean
63
- }
56
+ export interface SeriesLegendParams extends BaseLegendParams {}
@@ -17,9 +17,14 @@ export function getColor (colorType: ColorType, fullChartParams: ChartParams) {
17
17
  return colors.series[0]
18
18
  }
19
19
  // 對應colorType設定的顏色
20
- return colors[colorType] != null
21
- ? colors[colorType]
22
- : colors.primary
20
+ // return colors[colorType] != null
21
+ // ? colors[colorType]
22
+ // : colors.primary
23
+ return colorType == 'none'
24
+ ? 'none'
25
+ : colors[colorType] != undefined
26
+ ? colors[colorType]
27
+ : colors.primary // 如果比對不到
23
28
  }
24
29
 
25
30
  // 取得Series顏色
@@ -42,9 +47,11 @@ export function getDatumColor ({ datum, colorType, fullChartParams }: { datum: C
42
47
  }
43
48
  }
44
49
  // 對應colorType設定的顏色
45
- return fullChartParams.colors[fullChartParams.colorScheme][colorType] != null
46
- ? fullChartParams.colors[fullChartParams.colorScheme][colorType]
47
- : fullChartParams.colors[fullChartParams.colorScheme].primary
50
+ return colorType == 'none'
51
+ ? 'none'
52
+ : fullChartParams.colors[fullChartParams.colorScheme][colorType] != undefined
53
+ ? fullChartParams.colors[fullChartParams.colorScheme][colorType]
54
+ : fullChartParams.colors[fullChartParams.colorScheme].primary
48
55
  }
49
56
 
50
57
  export function getClassName (pluginName: string, elementName: string, modifier?: string) {