@orbcharts/core 3.0.3 → 3.0.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 (70) hide show
  1. package/LICENSE +200 -200
  2. package/dist/orbcharts-core.es.js +1896 -1847
  3. package/dist/orbcharts-core.umd.js +5 -5
  4. package/dist/src/series/seriesObservables.d.ts +18 -7
  5. package/dist/src/utils/errorMessage.d.ts +1 -4
  6. package/lib/core-types.ts +7 -7
  7. package/package.json +46 -46
  8. package/src/AbstractChart.ts +57 -57
  9. package/src/GridChart.ts +24 -24
  10. package/src/MultiGridChart.ts +24 -24
  11. package/src/MultiValueChart.ts +24 -24
  12. package/src/RelationshipChart.ts +24 -24
  13. package/src/SeriesChart.ts +24 -24
  14. package/src/TreeChart.ts +24 -24
  15. package/src/base/createBaseChart.ts +524 -526
  16. package/src/base/createBasePlugin.ts +154 -154
  17. package/src/base/validators/chartOptionsValidator.ts +23 -23
  18. package/src/base/validators/chartParamsValidator.ts +133 -133
  19. package/src/base/validators/elementValidator.ts +13 -13
  20. package/src/base/validators/pluginsValidator.ts +14 -14
  21. package/src/defaults.ts +284 -283
  22. package/src/defineGridPlugin.ts +3 -3
  23. package/src/defineMultiGridPlugin.ts +3 -3
  24. package/src/defineMultiValuePlugin.ts +3 -3
  25. package/src/defineNoneDataPlugin.ts +4 -4
  26. package/src/defineRelationshipPlugin.ts +3 -3
  27. package/src/defineSeriesPlugin.ts +3 -3
  28. package/src/defineTreePlugin.ts +3 -3
  29. package/src/grid/computedDataFn.ts +129 -129
  30. package/src/grid/contextObserverCallback.ts +201 -201
  31. package/src/grid/dataFormatterValidator.ts +125 -125
  32. package/src/grid/dataValidator.ts +12 -12
  33. package/src/grid/gridObservables.ts +694 -718
  34. package/src/index.ts +20 -20
  35. package/src/multiGrid/computedDataFn.ts +123 -123
  36. package/src/multiGrid/contextObserverCallback.ts +75 -75
  37. package/src/multiGrid/dataFormatterValidator.ts +120 -120
  38. package/src/multiGrid/dataValidator.ts +12 -12
  39. package/src/multiGrid/multiGridObservables.ts +357 -401
  40. package/src/multiValue/computedDataFn.ts +113 -113
  41. package/src/multiValue/contextObserverCallback.ts +328 -328
  42. package/src/multiValue/dataFormatterValidator.ts +94 -94
  43. package/src/multiValue/dataValidator.ts +12 -12
  44. package/src/multiValue/multiValueObservables.ts +865 -1219
  45. package/src/relationship/computedDataFn.ts +159 -159
  46. package/src/relationship/contextObserverCallback.ts +80 -80
  47. package/src/relationship/dataFormatterValidator.ts +13 -13
  48. package/src/relationship/dataValidator.ts +13 -13
  49. package/src/relationship/relationshipObservables.ts +84 -84
  50. package/src/series/computedDataFn.ts +88 -88
  51. package/src/series/contextObserverCallback.ts +132 -107
  52. package/src/series/dataFormatterValidator.ts +46 -46
  53. package/src/series/dataValidator.ts +12 -12
  54. package/src/series/seriesObservables.ts +209 -175
  55. package/src/tree/computedDataFn.ts +129 -129
  56. package/src/tree/contextObserverCallback.ts +58 -58
  57. package/src/tree/dataFormatterValidator.ts +13 -13
  58. package/src/tree/dataValidator.ts +13 -13
  59. package/src/tree/treeObservables.ts +105 -105
  60. package/src/utils/commonUtils.ts +55 -55
  61. package/src/utils/d3Scale.ts +198 -198
  62. package/src/utils/errorMessage.ts +40 -43
  63. package/src/utils/index.ts +3 -3
  64. package/src/utils/observables.ts +308 -293
  65. package/src/utils/orbchartsUtils.ts +396 -396
  66. package/src/utils/validator.ts +126 -126
  67. package/tsconfig.base.json +13 -13
  68. package/tsconfig.json +2 -2
  69. package/vite-env.d.ts +6 -6
  70. package/vite.config.js +22 -22
