@kanaries/graphic-walker 0.3.6 → 0.3.8
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/pivotTable/index.d.ts +1 -1
- package/dist/components/toggle.d.ts +8 -0
- package/dist/graphic-walker.es.js +17079 -16915
- 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/index.d.ts +1 -0
- package/dist/interfaces.d.ts +4 -0
- package/dist/renderer/hooks.d.ts +15 -0
- package/dist/renderer/pureRenderer.d.ts +12 -0
- package/dist/renderer/specRenderer.d.ts +7 -1
- 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/pivotTable/index.tsx +1 -1
- package/src/components/toggle.tsx +40 -0
- package/src/components/visualConfig/index.tsx +31 -7
- package/src/fields/aestheticFields.tsx +2 -0
- package/src/index.tsx +2 -0
- package/src/interfaces.ts +6 -0
- package/src/renderer/hooks.ts +73 -0
- package/src/renderer/index.tsx +61 -46
- package/src/renderer/pureRenderer.tsx +90 -0
- package/src/renderer/specRenderer.tsx +63 -28
- package/src/store/visualSpecStore.ts +4 -1
- package/src/vis/react-vega.tsx +25 -29
- package/src/vis/spec/mark.ts +1 -1
- package/src/vis/spec/tooltip.ts +4 -3
- package/src/vis/spec/view.ts +1 -1
- package/dist/utils/autoMark.d.ts +0 -7
- package/src/utils/autoMark.ts +0 -30
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { toJS } from 'mobx';
|
|
2
2
|
import { Resizable } from 're-resizable';
|
|
3
|
-
import React, {
|
|
3
|
+
import React, { forwardRef, useMemo } from 'react';
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import PivotTable from '../components/pivotTable';
|
|
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;
|
|
@@ -13,15 +15,20 @@ interface SpecRendererProps {
|
|
|
13
15
|
data: IRow[];
|
|
14
16
|
loading: boolean;
|
|
15
17
|
draggableFieldState: DeepReadonly<DraggableFieldState>;
|
|
16
|
-
visualConfig: IVisualConfig
|
|
18
|
+
visualConfig: DeepReadonly<IVisualConfig>;
|
|
19
|
+
onGeomClick?: ((values: any, e: any) => void) | undefined;
|
|
20
|
+
onChartResize?: ((width: number, height: number) => void) | undefined;
|
|
17
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Sans-store renderer of GraphicWalker.
|
|
24
|
+
* This is a pure component, which means it will not depend on any global state.
|
|
25
|
+
*/
|
|
18
26
|
const SpecRenderer = forwardRef<IReactVegaHandler, SpecRendererProps>(function (
|
|
19
|
-
{ themeKey, dark, data, loading, draggableFieldState, visualConfig },
|
|
27
|
+
{ themeKey, dark, data, loading, draggableFieldState, visualConfig, onGeomClick, onChartResize },
|
|
20
28
|
ref
|
|
21
29
|
) {
|
|
22
|
-
const { vizStore, commonStore } = useGlobalStore();
|
|
23
30
|
// const { draggableFieldState, visualConfig } = vizStore;
|
|
24
|
-
const { geoms, interactiveScale, defaultAggregated, stack, showActions, size, format: _format } = visualConfig;
|
|
31
|
+
const { geoms, interactiveScale, defaultAggregated, stack, showActions, size, format: _format, zeroScale } = visualConfig;
|
|
25
32
|
|
|
26
33
|
const rows = draggableFieldState.rows;
|
|
27
34
|
const columns = draggableFieldState.columns;
|
|
@@ -41,30 +48,59 @@ const SpecRenderer = forwardRef<IReactVegaHandler, SpecRendererProps>(function (
|
|
|
41
48
|
[columns]
|
|
42
49
|
);
|
|
43
50
|
|
|
51
|
+
const isPivotTable = geoms[0] === 'table';
|
|
52
|
+
|
|
44
53
|
const hasFacet = rowLeftFacetFields.length > 0 || colLeftFacetFields.length > 0;
|
|
45
54
|
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
const enableResize = size.mode === 'fixed' && !hasFacet && Boolean(onChartResize);
|
|
56
|
+
const mediaTheme = useCurrentMediaTheme(dark);
|
|
57
|
+
const themeConfig = builtInThemes[themeKey ?? 'vega']?.[mediaTheme];
|
|
58
|
+
|
|
59
|
+
const vegaConfig = useMemo<VegaGlobalConfig>(() => {
|
|
60
|
+
const config: VegaGlobalConfig = {
|
|
61
|
+
...themeConfig,
|
|
62
|
+
}
|
|
63
|
+
if (format.normalizedNumberFormat && format.normalizedNumberFormat.length > 0) {
|
|
64
|
+
// @ts-ignore
|
|
65
|
+
config.normalizedNumberFormat = format.normalizedNumberFormat;
|
|
66
|
+
}
|
|
67
|
+
if (format.numberFormat && format.numberFormat.length > 0) {
|
|
68
|
+
// @ts-ignore
|
|
69
|
+
config.numberFormat = format.numberFormat;
|
|
70
|
+
}
|
|
71
|
+
if (format.timeFormat && format.timeFormat.length > 0) {
|
|
72
|
+
// @ts-ignore
|
|
73
|
+
config.timeFormat = format.timeFormat;
|
|
74
|
+
}
|
|
75
|
+
// @ts-ignore
|
|
76
|
+
if (!config.scale) {
|
|
77
|
+
// @ts-ignore
|
|
78
|
+
config.scale = {};
|
|
79
|
+
}
|
|
80
|
+
// @ts-ignore
|
|
81
|
+
config.scale.zero = Boolean(zeroScale)
|
|
82
|
+
return config;
|
|
83
|
+
}, [themeConfig, zeroScale, format.normalizedNumberFormat, format.numberFormat, format.timeFormat])
|
|
84
|
+
|
|
85
|
+
if (isPivotTable) {
|
|
86
|
+
return (
|
|
87
|
+
<PivotTable
|
|
88
|
+
data={data}
|
|
89
|
+
draggableFieldState={draggableFieldState}
|
|
90
|
+
visualConfig={visualConfig}
|
|
91
|
+
loading={loading}
|
|
92
|
+
themeKey={themeKey}
|
|
93
|
+
dark={dark}
|
|
94
|
+
/>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
57
97
|
|
|
58
98
|
return (
|
|
59
99
|
<Resizable
|
|
60
100
|
className={enableResize ? 'border-blue-400 border-2 overflow-hidden' : ''}
|
|
61
101
|
style={{ padding: '12px' }}
|
|
62
102
|
onResizeStop={(e, direction, ref, d) => {
|
|
63
|
-
|
|
64
|
-
mode: 'fixed',
|
|
65
|
-
width: size.width + d.width,
|
|
66
|
-
height: size.height + d.height,
|
|
67
|
-
});
|
|
103
|
+
onChartResize?.(size.width + d.width, size.height + d.height);
|
|
68
104
|
}}
|
|
69
105
|
enable={
|
|
70
106
|
enableResize
|
|
@@ -87,7 +123,8 @@ const SpecRenderer = forwardRef<IReactVegaHandler, SpecRendererProps>(function (
|
|
|
87
123
|
>
|
|
88
124
|
{loading && <LoadingLayer />}
|
|
89
125
|
<ReactVega
|
|
90
|
-
|
|
126
|
+
vegaConfig={vegaConfig}
|
|
127
|
+
// format={format}
|
|
91
128
|
layoutMode={size.mode}
|
|
92
129
|
interactiveScale={interactiveScale}
|
|
93
130
|
geomType={geoms[0]}
|
|
@@ -108,9 +145,7 @@ const SpecRenderer = forwardRef<IReactVegaHandler, SpecRendererProps>(function (
|
|
|
108
145
|
width={size.width - 12 * 4}
|
|
109
146
|
height={size.height - 12 * 4}
|
|
110
147
|
ref={ref}
|
|
111
|
-
onGeomClick={
|
|
112
|
-
themeKey={themeKey}
|
|
113
|
-
dark={dark}
|
|
148
|
+
onGeomClick={onGeomClick}
|
|
114
149
|
/>
|
|
115
150
|
</Resizable>
|
|
116
151
|
);
|
|
@@ -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/mark.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { ISemanticType } from "../../interfaces";
|
|
|
7
7
|
*/
|
|
8
8
|
export function autoMark(semanticTypeList: ISemanticType[]): string {
|
|
9
9
|
if (semanticTypeList.length < 2) {
|
|
10
|
-
if (semanticTypeList[0] === "temporal") return "tick";
|
|
10
|
+
if (semanticTypeList[0] === "temporal" || semanticTypeList[0] === 'quantitative') return "tick";
|
|
11
11
|
return "bar";
|
|
12
12
|
}
|
|
13
13
|
const couter: Map<ISemanticType, number> = new Map();
|
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,
|
package/dist/utils/autoMark.d.ts
DELETED
package/src/utils/autoMark.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { ISemanticType } from "../interfaces";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
*
|
|
5
|
-
* @param semanticTypeList semanticTypeList.length <= 2,调用时,手动将columns 和 rows的最后一个元素组合传进来
|
|
6
|
-
* @returns geom(mark) type
|
|
7
|
-
*/
|
|
8
|
-
export function autoMark(semanticTypeList: ISemanticType[]): string {
|
|
9
|
-
if (semanticTypeList.length < 2) {
|
|
10
|
-
if (semanticTypeList[0] === "temporal") return "tick";
|
|
11
|
-
return "bar";
|
|
12
|
-
}
|
|
13
|
-
const couter: Map<ISemanticType, number> = new Map();
|
|
14
|
-
(["nominal", "ordinal", "quantitative", "temporal"] as ISemanticType[]).forEach((s) => {
|
|
15
|
-
couter.set(s, 0);
|
|
16
|
-
});
|
|
17
|
-
for (let st of semanticTypeList) {
|
|
18
|
-
couter.set(st, couter.get(st)! + 1);
|
|
19
|
-
}
|
|
20
|
-
if (couter.get("nominal") === 1 || couter.get("ordinal") === 1) {
|
|
21
|
-
return "bar";
|
|
22
|
-
}
|
|
23
|
-
if (couter.get("temporal") === 1 && couter.get("quantitative") === 1) {
|
|
24
|
-
return "line";
|
|
25
|
-
}
|
|
26
|
-
if (couter.get("quantitative") === 2) {
|
|
27
|
-
return "point";
|
|
28
|
-
}
|
|
29
|
-
return "point";
|
|
30
|
-
}
|