@kanaries/graphic-walker 0.3.6 → 0.3.7
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/dist/components/toggle.d.ts +8 -0
- package/dist/graphic-walker.es.js +16574 -16467
- package/dist/graphic-walker.es.js.map +1 -1
- package/dist/graphic-walker.umd.js +120 -120
- package/dist/graphic-walker.umd.js.map +1 -1
- package/dist/interfaces.d.ts +4 -0
- package/dist/vis/react-vega.d.ts +2 -5
- package/dist/vis/spec/tooltip.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/toggle.tsx +40 -0
- package/src/components/visualConfig/index.tsx +31 -7
- package/src/interfaces.ts +6 -0
- package/src/renderer/specRenderer.tsx +34 -5
- package/src/store/visualSpecStore.ts +4 -1
- package/src/vis/react-vega.tsx +25 -29
- package/src/vis/spec/tooltip.ts +4 -3
- package/src/vis/spec/view.ts +1 -1
package/dist/interfaces.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Config as VgConfig } from 'vega';
|
|
2
|
+
import { Config as VlConfig } from 'vega-lite';
|
|
1
3
|
export type DeepReadonly<T extends Record<keyof any, any>> = {
|
|
2
4
|
readonly [K in keyof T]: T[K] extends Record<keyof any, any> ? DeepReadonly<T[K]> : T[K];
|
|
3
5
|
};
|
|
@@ -164,6 +166,7 @@ export interface IVisualConfig {
|
|
|
164
166
|
showActions: boolean;
|
|
165
167
|
interactiveScale: boolean;
|
|
166
168
|
sorted: 'none' | 'ascending' | 'descending';
|
|
169
|
+
zeroScale: boolean;
|
|
167
170
|
format: {
|
|
168
171
|
numberFormat?: string;
|
|
169
172
|
timeFormat?: string;
|
|
@@ -187,3 +190,4 @@ export declare enum ISegmentKey {
|
|
|
187
190
|
}
|
|
188
191
|
export type IThemeKey = 'vega' | 'g2';
|
|
189
192
|
export type IDarkMode = 'media' | 'light' | 'dark';
|
|
193
|
+
export type VegaGlobalConfig = VgConfig | VlConfig;
|
package/dist/vis/react-vega.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { IViewField, IRow, IStackMode,
|
|
2
|
+
import { IViewField, IRow, IStackMode, VegaGlobalConfig } from '../interfaces';
|
|
3
3
|
export interface IReactVegaHandler {
|
|
4
4
|
getSVGData: () => Promise<string[]>;
|
|
5
5
|
getCanvasData: () => Promise<string[]>;
|
|
@@ -7,7 +7,6 @@ export interface IReactVegaHandler {
|
|
|
7
7
|
downloadPNG: (filename?: string) => Promise<string[]>;
|
|
8
8
|
}
|
|
9
9
|
interface ReactVegaProps {
|
|
10
|
-
format: IVisualConfig['format'];
|
|
11
10
|
rows: Readonly<IViewField[]>;
|
|
12
11
|
columns: Readonly<IViewField[]>;
|
|
13
12
|
dataSource: IRow[];
|
|
@@ -28,9 +27,7 @@ interface ReactVegaProps {
|
|
|
28
27
|
width: number;
|
|
29
28
|
height: number;
|
|
30
29
|
onGeomClick?: (values: any, e: any) => void;
|
|
31
|
-
|
|
32
|
-
themeKey?: IThemeKey;
|
|
33
|
-
dark?: IDarkMode;
|
|
30
|
+
vegaConfig: VegaGlobalConfig;
|
|
34
31
|
}
|
|
35
32
|
declare const ReactVega: React.ForwardRefExoticComponent<ReactVegaProps & React.RefAttributes<IReactVegaHandler>>;
|
|
36
33
|
export default ReactVega;
|
package/package.json
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Switch } from '@headlessui/react';
|
|
3
|
+
|
|
4
|
+
function classNames(...classes: string[]) {
|
|
5
|
+
return classes.filter(Boolean).join(' ');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface ToggleProps {
|
|
9
|
+
enabled: boolean;
|
|
10
|
+
onChange: (enabled: boolean) => void;
|
|
11
|
+
label?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default function Toggle(props: ToggleProps) {
|
|
15
|
+
const { enabled, onChange, label } = props;
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<Switch.Group as="div" className="flex items-center">
|
|
19
|
+
<Switch
|
|
20
|
+
checked={enabled}
|
|
21
|
+
onChange={onChange}
|
|
22
|
+
className={classNames(
|
|
23
|
+
enabled ? 'bg-indigo-600' : 'bg-gray-200',
|
|
24
|
+
'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2'
|
|
25
|
+
)}
|
|
26
|
+
>
|
|
27
|
+
<span
|
|
28
|
+
aria-hidden="true"
|
|
29
|
+
className={classNames(
|
|
30
|
+
enabled ? 'translate-x-5' : 'translate-x-0',
|
|
31
|
+
'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out'
|
|
32
|
+
)}
|
|
33
|
+
/>
|
|
34
|
+
</Switch>
|
|
35
|
+
<Switch.Label as="span" className="ml-3 text-sm">
|
|
36
|
+
<span className="font-medium text-gray-900">{label}</span>
|
|
37
|
+
</Switch.Label>
|
|
38
|
+
</Switch.Group>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -6,6 +6,8 @@ import { IVisualConfig } from '../../interfaces';
|
|
|
6
6
|
import PrimaryButton from '../button/primary';
|
|
7
7
|
import DefaultButton from '../button/default';
|
|
8
8
|
import { useTranslation } from 'react-i18next';
|
|
9
|
+
import Toggle from '../toggle';
|
|
10
|
+
import { runInAction } from 'mobx';
|
|
9
11
|
|
|
10
12
|
const VisualConfigPanel: React.FC = (props) => {
|
|
11
13
|
const { commonStore, vizStore } = useGlobalStore();
|
|
@@ -22,6 +24,7 @@ const VisualConfigPanel: React.FC = (props) => {
|
|
|
22
24
|
timeFormat: visualConfig.format.timeFormat,
|
|
23
25
|
normalizedNumberFormat: visualConfig.format.normalizedNumberFormat,
|
|
24
26
|
});
|
|
27
|
+
const [zeroScale, setZeroScale] = useState<boolean>(visualConfig.zeroScale);
|
|
25
28
|
|
|
26
29
|
return (
|
|
27
30
|
<Modal
|
|
@@ -31,8 +34,17 @@ const VisualConfigPanel: React.FC = (props) => {
|
|
|
31
34
|
}}
|
|
32
35
|
>
|
|
33
36
|
<div>
|
|
34
|
-
<h2 className=
|
|
35
|
-
<p className=
|
|
37
|
+
<h2 className="text-lg mb-4">{t('config.format')}</h2>
|
|
38
|
+
<p className="text-xs">
|
|
39
|
+
Format guides docs:{' '}
|
|
40
|
+
<a
|
|
41
|
+
target="_blank"
|
|
42
|
+
className="underline text-blue-500"
|
|
43
|
+
href="https://github.com/d3/d3-format#locale_format"
|
|
44
|
+
>
|
|
45
|
+
read here
|
|
46
|
+
</a>
|
|
47
|
+
</p>
|
|
36
48
|
{formatConfigList.map((fc) => (
|
|
37
49
|
<div className="my-2" key={fc}>
|
|
38
50
|
<label className="block text-xs font-medium leading-6 text-gray-900">{t(`config.${fc}`)}</label>
|
|
@@ -51,18 +63,30 @@ const VisualConfigPanel: React.FC = (props) => {
|
|
|
51
63
|
</div>
|
|
52
64
|
</div>
|
|
53
65
|
))}
|
|
54
|
-
<div className=
|
|
66
|
+
<div className="my-2">
|
|
67
|
+
<Toggle
|
|
68
|
+
label="zero scale"
|
|
69
|
+
enabled={zeroScale}
|
|
70
|
+
onChange={(en) => {
|
|
71
|
+
setZeroScale(en);
|
|
72
|
+
}}
|
|
73
|
+
/>
|
|
74
|
+
</div>
|
|
75
|
+
<div className="mt-4">
|
|
55
76
|
<PrimaryButton
|
|
56
77
|
text={t('actions.confirm')}
|
|
57
|
-
className=
|
|
78
|
+
className="mr-2"
|
|
58
79
|
onClick={() => {
|
|
59
|
-
|
|
60
|
-
|
|
80
|
+
runInAction(() => {
|
|
81
|
+
vizStore.setVisualConfig('format', format);
|
|
82
|
+
vizStore.setVisualConfig('zeroScale', zeroScale);
|
|
83
|
+
commonStore.setShowVisualConfigPanel(false);
|
|
84
|
+
})
|
|
61
85
|
}}
|
|
62
86
|
/>
|
|
63
87
|
<DefaultButton
|
|
64
88
|
text={t('actions.cancel')}
|
|
65
|
-
className=
|
|
89
|
+
className="mr-2"
|
|
66
90
|
onClick={() => {
|
|
67
91
|
commonStore.setShowVisualConfigPanel(false);
|
|
68
92
|
}}
|
package/src/interfaces.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {Config as VgConfig} from 'vega';
|
|
2
|
+
import {Config as VlConfig} from 'vega-lite';
|
|
3
|
+
|
|
1
4
|
export type DeepReadonly<T extends Record<keyof any, any>> = {
|
|
2
5
|
readonly [K in keyof T]: T[K] extends Record<keyof any, any> ? DeepReadonly<T[K]> : T[K];
|
|
3
6
|
};
|
|
@@ -192,6 +195,7 @@ export interface IVisualConfig {
|
|
|
192
195
|
showActions: boolean;
|
|
193
196
|
interactiveScale: boolean;
|
|
194
197
|
sorted: 'none' | 'ascending' | 'descending';
|
|
198
|
+
zeroScale: boolean;
|
|
195
199
|
format: {
|
|
196
200
|
numberFormat?: string;
|
|
197
201
|
timeFormat?: string;
|
|
@@ -218,3 +222,5 @@ export enum ISegmentKey {
|
|
|
218
222
|
|
|
219
223
|
export type IThemeKey = 'vega' | 'g2';
|
|
220
224
|
export type IDarkMode = 'media' | 'light' | 'dark';
|
|
225
|
+
|
|
226
|
+
export type VegaGlobalConfig = VgConfig | VlConfig;
|
|
@@ -4,8 +4,10 @@ import React, { useCallback, forwardRef, useMemo } from 'react';
|
|
|
4
4
|
|
|
5
5
|
import { useGlobalStore } from '../store';
|
|
6
6
|
import ReactVega, { IReactVegaHandler } from '../vis/react-vega';
|
|
7
|
-
import { DeepReadonly, DraggableFieldState, IDarkMode, IRow, IThemeKey, IVisualConfig } from '../interfaces';
|
|
7
|
+
import { DeepReadonly, DraggableFieldState, IDarkMode, IRow, IThemeKey, IVisualConfig, VegaGlobalConfig } from '../interfaces';
|
|
8
8
|
import LoadingLayer from '../components/loadingLayer';
|
|
9
|
+
import { useCurrentMediaTheme } from '../utils/media';
|
|
10
|
+
import { builtInThemes } from '../vis/theme';
|
|
9
11
|
|
|
10
12
|
interface SpecRendererProps {
|
|
11
13
|
themeKey?: IThemeKey;
|
|
@@ -21,7 +23,7 @@ const SpecRenderer = forwardRef<IReactVegaHandler, SpecRendererProps>(function (
|
|
|
21
23
|
) {
|
|
22
24
|
const { vizStore, commonStore } = useGlobalStore();
|
|
23
25
|
// const { draggableFieldState, visualConfig } = vizStore;
|
|
24
|
-
const { geoms, interactiveScale, defaultAggregated, stack, showActions, size, format: _format } = visualConfig;
|
|
26
|
+
const { geoms, interactiveScale, defaultAggregated, stack, showActions, size, format: _format, zeroScale } = visualConfig;
|
|
25
27
|
|
|
26
28
|
const rows = draggableFieldState.rows;
|
|
27
29
|
const columns = draggableFieldState.columns;
|
|
@@ -54,6 +56,34 @@ const SpecRenderer = forwardRef<IReactVegaHandler, SpecRendererProps>(function (
|
|
|
54
56
|
[]
|
|
55
57
|
);
|
|
56
58
|
const enableResize = size.mode === 'fixed' && !hasFacet;
|
|
59
|
+
const mediaTheme = useCurrentMediaTheme(dark);
|
|
60
|
+
const themeConfig = builtInThemes[themeKey ?? 'vega']?.[mediaTheme];
|
|
61
|
+
|
|
62
|
+
const vegaConfig = useMemo<VegaGlobalConfig>(() => {
|
|
63
|
+
const config: VegaGlobalConfig = {
|
|
64
|
+
...themeConfig,
|
|
65
|
+
}
|
|
66
|
+
if (format.normalizedNumberFormat && format.normalizedNumberFormat.length > 0) {
|
|
67
|
+
// @ts-ignore
|
|
68
|
+
config.normalizedNumberFormat = format.normalizedNumberFormat;
|
|
69
|
+
}
|
|
70
|
+
if (format.numberFormat && format.numberFormat.length > 0) {
|
|
71
|
+
// @ts-ignore
|
|
72
|
+
config.numberFormat = format.numberFormat;
|
|
73
|
+
}
|
|
74
|
+
if (format.timeFormat && format.timeFormat.length > 0) {
|
|
75
|
+
// @ts-ignore
|
|
76
|
+
config.timeFormat = format.timeFormat;
|
|
77
|
+
}
|
|
78
|
+
// @ts-ignore
|
|
79
|
+
if (!config.scale) {
|
|
80
|
+
// @ts-ignore
|
|
81
|
+
config.scale = {};
|
|
82
|
+
}
|
|
83
|
+
// @ts-ignore
|
|
84
|
+
config.scale.zero = Boolean(zeroScale)
|
|
85
|
+
return config;
|
|
86
|
+
}, [themeConfig, zeroScale, format.normalizedNumberFormat, format.numberFormat, format.timeFormat])
|
|
57
87
|
|
|
58
88
|
return (
|
|
59
89
|
<Resizable
|
|
@@ -87,7 +117,8 @@ const SpecRenderer = forwardRef<IReactVegaHandler, SpecRendererProps>(function (
|
|
|
87
117
|
>
|
|
88
118
|
{loading && <LoadingLayer />}
|
|
89
119
|
<ReactVega
|
|
90
|
-
|
|
120
|
+
vegaConfig={vegaConfig}
|
|
121
|
+
// format={format}
|
|
91
122
|
layoutMode={size.mode}
|
|
92
123
|
interactiveScale={interactiveScale}
|
|
93
124
|
geomType={geoms[0]}
|
|
@@ -109,8 +140,6 @@ const SpecRenderer = forwardRef<IReactVegaHandler, SpecRendererProps>(function (
|
|
|
109
140
|
height={size.height - 12 * 4}
|
|
110
141
|
ref={ref}
|
|
111
142
|
onGeomClick={handleGeomClick}
|
|
112
|
-
themeKey={themeKey}
|
|
113
|
-
dark={dark}
|
|
114
143
|
/>
|
|
115
144
|
</Resizable>
|
|
116
145
|
);
|
|
@@ -70,6 +70,7 @@ export function initVisualConfig(): IVisualConfig {
|
|
|
70
70
|
showActions: false,
|
|
71
71
|
interactiveScale: false,
|
|
72
72
|
sorted: "none",
|
|
73
|
+
zeroScale: true,
|
|
73
74
|
size: {
|
|
74
75
|
mode: "auto",
|
|
75
76
|
width: 320,
|
|
@@ -371,14 +372,16 @@ export class VizSpecStore {
|
|
|
371
372
|
case configKey === "geoms" && Array.isArray(value):
|
|
372
373
|
case configKey === "size" && typeof value === "object":
|
|
373
374
|
case configKey === "sorted":
|
|
375
|
+
case configKey === "zeroScale":
|
|
374
376
|
case configKey === "stack": {
|
|
375
377
|
return (config[configKey] = value);
|
|
376
378
|
}
|
|
377
379
|
case configKey === 'format' && typeof value === "object": {
|
|
378
380
|
return config[configKey] = value
|
|
379
381
|
}
|
|
382
|
+
|
|
380
383
|
default: {
|
|
381
|
-
console.error("unknown key" + configKey);
|
|
384
|
+
console.error("[unknown key] " + configKey + " You should registered visualConfig at setVisualConfig");
|
|
382
385
|
}
|
|
383
386
|
}
|
|
384
387
|
});
|
package/src/vis/react-vega.tsx
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import React, { useEffect, useState, useMemo, forwardRef, useImperativeHandle, useRef } from 'react';
|
|
2
|
-
import embed
|
|
2
|
+
import embed from 'vega-embed';
|
|
3
3
|
import { Subject, Subscription } from 'rxjs'
|
|
4
4
|
import * as op from 'rxjs/operators';
|
|
5
5
|
import type { ScenegraphEvent, View } from 'vega';
|
|
6
6
|
import styled from 'styled-components';
|
|
7
7
|
|
|
8
|
-
import { IViewField, IRow, IStackMode,
|
|
8
|
+
import { IViewField, IRow, IStackMode, VegaGlobalConfig } from '../interfaces';
|
|
9
9
|
import { useTranslation } from 'react-i18next';
|
|
10
10
|
import { getVegaTimeFormatRules } from './temporalFormat';
|
|
11
|
-
import {
|
|
12
|
-
import { useCurrentMediaTheme } from '../utils/media';
|
|
13
|
-
import { SingleViewProps, getSingleView } from './spec/view';
|
|
11
|
+
import { getSingleView } from './spec/view';
|
|
14
12
|
import { NULL_FIELD } from './spec/field';
|
|
15
13
|
|
|
16
14
|
const CanvaContainer = styled.div<{rowSize: number; colSize: number;}>`
|
|
@@ -27,7 +25,6 @@ export interface IReactVegaHandler {
|
|
|
27
25
|
downloadPNG: (filename?: string) => Promise<string[]>;
|
|
28
26
|
}
|
|
29
27
|
interface ReactVegaProps {
|
|
30
|
-
format: IVisualConfig['format'];
|
|
31
28
|
rows: Readonly<IViewField[]>;
|
|
32
29
|
columns: Readonly<IViewField[]>;
|
|
33
30
|
dataSource: IRow[];
|
|
@@ -48,9 +45,7 @@ interface ReactVegaProps {
|
|
|
48
45
|
width: number;
|
|
49
46
|
height: number;
|
|
50
47
|
onGeomClick?: (values: any, e: any) => void
|
|
51
|
-
|
|
52
|
-
themeKey?: IThemeKey;
|
|
53
|
-
dark?: IDarkMode;
|
|
48
|
+
vegaConfig: VegaGlobalConfig;
|
|
54
49
|
}
|
|
55
50
|
|
|
56
51
|
const click$ = new Subject<ScenegraphEvent>();
|
|
@@ -98,30 +93,31 @@ const ReactVega = forwardRef<IReactVegaHandler, ReactVegaProps>(function ReactVe
|
|
|
98
93
|
width,
|
|
99
94
|
height,
|
|
100
95
|
details = [],
|
|
101
|
-
themeKey = 'vega',
|
|
102
|
-
dark = 'media',
|
|
103
|
-
|
|
96
|
+
// themeKey = 'vega',
|
|
97
|
+
// dark = 'media',
|
|
98
|
+
vegaConfig,
|
|
99
|
+
// format
|
|
104
100
|
} = props;
|
|
105
101
|
const [viewPlaceholders, setViewPlaceholders] = useState<React.MutableRefObject<HTMLDivElement>[]>([]);
|
|
106
102
|
const { i18n } = useTranslation();
|
|
107
|
-
const mediaTheme = useCurrentMediaTheme(dark);
|
|
108
|
-
const themeConfig = builtInThemes[themeKey]?.[mediaTheme];
|
|
103
|
+
// const mediaTheme = useCurrentMediaTheme(dark);
|
|
104
|
+
// const themeConfig = builtInThemes[themeKey]?.[mediaTheme];
|
|
109
105
|
|
|
110
|
-
const vegaConfig = useMemo(() => {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}, [themeConfig, format.normalizedNumberFormat, format.numberFormat, format.timeFormat])
|
|
106
|
+
// const vegaConfig = useMemo(() => {
|
|
107
|
+
// const config: any = {
|
|
108
|
+
// ...themeConfig,
|
|
109
|
+
// }
|
|
110
|
+
// if (format.normalizedNumberFormat && format.normalizedNumberFormat.length > 0) {
|
|
111
|
+
// config.normalizedNumberFormat = format.normalizedNumberFormat;
|
|
112
|
+
// }
|
|
113
|
+
// if (format.numberFormat && format.numberFormat.length > 0) {
|
|
114
|
+
// config.numberFormat = format.numberFormat;
|
|
115
|
+
// }
|
|
116
|
+
// if (format.timeFormat && format.timeFormat.length > 0) {
|
|
117
|
+
// config.timeFormat = format.timeFormat;
|
|
118
|
+
// }
|
|
119
|
+
// return config;
|
|
120
|
+
// }, [themeConfig, format.normalizedNumberFormat, format.numberFormat, format.timeFormat])
|
|
125
121
|
|
|
126
122
|
useEffect(() => {
|
|
127
123
|
const clickSub = geomClick$.subscribe(([values, e]) => {
|
package/src/vis/spec/tooltip.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { IViewField } from "../../interfaces";
|
|
2
|
+
import { getMeaAggKey } from "../../utils";
|
|
2
3
|
|
|
3
|
-
export function addTooltipEncode (encoding: {[key: string]: any}, details: Readonly<IViewField[]> = []) {
|
|
4
|
+
export function addTooltipEncode (encoding: {[key: string]: any}, details: Readonly<IViewField[]> = [], defaultAggregated = false) {
|
|
4
5
|
const encs = Object.keys(encoding).filter(ck => ck !== 'tooltip').map(ck => {
|
|
5
6
|
return {
|
|
6
7
|
field: encoding[ck].field,
|
|
@@ -8,8 +9,8 @@ export function addTooltipEncode (encoding: {[key: string]: any}, details: Reado
|
|
|
8
9
|
title: encoding[ck].title
|
|
9
10
|
}
|
|
10
11
|
}).concat(details.map(f => ({
|
|
11
|
-
field: f.fid,
|
|
12
|
-
title: f.name,
|
|
12
|
+
field: defaultAggregated ? getMeaAggKey(f.fid, f.aggName) : f.fid,
|
|
13
|
+
title: defaultAggregated && f.aggName ? `${f.aggName}(${f.name})` : f.name,
|
|
13
14
|
type: f.semanticType
|
|
14
15
|
})))
|
|
15
16
|
encoding.tooltip = encs
|
package/src/vis/spec/view.ts
CHANGED
|
@@ -64,10 +64,10 @@ export function getSingleView(props: SingleViewProps) {
|
|
|
64
64
|
details,
|
|
65
65
|
text
|
|
66
66
|
});
|
|
67
|
-
addTooltipEncode(encoding, details)
|
|
68
67
|
if (defaultAggregated) {
|
|
69
68
|
channelAggregate(encoding, fields);
|
|
70
69
|
}
|
|
70
|
+
addTooltipEncode(encoding, details, defaultAggregated);
|
|
71
71
|
channelStack(encoding, stack);
|
|
72
72
|
const mark = {
|
|
73
73
|
type: markType,
|