@kanaries/graphic-walker 0.3.5 → 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.
@@ -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
  };
@@ -75,7 +77,7 @@ export interface IField {
75
77
  analyticType: IAnalyticType;
76
78
  cmp?: (a: any, b: any) => number;
77
79
  computed?: boolean;
78
- expressoion?: IExpression;
80
+ expression?: IExpression;
79
81
  }
80
82
  export interface IViewField extends IField {
81
83
  dragId: string;
@@ -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;
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { IViewField, IRow, IStackMode, IDarkMode, IThemeKey, IVisualConfig } from '../interfaces';
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
- /** @default "vega" */
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;
@@ -1,4 +1,4 @@
1
1
  import { IViewField } from "../../interfaces";
2
2
  export declare function addTooltipEncode(encoding: {
3
3
  [key: string]: any;
4
- }, details?: Readonly<IViewField[]>): void;
4
+ }, details?: Readonly<IViewField[]>, defaultAggregated?: boolean): void;
@@ -1,9 +1,12 @@
1
1
  import React from 'react';
2
2
  import { IDarkMode } from '../interfaces';
3
3
  import { IReactVegaHandler } from '../vis/react-vega';
4
+ import { ToolbarItemProps } from '../components/toolbar';
4
5
  interface IVisualSettings {
5
6
  darkModePreference: IDarkMode;
6
7
  rendererHandler?: React.RefObject<IReactVegaHandler>;
8
+ exclude?: string[];
9
+ extra?: ToolbarItemProps[];
7
10
  }
8
11
  declare const _default: React.FunctionComponent<IVisualSettings>;
9
12
  export default _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kanaries/graphic-walker",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "scripts": {
5
5
  "dev:front_end": "vite --host",
6
6
  "dev": "npm run dev:front_end",
package/src/App.tsx CHANGED
@@ -19,6 +19,7 @@ import DatasetConfig from './dataSource/datasetConfig';
19
19
  import { useCurrentMediaTheme } from './utils/media';
20
20
  import CodeExport from './components/codeExport';
21
21
  import VisualConfig from './components/visualConfig';
22
+ import type { ToolbarItemProps } from './components/toolbar';
22
23
 
