@kanaries/graphic-walker 0.2.16 → 0.2.18

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 (74) hide show
  1. package/dist/App.d.ts +3 -4
  2. package/dist/assets/viewQuery.worker-ffefc111.js.map +1 -1
  3. package/dist/dataSource/index.d.ts +0 -1
  4. package/dist/fields/encodeFields/singleEncodeEditor.d.ts +4 -4
  5. package/dist/graphic-walker.es.js +29716 -34641
  6. package/dist/graphic-walker.es.js.map +1 -1
  7. package/dist/graphic-walker.umd.js +218 -256
  8. package/dist/graphic-walker.umd.js.map +1 -1
  9. package/dist/interfaces.d.ts +31 -16
  10. package/dist/lib/inferMeta.d.ts +2 -9
  11. package/dist/lib/insights/explainByChildren.d.ts +16 -0
  12. package/dist/lib/insights/explainBySelection.d.ts +5 -0
  13. package/dist/lib/insights/explainValue.d.ts +2 -0
  14. package/dist/lib/insights/utils.d.ts +11 -0
  15. package/dist/lib/interfaces.d.ts +2 -1
  16. package/dist/lib/op/aggregate.d.ts +1 -1
  17. package/dist/lib/op/bin.d.ts +1 -1
  18. package/dist/lib/op/fold.d.ts +1 -1
  19. package/dist/lib/viewQuery.d.ts +1 -2
  20. package/dist/services.d.ts +1 -30
  21. package/dist/store/visualSpecStore.d.ts +1 -2
  22. package/dist/utils/autoMark.d.ts +1 -1
  23. package/dist/utils/dataPrep.d.ts +1 -2
  24. package/dist/vis/react-vega.d.ts +1 -0
  25. package/dist/vis/spec/encode.d.ts +1 -0
  26. package/dist/vis/spec/mark.d.ts +1 -1
  27. package/dist/vis/spec/tooltip.d.ts +4 -0
  28. package/dist/vis/spec/view.d.ts +9 -3
  29. package/package.json +2 -4
  30. package/src/App.tsx +55 -68
  31. package/src/dataSource/index.tsx +0 -17
  32. package/src/fields/aestheticFields.tsx +1 -2
  33. package/src/fields/encodeFields/singleEncodeEditor.tsx +11 -12
  34. package/src/fields/fieldsContext.tsx +1 -0
  35. package/src/interfaces.ts +75 -63
  36. package/src/lib/inferMeta.ts +26 -29
  37. package/src/lib/insights/explainByChildren.ts +50 -0
  38. package/src/lib/insights/explainBySelection.ts +47 -0
  39. package/src/lib/insights/explainValue.ts +30 -0
  40. package/src/lib/insights/utils.ts +21 -0
  41. package/src/lib/interfaces.ts +3 -9
  42. package/src/lib/op/aggregate.ts +1 -1
  43. package/src/lib/op/bin.ts +1 -1
  44. package/src/lib/op/fold.ts +1 -1
  45. package/src/lib/viewQuery.ts +1 -2
  46. package/src/locales/en-US.json +2 -1
  47. package/src/locales/zh-CN.json +2 -1
  48. package/src/main.tsx +4 -2
  49. package/src/renderer/specRenderer.tsx +2 -0
  50. package/src/services.ts +65 -67
  51. package/src/store/visualSpecStore.ts +3 -4
  52. package/src/utils/autoMark.ts +1 -1
  53. package/src/utils/dataPrep.ts +1 -2
  54. package/src/vis/react-vega.tsx +5 -0
  55. package/src/vis/spec/encode.ts +1 -0
  56. package/src/vis/spec/mark.ts +1 -1
  57. package/src/vis/spec/tooltip.ts +16 -0
  58. package/src/vis/spec/view.ts +12 -14
  59. package/dist/assets/explainer.worker-8428eb12.js.map +0 -1
  60. package/dist/insightBoard/index.d.ts +0 -3
  61. package/dist/insightBoard/mainBoard.d.ts +0 -11
  62. package/dist/insightBoard/radioGroupButtons.d.ts +0 -12
  63. package/dist/insightBoard/selectionSpec.d.ts +0 -13
  64. package/dist/insightBoard/std2vegaSpec.d.ts +0 -12
  65. package/dist/insightBoard/utils.d.ts +0 -8
  66. package/dist/insights.d.ts +0 -61
  67. package/src/insightBoard/index.tsx +0 -31
  68. package/src/insightBoard/mainBoard.tsx +0 -224
  69. package/src/insightBoard/radioGroupButtons.tsx +0 -57
  70. package/src/insightBoard/selectionSpec.ts +0 -113
  71. package/src/insightBoard/std2vegaSpec.ts +0 -184
  72. package/src/insightBoard/utils.ts +0 -32
  73. package/src/insights.ts +0 -408
  74. package/src/workers/explainer.worker.js +0 -76