@@ -1,294 +1,309 @@
1
- import {
2
- combineLatest,
3
- distinctUntilChanged,
4
- filter,
5
- map,
6
- merge,
7
- takeUntil,
8
- shareReplay,
9
- switchMap,
10
- Subject,
11
- Observable } from 'rxjs'
12
- import type {
13
- ChartType,
14
- ChartParams,
15
- ComputedDatumBase,
16
- ComputedDataTypeMap,
17
- ComputedDatumTypeMap,
18
- ContainerPositionScaled,
19
- DataFormatterContainer,
20
- DataFormatterTypeMap,
21
- EventTypeMap,
22
- HighlightTarget,
23
- Layout,
24
- TransformData } from '../../lib/core-types'
25
-
26
- // interface DatumUnknown {
27
- // value: number | null
28
- // id: string
29
- // // label: string
30
- // seriesLabel?: string // 要符合每一種computedData所以不一定會有seriesLabel
31
- // groupLabel?: string // 要符合每一種computedData所以不一定會有groupLabel
32
- // }
33
-
34
- export function resizeObservable(elem: HTMLElement | Element): Observable<DOMRectReadOnly> {
35
- return new Observable(subscriber => {
36
- const ro = new ResizeObserver(entries => {
37
- const entry = entries[0]
38
- if (entry && entry.contentRect) {
39
- subscriber.next(entry.contentRect)
40
- }
41
- })
42
-
43
- ro.observe(elem)
44
- return function unsubscribe() {
45
- ro.unobserve(elem)
46
- }
47
- })
48
- }
49
-
50
- interface HighlightTargetValue {
51
- id: string | null
52
- label: string | null
53
- seriesLabel: string | null
54
- groupLabel: string | null
55
- categoryLabel: string | null
56
- highlightDefault: string | null
57
- }
58
-
59
- // 通用 highlight Observable
60
- export const highlightObservable = <T extends ChartType, D>({ datumList$, fullChartParams$, event$ }: {
61
- datumList$: Observable<D[]>
62
- fullChartParams$: Observable<ChartParams>
63
- event$: Subject<EventTypeMap<T>>
64
- }): Observable<D[]> => {
65
- const destroy$ = new Subject()
66
-
67
- // 預設的highlight
68
- const highlightDefault$: Observable<HighlightTargetValue> = fullChartParams$.pipe(
69
- takeUntil(destroy$),
70
- map(d => {
71
- return {
72
- id: null,
73
- label: null,
74
- seriesLabel: null,
75
- groupLabel: null,
76
- categoryLabel: null,
77
- highlightDefault: d.highlightDefault
78
- } as HighlightTargetValue
79
- }),
80
- distinctUntilChanged()
81
- )
82
-
83
- // 事件觸發的highlight
84
- const highlightMouseover$: Observable<HighlightTargetValue> = event$.pipe(
85
- takeUntil(destroy$),
86
- // filter(d => d.eventName === 'mouseover' || d.eventName === 'mousemove'),
87
- filter(d => d.eventName === 'mouseover'),
88
- // distinctUntilChanged((prev, current) => prev.eventName === current.eventName)
89
- map(_d => {
90
- const d = _d as any
91
- return d.datum
92
- ? {
93
- id: d.datum.id,
94
- label: d.datum.label,
95
- seriesLabel: d.datum.seriesLabel,
96
- groupLabel: d.datum.groupLabel,
97
- categoryLabel: d.datum.categoryLabel,
98
- highlightDefault: null
99
- } as HighlightTargetValue
100
- : {
101
- id: null,
102
- label: null,
103
- seriesLabel: null,
104
- groupLabel: null,
105
- categoryLabel: null,
106
- highlightDefault: null
107
- } as HighlightTargetValue
108
- })
109
- )
110
- const highlightMouseout$ = event$.pipe(
111
- takeUntil(destroy$),
112
- filter(d => d.eventName === 'mouseout'),
113
- // distinctUntilChanged((prev, current) => prev.eventName === current.eventName)
114
- // map(d => {
115
- // return { id: '', label: '' }
116
- // })
117
- switchMap(d => highlightDefault$)
118
- )
119
-
120
- // function getDatumIds (datumList: ComputedDatumTypeMap<T>[], id: string | null) {
121
- // const datum = datumList.find(d => (d as ComputedDatumBase).id === id)
122
- // return datum ? [datum] : []
123
- // }
124
- function getDatumIds (datumList: ComputedDatumTypeMap<T>[], id: string | null, label: string | null) {
125
- return id == null && label == null
126
- ? []
127
- : datumList.filter(d => (d as ComputedDatumBase).id === id || (d as ComputedDatumBase).label === label)
128
- }
129
-
130
- function getSeriesIds (datumList: ComputedDatumTypeMap<T>[], seriesLabel: string | null) {
131
- return seriesLabel == null
132
- ? []
133
- : datumList.filter(d => (d as ComputedDatumTypeMap<"series">).seriesLabel === seriesLabel)
134
- }
135
-
136
- function getGroupIds (datumList: ComputedDatumTypeMap<T>[], groupLabel: string | null) {
137
- return groupLabel == null
138
- ? []
139
- : datumList.filter(d => (d as ComputedDatumTypeMap<"grid">).groupLabel === groupLabel)
140
- }
141
-
142
- function getCategoryIds (datumList: ComputedDatumTypeMap<T>[], categoryLabel: string | null) {
143
- return categoryLabel == null
144
- ? []
145
- : datumList.filter(d => (d as ComputedDatumTypeMap<"multiValue" | "relationship" | "tree">).categoryLabel === categoryLabel)
146
- }
147
-
148
- return new Observable<D[]>(subscriber => {
149
- combineLatest({
150
- target: merge(highlightMouseover$, highlightMouseout$, highlightDefault$),
151
- datumList: datumList$,
152
- fullChartParams: fullChartParams$,
153
- }).pipe(
154
- takeUntil(destroy$),
155
- switchMap(async d => d)
156
- ).subscribe(data => {
157
- let datumList: ComputedDatumTypeMap<T>[] = []
158
- if (data.fullChartParams.highlightTarget === 'datum') {
159
- datumList = getDatumIds(data.datumList as ComputedDatumTypeMap<T>[], data.target.id, data.target.label)
160
- } else if (data.fullChartParams.highlightTarget === 'series') {
161
- datumList = getSeriesIds(data.datumList as ComputedDatumTypeMap<T>[], data.target.seriesLabel)
162
- } else if (data.fullChartParams.highlightTarget === 'group') {
163
- datumList = getGroupIds(data.datumList as ComputedDatumTypeMap<T>[], data.target.groupLabel)
164
- } else if (data.fullChartParams.highlightTarget === 'category') {
165
- datumList = getCategoryIds(data.datumList as ComputedDatumTypeMap<T>[], data.target.categoryLabel)
166
- }
167
- subscriber.next(datumList as D[])
168
- })
169
-
170
- return function unsubscribe () {
171
- destroy$.next(undefined)
172
- }
173
- })
174
- }
175
-
176
- export const seriesDataMapObservable = <DatumType extends ComputedDatumTypeMap<'series' | 'grid'>>({ datumList$ }: { datumList$: Observable<DatumType[]> }) => {
177
- return datumList$.pipe(
178
- map(data => {
179
- const SeriesDataMap: Map<string, DatumType[]> = new Map()
180
- data.forEach(d => {
181
- const seriesData = SeriesDataMap.get(d.seriesLabel) ?? []
182
- seriesData.push(d)
183
- SeriesDataMap.set(d.seriesLabel, seriesData)
184
- })
185
- return SeriesDataMap
186
- })
187
- )
188
- }
189
-
190
- export const groupDataMapObservable = <DatumType extends ComputedDatumTypeMap<'grid'>> ({ datumList$ }: { datumList$: Observable<DatumType[]> }) => {
191
- return datumList$.pipe(
192
- map(data => {
193
- const GroupDataMap: Map<string, DatumType[]> = new Map()
194
- data.forEach(d => {
195
- const groupData = GroupDataMap.get(d.groupLabel) ?? []
196
- groupData.push(d)
197
- GroupDataMap.set(d.groupLabel, groupData)
198
- })
199
- return GroupDataMap
200
- })
201
- )
202
- }
203
-
204
- export const categoryDataMapObservable = <DatumType extends ComputedDatumTypeMap<'multiValue' | 'relationship' | 'tree'>> ({ datumList$ }: { datumList$: Observable<DatumType[]> }) => {
205
- return datumList$.pipe(
206
- map(data => {
207
- const GroupDataMap: Map<string, DatumType[]> = new Map()
208
- data
209
- .filter(d => d.categoryLabel != null)
210
- .forEach(d => {
211
- const groupData = GroupDataMap.get(d.categoryLabel) ?? []
212
- groupData.push(d)
213
- GroupDataMap.set(d.categoryLabel, groupData)
214
- })
215
- return GroupDataMap
216
- })
217
- )
218
- }
219
-
220
- export const textSizePxObservable = (chartParams$: Observable<ChartParams>) => {
221
- return chartParams$.pipe(
222
- map(d => d.styles.textSize),
223
- distinctUntilChanged(),
224
- map(data => {
225
- let value = NaN
226
- if (typeof data === 'string') {
227
- if (data.includes('rem')) {
228
- const rootFontSizePx = parseFloat(getComputedStyle(document.documentElement).fontSize)
229
- const num = parseFloat(data)
230
- value = num * rootFontSizePx
231
- } else if (data.includes('px')) {
232
- value = parseFloat(data)
233
- }
234
- } else if (typeof data === 'number') {
235
- return data
236
- }
237
- return value ? value : 14 // default
238
- })
239
- )
240
- }
241
-
242
- export const containerSizeObservable = ({ layout$, containerPosition$, container$ }: {
243
- layout$: Observable<Layout>
244
- containerPosition$: Observable<ContainerPositionScaled[]>
245
- container$: Observable<DataFormatterContainer>
246
- }) => {
247
- const rowAmount$ = containerPosition$.pipe(
248
- map(containerPosition => {
249
- const maxRowIndex = containerPosition.reduce((acc, current) => {
250
- return current.rowIndex > acc ? current.rowIndex : acc
251
- }, 0)
252
- return maxRowIndex + 1
253
- }),
254
- distinctUntilChanged(),
255
- )
256
-
257
- const columnAmount$ = containerPosition$.pipe(
258
- map(containerPosition => {
259
- const maxColumnIndex = containerPosition.reduce((acc, current) => {
260
- return current.columnIndex > acc ? current.columnIndex : acc
261
- }, 0)
262
- return maxColumnIndex + 1
263
- }),
264
- distinctUntilChanged()
265
- )
266
-
267
- return combineLatest({
268
- layout: layout$,
269
- rowAmount: rowAmount$,
270
- columnAmount: columnAmount$,
271
- container: container$
272
- }).pipe(
273
- switchMap(async (d) => d),
274
- map(data => {
275
- // const width = (data.layout.rootWidth / data.columnAmount) - (data.layout.left + data.layout.right)
276
- // const height = (data.layout.rootHeight / data.rowAmount) - (data.layout.top + data.layout.bottom)
277
- const columnGap = data.container.columnGap === 'auto'
278
- ? data.layout.left + data.layout.right
279
- : data.container.columnGap
280
- const rowGap = data.container.rowGap === 'auto'
281
- ? data.layout.top + data.layout.bottom
282
- : data.container.rowGap
283
- const width = (data.layout.rootWidth - data.layout.left - data.layout.right - (columnGap * (data.columnAmount - 1))) / data.columnAmount
284
- const height = (data.layout.rootHeight - data.layout.top - data.layout.bottom - (rowGap * (data.rowAmount - 1))) / data.rowAmount
285
-
286
- return {
287
- width,
288
- height
289
- }
290
- }),
291
- distinctUntilChanged((a, b) => a.width === b.width && a.height === b.height),
292
- // shareReplay(1)
293
- )
1
+ import {
2
+ combineLatest,
3
+ distinctUntilChanged,
4
+ filter,
5
+ map,
6
+ merge,
7
+ takeUntil,
8
+ shareReplay,
9
+ switchMap,
10
+ Subject,
11
+ Observable } from 'rxjs'
12
+ import type {
13
+ ChartType,
14
+ ChartParams,
15
+ ComputedDatumBase,
16
+ ComputedDataTypeMap,
17
+ ComputedDatumTypeMap,
18
+ ContainerPositionScaled,
19
+ DataFormatterContainer,
20
+ DataFormatterTypeMap,
21
+ EventTypeMap,
22
+ HighlightTarget,
23
+ Layout,
24
+ TransformData } from '../../lib/core-types'
25
+
26
+ // interface DatumUnknown {
27
+ // value: number | null
28
+ // id: string
29
+ // // label: string
30
+ // seriesLabel?: string // 要符合每一種computedData所以不一定會有seriesLabel
31
+ // groupLabel?: string // 要符合每一種computedData所以不一定會有groupLabel
32
+ // }
33
+
34
+ export function resizeObservable(elem: HTMLElement | Element): Observable<DOMRectReadOnly> {
35
+ return new Observable(subscriber => {
36
+ const ro = new ResizeObserver(entries => {
37
+ const entry = entries[0]
38
+ if (entry && entry.contentRect) {
39
+ subscriber.next(entry.contentRect)
40
+ }
41
+ })
42
+
43
+ ro.observe(elem)
44
+ return function unsubscribe() {
45
+ ro.unobserve(elem)
46
+ }
47
+ })
48
+ }
49
+
50
+ interface HighlightTargetValue {
51
+ id: string | null
52
+ label: string | null
53
+ seriesLabel: string | null
54
+ groupLabel: string | null
55
+ categoryLabel: string | null
56
+ highlightDefault: string | null
57
+ }
58
+
59
+ // 通用 highlight Observable
60
+ export const highlightObservable = <T extends ChartType, D>({ datumList$, fullChartParams$, event$ }: {
61
+ datumList$: Observable<D[]>
62
+ fullChartParams$: Observable<ChartParams>
63
+ event$: Subject<EventTypeMap<T>>
64
+ }): Observable<D[]> => {
65
+ const destroy$ = new Subject()
66
+
67
+ // 預設的highlight
68
+ const highlightDefault$: Observable<HighlightTargetValue> = fullChartParams$.pipe(
69
+ takeUntil(destroy$),
70
+ map(d => d.highlightDefault),
71
+ distinctUntilChanged(),
72
+ map(highlightDefault => {
73
+ return {
74
+ id: null,
75
+ label: null,
76
+ seriesLabel: null,
77
+ groupLabel: null,
78
+ categoryLabel: null,
79
+ highlightDefault
80
+ } as HighlightTargetValue
81
+ }),
82
+ shareReplay(1)
83
+ )
84
+
85
+ const highlightTarget$: Observable<HighlightTarget> = fullChartParams$.pipe(
86
+ takeUntil(destroy$),
87
+ map(d => d.highlightTarget),
88
+ distinctUntilChanged(),
89
+ shareReplay(1)
90
+ )
91
+
92
+ // 事件觸發的highlight
93
+ const highlightMouseover$: Observable<HighlightTargetValue> = highlightTarget$.pipe(
94
+ switchMap(highlightTarget => {
95
+ return event$.pipe(
96
+ takeUntil(destroy$),
97
+ // filter(d => d.eventName === 'mouseover' || d.eventName === 'mousemove'),
98
+ filter(d => d.eventName === 'mouseover'),
99
+ // distinctUntilChanged((prev, current) => prev.eventName === current.eventName)
100
+ map(_d => {
101
+ const d = _d as any
102
+ return d.datum
103
+ ? {
104
+ id: d.datum.id,
105
+ label: null, // label有可能重覆所以不做判斷
106
+ seriesLabel: highlightTarget === 'series' ? d.datum.seriesLabel : null,
107
+ groupLabel: highlightTarget === 'group' ? d.datum.groupLabel : null,
108
+ categoryLabel: highlightTarget === 'category' ? d.datum.categoryLabel : null,
109
+ highlightDefault: null
110
+ } as HighlightTargetValue
111
+ : {
112
+ id: null,
113
+ label: null,
114
+ seriesLabel: null,
115
+ groupLabel: null,
116
+ categoryLabel: null,
117
+ highlightDefault: null
118
+ } as HighlightTargetValue
119
+ })
120
+ )
121
+ })
122
+ )
123
+
124
+ const highlightMouseout$ = event$.pipe(
125
+ takeUntil(destroy$),
126
+ filter(d => d.eventName === 'mouseout'),
127
+ // distinctUntilChanged((prev, current) => prev.eventName === current.eventName)
128
+ // map(d => {
129
+ // return { id: '', label: '' }
130
+ // })
131
+ switchMap(d => highlightDefault$)
132
+ )
133
+
134
+ // function getDatumIds (datumList: ComputedDatumTypeMap<T>[], id: string | null) {
135
+ // const datum = datumList.find(d => (d as ComputedDatumBase).id === id)
136
+ // return datum ? [datum] : []
137
+ // }
138
+ function getDatumIds (datumList: ComputedDatumTypeMap<T>[], id: string | null, label: string | null) {
139
+ return id == null && label == null
140
+ ? []
141
+ : datumList.filter(d => (d as ComputedDatumBase).id === id || (d as ComputedDatumBase).label === label)
142
+ }
143
+
144
+ function getSeriesIds (datumList: ComputedDatumTypeMap<T>[], seriesLabel: string | null) {
145
+ return seriesLabel == null
146
+ ? []
147
+ : datumList.filter(d => (d as ComputedDatumTypeMap<"series">).seriesLabel === seriesLabel)
148
+ }
149
+
150
+ function getGroupIds (datumList: ComputedDatumTypeMap<T>[], groupLabel: string | null) {
151
+ return groupLabel == null
152
+ ? []
153
+ : datumList.filter(d => (d as ComputedDatumTypeMap<"grid">).groupLabel === groupLabel)
154
+ }
155
+
156
+ function getCategoryIds (datumList: ComputedDatumTypeMap<T>[], categoryLabel: string | null) {
157
+ return categoryLabel == null
158
+ ? []
159
+ : datumList.filter(d => (d as ComputedDatumTypeMap<"multiValue" | "relationship" | "tree">).categoryLabel === categoryLabel)
160
+ }
161
+
162
+ return new Observable<D[]>(subscriber => {
163
+ combineLatest({
164
+ target: merge(highlightMouseover$, highlightMouseout$, highlightDefault$),
165
+ datumList: datumList$,
166
+ fullChartParams: fullChartParams$,
167
+ }).pipe(
168
+ takeUntil(destroy$),
169
+ switchMap(async d => d)
170
+ ).subscribe(data => {
171
+ let datumList: ComputedDatumTypeMap<T>[] = []
172
+ if (data.fullChartParams.highlightTarget === 'datum') {
173
+ datumList = getDatumIds(data.datumList as ComputedDatumTypeMap<T>[], data.target.id, data.target.label)
174
+ } else if (data.fullChartParams.highlightTarget === 'series') {
175
+ datumList = getSeriesIds(data.datumList as ComputedDatumTypeMap<T>[], data.target.seriesLabel)
176
+ } else if (data.fullChartParams.highlightTarget === 'group') {
177
+ datumList = getGroupIds(data.datumList as ComputedDatumTypeMap<T>[], data.target.groupLabel)
178
+ } else if (data.fullChartParams.highlightTarget === 'category') {
179
+ datumList = getCategoryIds(data.datumList as ComputedDatumTypeMap<T>[], data.target.categoryLabel)
180
+ }
181
+
182
+ subscriber.next(datumList as D[])
183
+ })
184
+
185
+ return function unsubscribe () {
186
+ destroy$.next(undefined)
187
+ }
188
+ })
189
+ }
190
+
191
+ export const seriesDataMapObservable = <DatumType extends ComputedDatumTypeMap<'series' | 'grid'>>({ datumList$ }: { datumList$: Observable<DatumType[]> }) => {
192
+ return datumList$.pipe(
193
+ map(data => {
194
+ const SeriesDataMap: Map<string, DatumType[]> = new Map()
195
+ data.forEach(d => {
196
+ const seriesData = SeriesDataMap.get(d.seriesLabel) ?? []
197
+ seriesData.push(d)
198
+ SeriesDataMap.set(d.seriesLabel, seriesData)
199
+ })
200
+ return SeriesDataMap
201
+ })
202
+ )
203
+ }
204
+
205
+ export const groupDataMapObservable = <DatumType extends ComputedDatumTypeMap<'grid'>> ({ datumList$ }: { datumList$: Observable<DatumType[]> }) => {
206
+ return datumList$.pipe(
207
+ map(data => {
208
+ const GroupDataMap: Map<string, DatumType[]> = new Map()
209
+ data.forEach(d => {
210
+ const groupData = GroupDataMap.get(d.groupLabel) ?? []
211
+ groupData.push(d)
212
+ GroupDataMap.set(d.groupLabel, groupData)
213
+ })
214
+ return GroupDataMap
215
+ })
216
+ )
217
+ }
218
+
219
+ export const categoryDataMapObservable = <DatumType extends ComputedDatumTypeMap<'multiValue' | 'relationship' | 'tree'>> ({ datumList$ }: { datumList$: Observable<DatumType[]> }) => {
220
+ return datumList$.pipe(
221
+ map(data => {
222
+ const GroupDataMap: Map<string, DatumType[]> = new Map()
223
+ data
224
+ .filter(d => d.categoryLabel != null)
225
+ .forEach(d => {
226
+ const groupData = GroupDataMap.get(d.categoryLabel) ?? []
227
+ groupData.push(d)
228
+ GroupDataMap.set(d.categoryLabel, groupData)
229
+ })
230
+ return GroupDataMap
231
+ })
232
+ )
233
+ }
234
+
235
+ export const textSizePxObservable = (chartParams$: Observable<ChartParams>) => {
236
+ return chartParams$.pipe(
237
+ map(d => d.styles.textSize),
238
+ distinctUntilChanged(),
239
+ map(data => {
240
+ let value = NaN
241
+ if (typeof data === 'string') {
242
+ if (data.includes('rem')) {
243
+ const rootFontSizePx = parseFloat(getComputedStyle(document.documentElement).fontSize)
244
+ const num = parseFloat(data)
245
+ value = num * rootFontSizePx
246
+ } else if (data.includes('px')) {
247
+ value = parseFloat(data)
248
+ }
249
+ } else if (typeof data === 'number') {
250
+ return data
251
+ }
252
+ return value ? value : 14 // default
253
+ })
254
+ )
255
+ }
256
+
257
+ export const containerSizeObservable = ({ layout$, containerPosition$, container$ }: {
258
+ layout$: Observable<Layout>
259
+ containerPosition$: Observable<ContainerPositionScaled[]>
260
+ container$: Observable<DataFormatterContainer>
261
+ }) => {
262
+ const rowAmount$ = containerPosition$.pipe(
263
+ map(containerPosition => {
264
+ const maxRowIndex = containerPosition.reduce((acc, current) => {
265
+ return current.rowIndex > acc ? current.rowIndex : acc
266
+ }, 0)
267
+ return maxRowIndex + 1
268
+ }),
269
+ distinctUntilChanged(),
270
+ )
271
+
272
+ const columnAmount$ = containerPosition$.pipe(
273
+ map(containerPosition => {
274
+ const maxColumnIndex = containerPosition.reduce((acc, current) => {
275
+ return current.columnIndex > acc ? current.columnIndex : acc
276
+ }, 0)
277
+ return maxColumnIndex + 1
278
+ }),
279
+ distinctUntilChanged()
280
+ )
281
+
282
+ return combineLatest({
283
+ layout: layout$,
284
+ rowAmount: rowAmount$,
285
+ columnAmount: columnAmount$,
286
+ container: container$
287
+ }).pipe(
288
+ switchMap(async (d) => d),
289
+ map(data => {
290
+ // const width = (data.layout.rootWidth / data.columnAmount) - (data.layout.left + data.layout.right)
291
+ // const height = (data.layout.rootHeight / data.rowAmount) - (data.layout.top + data.layout.bottom)
292
+ const columnGap = data.container.columnGap === 'auto'
293
+ ? data.layout.left + data.layout.right
294
+ : data.container.columnGap
295
+ const rowGap = data.container.rowGap === 'auto'
296
+ ? data.layout.top + data.layout.bottom
297
+ : data.container.rowGap
298
+ const width = (data.layout.rootWidth - data.layout.left - data.layout.right - (columnGap * (data.columnAmount - 1))) / data.columnAmount
299
+ const height = (data.layout.rootHeight - data.layout.top - data.layout.bottom - (rowGap * (data.rowAmount - 1))) / data.rowAmount
300
+
301
+ return {
302
+ width,
303
+ height
304
+ }
305
+ }),
306
+ distinctUntilChanged((a, b) => a.width === b.width && a.height === b.height),
307
+ // shareReplay(1)
308
+ )
294
309
  }