23
24
  export interface IGWProps {
24
25
  dataSource?: IRow[];
@@ -36,6 +37,10 @@ export interface IGWProps {
36
37
  themeKey?: IThemeKey;
37
38
  dark?: IDarkMode;
38
39
  storeRef?: React.MutableRefObject<IGlobalStore | null>;
40
+ toolbar?: {
41
+ extra?: ToolbarItemProps[];
42
+ exclude?: string[];
43
+ };
39
44
  }
40
45
 
41
46
  const App = observer<IGWProps>(function App(props) {
@@ -49,6 +54,7 @@ const App = observer<IGWProps>(function App(props) {
49
54
  fieldKeyGuard = true,
50
55
  themeKey = 'vega',
51
56
  dark = 'media',
57
+ toolbar,
52
58
  } = props;
53
59
  const { commonStore, vizStore } = useGlobalStore();
54
60
 
@@ -124,7 +130,7 @@ const App = observer<IGWProps>(function App(props) {
124
130
  style={{ marginTop: '0em', borderTop: 'none' }}
125
131
  className="m-4 p-4 border border-gray-200 dark:border-gray-700"
126
132
  >
127
- <VisualSettings rendererHandler={rendererRef} darkModePreference={dark} />
133
+ <VisualSettings rendererHandler={rendererRef} darkModePreference={dark} exclude={toolbar?.exclude} extra={toolbar?.extra} />
128
134
  <CodeExport />
129
135
  <VisualConfig />
130
136
  <div className="md:grid md:grid-cols-12 xl:grid-cols-6">
@@ -187,3 +193,5 @@ const App = observer<IGWProps>(function App(props) {
187
193
  });
188
194
 
189
195
  export default App;
196
+
197
+ export type { ToolbarItemProps };
@@ -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='text-lg mb-4'>{t('config.format')}</h2>
35
- <p className='text-xs'>Format guides docs: <a target="_blank" className='underline text-blue-500' href="https://github.com/d3/d3-format#locale_format">read here</a></p>
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='mt-4'>
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='mr-2'
78
+ className="mr-2"
58
79
  onClick={() => {
59
- vizStore.setVisualConfig('format', format);
60
- commonStore.setShowVisualConfigPanel(false);
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='mr-2'
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
  };
@@ -86,7 +89,7 @@ export interface IField {
86
89
  analyticType: IAnalyticType;
87
90
  cmp?: (a: any, b: any) => number;
88
91
  computed?: boolean;
89
- expressoion?: IExpression;
92
+ expression?: IExpression;
90
93
  }
91
94
 
92
95
  export interface IViewField extends IField {
@@ -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;
@@ -148,7 +148,8 @@
148
148
  "descending": "Sort in Descending Order",
149
149
  "transpose": "Transpose",
150
150
  "export_chart": "Export",
151
- "export_chart_as": "Export as {{type}}"
151
+ "export_chart_as": "Export as {{type}}",
152
+ "export_code": "Export Code"
152
153
  },
153
154
  "size": "Resize",
154
155
  "size_setting": {
@@ -147,7 +147,8 @@
147
147
  "descending": "降順に並べ替える",
148
148
  "transpose": "転置",
149
149
  "export_chart": "エクスポート",
150
- "export_chart_as": "{{type}}としてエクスポート"
150
+ "export_chart_as": "{{type}}としてエクスポート",
151
+ "export_code": "コードをエクスポート"
151
152
  },
152
153
  "size": "サイズ変更",
153
154
  "size_setting": {
@@ -148,7 +148,8 @@
148
148
  "descending": "降序排序",
149
149
  "transpose": "转置",
150
150
  "export_chart": "导出",
151
- "export_chart_as": "导出 {{type}}"
151
+ "export_chart_as": "导出 {{type}}",
152
+ "export_code": "导出代码"
152
153
  },
153
154
  "size": "调整尺寸",
154
155
  "size_setting": {
@@ -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
- format={format}
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
  });
@@ -505,7 +508,7 @@ export class VizSpecStore {
505
508
  semanticType: "ordinal",
506
509
  analyticType: "dimension",
507
510
  computed: true,
508
- expressoion: {
511
+ expression: {
509
512
  op: binType,
510
513
  as: newVarKey,
511
514
  params: [
@@ -535,7 +538,7 @@ export class VizSpecStore {
535
538
  analyticType: originField.analyticType,
536
539
  aggName: 'sum',
537
540
  computed: true,
538
- expressoion: {
541
+ expression: {
539
542
  op: scaleType,
540
543
  as: newVarKey,
541
544
  params: [
@@ -246,7 +246,7 @@ export function createCountField(): IViewField {
246
246
  semanticType: "quantitative",
247
247
  aggName: 'sum',
248
248
  computed: true,
249
- expressoion: {
249
+ expression: {
250
250
  op: 'one',
251
251
  params: [],
252
252
  as: COUNT_FIELD_ID,
@@ -1,16 +1,14 @@
1
1
  import React, { useEffect, useState, useMemo, forwardRef, useImperativeHandle, useRef } from 'react';
2
- import embed, { vega } from 'vega-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, IDarkMode, IThemeKey, IVisualConfig } from '../interfaces';
8
+ import { IViewField, IRow, IStackMode, VegaGlobalConfig } from '../interfaces';
9
9
  import { useTranslation } from 'react-i18next';
10
10
  import { getVegaTimeFormatRules } from './temporalFormat';
11
- import { builtInThemes } from './theme';
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
- /** @default "vega" */
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
- format
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
- const config: any = {
112
- ...themeConfig,
113
- }
114
- if (format.normalizedNumberFormat && format.normalizedNumberFormat.length > 0) {
115
- config.normalizedNumberFormat = format.normalizedNumberFormat;
116
- }
117
- if (format.numberFormat && format.numberFormat.length > 0) {
118
- config.numberFormat = format.numberFormat;
119
- }
120
- if (format.timeFormat && format.timeFormat.length > 0) {
121
- config.timeFormat = format.timeFormat;
122
- }
123
- return config;
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]) => {
@@ -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
@@ -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,
@@ -61,9 +61,11 @@ const FormContainer = styled.div`
61
61
  interface IVisualSettings {
62
62
  darkModePreference: IDarkMode;
63
63
  rendererHandler?: React.RefObject<IReactVegaHandler>;
64
+ exclude?: string[];
65
+ extra?: ToolbarItemProps[];
64
66
  }
65
67
 
66
- const VisualSettings: React.FC<IVisualSettings> = ({ rendererHandler, darkModePreference }) => {
68
+ const VisualSettings: React.FC<IVisualSettings> = ({ rendererHandler, darkModePreference, extra = [], exclude = [] }) => {
67
69
  const { vizStore, commonStore } = useGlobalStore();
68
70
  const { visualConfig, canUndo, canRedo } = vizStore;
69
71
  const { t: tGlobal } = useTranslation();
@@ -85,7 +87,7 @@ const VisualSettings: React.FC<IVisualSettings> = ({ rendererHandler, darkModePr
85
87
  const dark = useCurrentMediaTheme(darkModePreference) === 'dark';
86
88
 
87
89
  const items = useMemo<ToolbarItemProps[]>(() => {
88
- return [
90
+ const builtInItems = [
89
91
  {
90
92
  key: 'undo',
91
93
  label: 'undo (Ctrl + Z)',
@@ -296,9 +298,20 @@ const VisualSettings: React.FC<IVisualSettings> = ({ rendererHandler, darkModePr
296
298
  onClick: () => {
297
299
  commonStore.setShowCodeExportPanel(true);
298
300
  }
299
- }
301
+ },
300
302
  ] as ToolbarItemProps[];
301
- }, [vizStore, canUndo, canRedo, defaultAggregated, markType, stack, interactiveScale, sizeMode, width, height, showActions, downloadPNG, downloadSVG, dark]);
303
+
304
+ const items = builtInItems.filter(item => typeof item === 'string' || !exclude.includes(item.key));
305
+
306
+ if (extra.length > 0) {
307
+ items.push(
308
+ '-',
309
+ ...extra,
310
+ );
311
+ }
312
+
313
+ return items;
314
+ }, [vizStore, canUndo, canRedo, defaultAggregated, markType, stack, interactiveScale, sizeMode, width, height, showActions, downloadPNG, downloadSVG, dark, extra, exclude]);
302
315
 
303
316
  return <div style={{ margin: '0.38em 0.28em 0.2em 0.18em' }}>
304
317
  <Toolbar
@@ -6,7 +6,7 @@ export function transformData(data: IRow[], columns: IField[]) {
6
6
  let df = dataset2DataFrame(data, columns);
7
7
  for (let i = 0; i < computedFields.length; i++) {
8
8
  const field = computedFields[i];
9
- df = execExpression(field.expressoion!, df, columns);
9
+ df = execExpression(field.expression!, df, columns);
10
10
  }
11
11
  return dataframe2Dataset(df, columns);
12
12
  }