@@ -1,3 +0,0 @@
1
- import React from "react";
2
- declare const _default: React.FunctionComponent<{}>;
3
- export default _default;
@@ -1,11 +0,0 @@
1
- import React from "react";
2
- import { IField, Filters, IRow } from "../interfaces";
3
- interface InsightMainBoardProps {
4
- dataSource: IRow[];
5
- fields: Readonly<IField[]>;
6
- filters?: Filters;
7
- viewDs: IField[];
8
- viewMs: IField[];
9
- }
10
- declare const InsightMainBoard: React.FC<InsightMainBoardProps>;
11
- export default InsightMainBoard;
@@ -1,12 +0,0 @@
1
- import React from 'react';
2
- export interface RGBOption {
3
- label: string;
4
- value: any;
5
- }
6
- interface RadioGroupButtonsProps {
7
- options: RGBOption[];
8
- onChange?: (value: any, index: number) => void;
9
- choosenIndex: number;
10
- }
11
- declare const RadioGroupButtons: React.FC<RadioGroupButtonsProps>;
12
- export default RadioGroupButtons;
@@ -1,13 +0,0 @@
1
- import { Specification } from 'visual-insights';
2
- import { IRow, SemanticType } from '../interfaces';
3
- export declare const geomTypeMap: {
4
- [key: string]: any;
5
- };
6
- export declare function selectionVis(query: Specification, dataSource: IRow[], dimensions: string[], measures: string[], aggregatedMeasures: Array<{
7
- op: string;
8
- field: string;
9
- as: string;
10
- }>, fieldFeatures: Array<{
11
- name: string;
12
- type: SemanticType;
13
- }>, defaultAggregated?: boolean, defaultStack?: boolean): any;
@@ -1,12 +0,0 @@
1
- import { Specification } from 'visual-insights';
2
- import { IField, IRow } from '../interfaces';
3
- import { IPredicate } from '../utils';
4
- export type IReasonType = 'selection_dim_distribution' | 'selection_mea_distribution' | 'children_major_factor' | 'children_outlier';
5
- export declare const geomTypeMap: {
6
- [key: string]: any;
7
- };
8
- export declare function baseVis(query: Specification, dataSource: IRow[], dimensions: string[], measures: string[], predicates: IPredicate[] | null, aggregatedMeasures: Array<{
9
- op: string;
10
- field: string;
11
- as: string;
12
- }>, fields: Readonly<IField[]>, type: IReasonType, defaultAggregated?: boolean, defaultStack?: boolean): any;
@@ -1,8 +0,0 @@
1
- import { IField, IMeasure } from "../interfaces";
2
- /**
3
- * 合并两个measures数组,若出现相同的key,使用measures2中的op替换1中的op
4
- * @param measures1
5
- * @param measures2
6
- */
7
- export declare function mergeMeasures(measures1: IMeasure[], measures2: IMeasure[]): IMeasure[];
8
- export declare function formatFieldName(fid: string, fields: Readonly<IField[]>): string;
@@ -1,61 +0,0 @@
1
- import { IMutField } from 'visual-insights';
2
- import { IRow, IMeasure } from './interfaces';
3
- import { IPredicate } from './utils';
4
- export interface IExplaination {
5
- dimensions: string[];
6
- measures: IMeasure[];
7
- extendDs: string[];
8
- extendMs: IMeasure[];
9
- type: string;
10
- score: number;
11
- description: any;
12
- predicates: IPredicate[];
13
- }
14
- export interface IMeasureWithStat extends IMeasure {
15
- score: number;
16
- }
17
- export declare class DataExplainer {
18
- dataSource: IRow[];
19
- private engine;
20
- private defaultAggs;
21
- constructor(dataSource?: IRow[]);
22
- setFields(fields: IMutField[]): void;
23
- preAnalysis(): this;
24
- explain(predicates: IPredicate[], dimensions: string[], measures: IMeasure[], threshold?: number): IExplaination[];
25
- explainConditionalValue(predicates: IPredicate[], dimensions: string[], measures: IMeasure[], K_Neighbor?: number): void;
26
- explainValue(predicates: IPredicate[], dimensions: string[], measures: IMeasure[]): number[];
27
- explainByChildren(predicates: IPredicate[], dimensions: string[], measures: IMeasure[], K_Neighbor?: number): {
28
- majorList: {
29
- key: string;
30
- score: number;
31
- dimensions: string[];
32
- measures: IMeasure[];
33
- }[];
34
- outlierList: {
35
- key: string;
36
- score: number;
37
- dimensions: string[];
38
- measures: IMeasure[];
39
- }[];
40
- };
41
- explainBySelection(predicates: IPredicate[], dimensions: string[], measures: IMeasure[], K_Neighbor?: number): {
42
- score: number;
43
- dimensions: string[];
44
- measures: IMeasure[];
45
- }[];
46
- explainByCorMeasures(predicates: IPredicate[], dimensions: string[], measures: IMeasure[], K_Neighbor?: number): {
47
- score: number;
48
- dimensions: string[];
49
- measures: IMeasure[];
50
- max: number;
51
- min: number;
52
- intMeasures: IMeasureWithStat[];
53
- }[];
54
- getGeneralizeKNN(type: 'dimension' | 'measure', fields: string[], K_Neighbor?: number, threshold?: number): string[];
55
- getKNN(type: 'dimension' | 'measure', fields: string[], K_Neighbor?: number, threshold?: number): string[];
56
- getCenterFields(type: 'dimension' | 'measure', num?: number): string[];
57
- getVisSpec(spaces: IExplaination[]): {
58
- schema: import("visual-insights/build/esm/insights/InsightFlow/specification/encoding").ISpec;
59
- dataView: import("visual-insights").IRow[];
60
- }[];
61
- }
@@ -1,31 +0,0 @@
1
- import { toJS } from "mobx";
2
- import { observer } from "mobx-react-lite";
3
- import React, { useCallback } from "react";
4
- import Modal from "../components/modal";
5
- import { useGlobalStore } from "../store";
6
- import InsightMainBoard from "./mainBoard";
7
-
8
- const InsightBoard: React.FC = (props) => {
9
- const { commonStore, vizStore } = useGlobalStore();
10
- const { showInsightBoard, currentDataset, filters } = commonStore;
11
- const { viewDimensions, viewMeasures, draggableFieldState } = vizStore;
12
- const onCloseModal = useCallback(() => {
13
- commonStore.setShowInsightBoard(false);
14
- }, []);
15
- if (!showInsightBoard) {
16
- return null;
17
- }
18
- return (
19
- <Modal onClose={onCloseModal} show={showInsightBoard}>
20
- <InsightMainBoard
21
- dataSource={currentDataset.dataSource}
22
- fields={toJS(draggableFieldState.fields)}
23
- viewDs={viewDimensions}
24
- viewMs={viewMeasures}
25
- filters={toJS(filters)}
26
- />
27
- </Modal>
28
- );
29
- };
30
-
31
- export default observer(InsightBoard);
@@ -1,224 +0,0 @@
1
- import React, { useEffect, useState, useMemo, useRef } from "react";
2
- import embed from "vega-embed";
3
- import { Insight, Utils, UnivariateSummary } from "visual-insights";
4
- import ReactJson from "react-json-view";
5
- import { IField, Filters, IMeasure, IRow } from "../interfaces";
6
- import { IExplaination, IMeasureWithStat } from "../insights";
7
- import { getExplaination, IVisSpace } from "../services";
8
- import { baseVis, IReasonType } from "./std2vegaSpec";
9
- import RadioGroupButtons from "./radioGroupButtons";
10
- import { formatFieldName, mergeMeasures } from "./utils";
11
- import { useTranslation } from "react-i18next";
12
- import { useCurrentMediaTheme } from "../utils/media";
13
- import { builtInThemes } from "../vis/theme";
14
-
15
- const collection = Insight.IntentionWorkerCollection.init();
16
-
17
- enum IReasonTypes {
18
- selection_dim_distribution = "selection_dim_distribution",
19
- selection_mea_distribution = "selection_mea_distribution",
20
- children_major_factor = "children_major_factor",
21
- children_outlier = "children_outlier",
22
- }
23
-
24
- collection.enable(Insight.DefaultIWorker.cluster, false);
25
- interface SubSpace {
26
- dimensions: string[];
27
- measures: IMeasure[];
28
- }
29
-
30
- interface InsightMainBoardProps {
31
- dataSource: IRow[];
32
- fields: Readonly<IField[]>;
33
- filters?: Filters;
34
- viewDs: IField[];
35
- viewMs: IField[];
36
- }
37
- const InsightMainBoard: React.FC<InsightMainBoardProps> = (props) => {
38
- const { dataSource, fields, viewDs, viewMs, filters } = props;
39
- const [recSpaces, setRecSpaces] = useState<IExplaination[]>([]);
40
- const [visSpaces, setVisSpaces] = useState<IVisSpace[]>([]);
41
- const [visIndex, setVisIndex] = useState<number>(0);
42
- const [loading, setLoading] = useState<boolean>(false);
43
- const [valueExp, setValueExp] = useState<IMeasureWithStat[]>([]);
44
- const { t } = useTranslation();
45
- const container = useRef<HTMLDivElement>(null);
46
- const mediaTheme = useCurrentMediaTheme();
47
- const themeConfig = builtInThemes['g2'][mediaTheme];
48
-
49
- const dimsWithTypes = useMemo(() => {
50
- const dimensions = fields
51
- .filter((f) => f.analyticType === "dimension")
52
- .map((f) => f.fid)
53
- .filter((f) => !Utils.isFieldUnique(dataSource, f));
54
- return UnivariateSummary.getAllFieldTypes(dataSource, dimensions);
55
- }, [fields, dataSource]);
56
-
57
- const measWithTypes = useMemo(() => {
58
- const measures = fields.filter((f) => f.analyticType === "measure").map((f) => f.fid);
59
- return measures.map((m) => ({
60
- name: m,
61
- type: "quantitative",
62
- }));
63
- }, [fields]);
64
-
65
- useEffect(() => {
66
- if (dimsWithTypes.length > 0 && measWithTypes.length > 0 && dataSource.length > 0) {
67
- const measures = fields.filter((f) => f.analyticType === "measure").map((f) => f.fid);
68
- const dimensions = dimsWithTypes.map((d) => d.name);
69
- const currentSpace: SubSpace = {
70
- dimensions: viewDs.map((f) => f.fid),
71
- measures: viewMs.map((f) => ({
72
- key: f.fid,
73
- op: f.aggName as any,
74
- })),
75
- };
76
- setLoading(true);
77
-
78
- getExplaination({
79
- dimensions,
80
- measures,
81
- dataSource,
82
- currentSpace,
83
- filters,
84
- }).then(({ visSpaces, explainations, valueExp }) => {
85
- setRecSpaces(explainations);
86
- setVisSpaces(visSpaces);
87
- setValueExp(valueExp);
88
- setLoading(false);
89
- });
90
- }
91
- }, [fields, viewDs, viewMs, measWithTypes, filters, dimsWithTypes, measWithTypes, dataSource]);
92
-
93
- useEffect(() => {
94
- const RecSpace = recSpaces[visIndex];
95
- const visSpec = visSpaces[visIndex];
96
- if (container.current && RecSpace && visSpec) {
97
- const usePredicates: boolean =
98
- RecSpace.type === "selection_dim_distribution" || RecSpace.type === "selection_mea_distribution";
99
- const mergedMeasures = mergeMeasures(RecSpace.measures, RecSpace.extendMs);
100
- const _vegaSpec = baseVis(
101
- visSpec.schema,
102
- visSpec.schema.geomType && visSpec.schema.geomType[0] === "point" ? dataSource : visSpec.dataView,
103
- // result.aggData,
104
- [...RecSpace.dimensions, ...RecSpace.extendDs],
105
- [...RecSpace.measures, ...RecSpace.extendMs].map((m) => m.key),
106
- usePredicates ? RecSpace.predicates : null,
107
- mergedMeasures.map((m) => ({
108
- op: m.op,
109
- field: m.key,
110
- as: m.key,
111
- })),
112
- fields,
113
- RecSpace.type as IReasonType,
114
- true,
115
- true
116
- );
117
- if (container.current) {
118
- embed(container.current, _vegaSpec, {
119
- actions: false,
120
- config: themeConfig
121
- });
122
- }
123
- }
124
- }, [visIndex, recSpaces, visSpaces, fields, dataSource, themeConfig]);
125
-
126
- const FilterDesc = useMemo<React.ReactElement[]>(() => {
127
- if (filters) {
128
- const dimValues = Object.keys(filters)
129
- .filter((k) => filters[k].length > 0)
130
- .map((k, ki) => {
131
- return (
132
- <div key={`dim-${ki}`}>
133
- <div className="inline bg-gray-400 py-0.5 px-2 mx-2 rounded-full underline text-white">
134
- {formatFieldName(k, fields)}
135
- </div>
136
- <div className="inline text-lg ml-1 mr-1">=</div>
137
- <div className="inline bg-blue-600 py-0.5 px-2 mx-2 rounded-full text-white">{filters[k]}</div>
138
- </div>
139
- );
140
- });
141
- return dimValues;
142
- }
143
- return [];
144
- }, [filters]);
145
-
146
- const valueDesc = useMemo<React.ReactElement[]>(() => {
147
- const meaStatus = valueExp.map((mea, mi) => (
148
- <div key={`mea-${mi}`}>
149
- <span className="bg-gray-400 py-0.5 px-2 mx-2 rounded-full underline text-white">
150
- {formatFieldName(mea.key, fields)}({mea.op})
151
- </span>
152
- <span className="bg-red-500 py-0.5 px-2 mx-2 rounded-full text-white">
153
- {mea.score === 1 ? t("explain.lg_than") : t("explain.sm_than")}
154
- </span>
155
- <span>{t("explain.expection")}{" "}</span>
156
- </div>
157
- ));
158
- return meaStatus;
159
- }, [valueExp]);
160
-
161
- return (
162
- <div style={{ maxHeight: "80vh", minHeight: "200px", overflowY: "auto", maxWidth: "880px" }}>
163
- <div className="text-xs">{FilterDesc}</div>
164
- <div className="text-xs mt-2 mb-2">{valueDesc}</div>
165
- {loading && (
166
- <div className="animate-spin inline-block mr-2 ml-2 w-16 h-16 rounded-full border-t-2 border-l-2 border-blue-500"></div>
167
- )}
168
- <div style={{ display: "flex" }}>
169
- <div style={{ flexBasis: "200px", flexShrink: 0, maxHeight: "800px", overflowY: "auto" }}>
170
- <RadioGroupButtons
171
- choosenIndex={visIndex}
172
- options={recSpaces.map((s, i) => ({
173
- value: s.type || "" + i,
174
- label: `${
175
- s.type ? t(`explain.reason.${IReasonTypes[s.type]}`) : t("explain.unrecognized")
176
- }: ${s.score.toFixed(2)}`,
177
- }))}
178
- onChange={(v, i) => {
179
- setVisIndex(i);
180
- }}
181
- />
182
- </div>
183
- <div className="p-4 text-sm">
184
- <div ref={container}></div>
185
- {recSpaces[visIndex] && (
186
- <div>
187
- {t("constant.analytic_type.dimension")} =
188
- {recSpaces[visIndex].dimensions.map((f) => formatFieldName(f, fields)).join(", ")}
189
- <br />
190
- {t("constant.analytic_type.measure")} =
191
- {recSpaces[visIndex].measures
192
- .map((m) => m.key)
193
- .map((f) => formatFieldName(f, fields))
194
- .join(", ")}
195
- <br />
196
- {" " + t("explain.contains") + " "}
197
- {recSpaces[visIndex].type
198
- ? t(`explain.reason.${IReasonTypes[recSpaces[visIndex].type]}`)
199
- : t("explain.unrecognized")}{" "}
200
- ,{t("explain.score")}
201
- {recSpaces[visIndex].score}
202
- <br />
203
- {recSpaces[visIndex].description &&
204
- recSpaces[visIndex].description.intMeasures &&
205
- FilterDesc +
206
- recSpaces[visIndex].description.intMeasures
207
- .map(
208
- (mea: any) =>
209
- `${formatFieldName(mea.key, fields)}(${mea.op})}${
210
- mea.score === 1 ? t("explain.lg_than") : t("explain.sm_than")
211
- }${t("explain.expection")}`
212
- )
213
- .join(", ")}
214
- <br />
215
- <ReactJson src={recSpaces[visIndex].description} />
216
- </div>
217
- )}
218
- </div>
219
- </div>
220
- </div>
221
- );
222
- };
223
-
224
- export default InsightMainBoard;
@@ -1,57 +0,0 @@
1
- import React from 'react';
2
- import styled from 'styled-components';
3
-
4
- const RGBContainer = styled.div`
5
- font-size: 14px;
6
- .option {
7
- padding: 0.8em;
8
- margin: 4px;
9
- border: 1px solid #f0f0f0;
10
- background-color: #f0f0f0;
11
- cursor: pointer;
12
- @media (prefers-color-scheme: dark) {
13
- background-color: #000;
14
- border: 1px solid #4b5563;
15
- }
16
- }
17
- .choosen.option {
18
- background-color: #fff;
19
- @media (prefers-color-scheme: dark) {
20
- background-color: #000;
21
- }
22
- }
23
- `;
24
-
25
- export interface RGBOption {
26
- label: string;
27
- value: any;
28
- }
29
-
30
- interface RadioGroupButtonsProps {
31
- options: RGBOption[];
32
- onChange?: (value: any, index: number) => void;
33
- choosenIndex: number;
34
- }
35
-
36
- const RadioGroupButtons: React.FC<RadioGroupButtonsProps> = (props) => {
37
- const { options, onChange, choosenIndex } = props;
38
- return (
39
- <RGBContainer>
40
- {options.map((op, i) => (
41
- <div
42
- key={i}
43
- className={`${choosenIndex === i ? 'choosen' : ''} option`}
44
- onClick={() => {
45
- if (onChange) {
46
- onChange(op.value, i);
47
- }
48
- }}
49
- >
50
- {op.label}
51
- </div>
52
- ))}
53
- </RGBContainer>
54
- );
55
- };
56
-
57
- export default RadioGroupButtons;
@@ -1,113 +0,0 @@
1
- import { Specification } from 'visual-insights';
2
- import { IRow, SemanticType } from '../interfaces';
3
- export const geomTypeMap: { [key: string]: any } = {
4
- interval: 'bar',
5
- line: 'line',
6
- point: 'point',
7
- // density: 'rect'
8
- density: 'point',
9
- };
10
- export function selectionVis(
11
- query: Specification,
12
- dataSource: IRow[],
13
- dimensions: string[],
14
- measures: string[],
15
- aggregatedMeasures: Array<{ op: string; field: string; as: string }>,
16
- fieldFeatures: Array<{ name: string; type: SemanticType }>,
17
- defaultAggregated?: boolean,
18
- defaultStack?: boolean
19
- ) {
20
- const {
21
- position = [],
22
- color = [],
23
- size = [],
24
- facets = [],
25
- opacity = [],
26
- geomType = [],
27
- page = [],
28
- } = query;
29
-
30
- function adjustField(fieldName: string): string {
31
- if (defaultAggregated && measures.includes(fieldName)) {
32
- let aggField = aggregatedMeasures.find((mea) => {
33
- return mea.field === fieldName;
34
- });
35
- return aggField ? aggField.as : fieldName;
36
- }
37
- return fieldName;
38
- }
39
-
40
- function getFieldType(field: string): SemanticType {
41
- let targetField = fieldFeatures.find((f) => f.name === field);
42
- return targetField ? targetField.type : 'nominal';
43
- }
44
-
45
- let chartWidth = 500; //container.current ? container.current.offsetWidth * 0.8 : 600;
46
- const fieldMap: any = {
47
- x: position[0],
48
- y: position[1],
49
- color: color[0],
50
- size: size[0],
51
- opacity: opacity[0],
52
- row: facets[0],
53
- column: facets[1],
54
- };
55
- let spec: any = {
56
- width: chartWidth,
57
- data: {
58
- values: dataSource,
59
- },
60
- };
61
- let basicSpec: any = {
62
- width: chartWidth,
63
- mark: {
64
- type: geomType[0] && geomTypeMap[geomType[0]] ? geomTypeMap[geomType[0]] : geomType[0],
65
- tooltip: true,
66
- },
67
- encoding: {},
68
- };
69
- for (let channel in fieldMap) {
70
- if (fieldMap[channel]) {
71
- basicSpec.encoding[channel] = {
72
- field: adjustField(fieldMap[channel]),
73
- type: getFieldType(fieldMap[channel]),
74
- };
75
- if (
76
- ['x', 'y'].includes(channel) &&
77
- getFieldType(fieldMap[channel]) === 'quantitative' &&
78
- !defaultStack
79
- ) {
80
- basicSpec.encoding[channel].stack = null;
81
- }
82
- }
83
- }
84
- if (!defaultStack && opacity.length === 0) {
85
- basicSpec.encoding.opacity = { value: 0.7 };
86
- }
87
- if (page.length === 0) {
88
- spec = {
89
- ...spec,
90
- ...basicSpec,
91
- };
92
- } else if (page.length > 0) {
93
- basicSpec.transform = [
94
- { filter: { selection: 'brush' } },
95
- defaultAggregated
96
- ? {
97
- aggregate: aggregatedMeasures,
98
- groupby: dimensions.filter((dim) => dim !== page[0]),
99
- }
100
- : null,
101
- ].filter(Boolean);
102
- let sliderSpec = {
103
- width: chartWidth,
104
- mark: 'tick',
105
- selection: { brush: { encodings: ['x'], type: 'interval' } },
106
- encoding: {
107
- x: { field: page[0], type: getFieldType(page[0]) },
108
- },
109
- };
110
- spec.vconcat = [basicSpec, sliderSpec];
111
- }
112
- return spec;
113
- }