@kanaries/graphic-walker 0.3.16 → 0.4.1
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/App.d.ts +9 -2
- package/dist/assets/filter.worker-f09fcd6f.js.map +1 -1
- package/dist/assets/sort.worker-f77540ac.js.map +1 -0
- package/dist/assets/transform.worker-bae8e910.js.map +1 -0
- package/dist/assets/{viewQuery.worker-03404216.js.map → viewQuery.worker-bdb6477c.js.map} +1 -1
- package/dist/components/askViz/index.d.ts +6 -0
- package/dist/components/askViz/schemaTransform.d.ts +2 -0
- package/dist/components/dataTable/index.d.ts +9 -5
- package/dist/components/pivotTable/store.d.ts +0 -2
- package/dist/components/spinner.d.ts +2 -0
- package/dist/computation/clientComputation.d.ts +3 -0
- package/dist/computation/serverComputation.d.ts +8 -0
- package/dist/config.d.ts +3 -1
- package/dist/fields/filterField/tabs.d.ts +2 -1
- package/dist/graphic-walker.es.js +22268 -21682
- package/dist/graphic-walker.es.js.map +1 -1
- package/dist/graphic-walker.umd.js +139 -139
- package/dist/graphic-walker.umd.js.map +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/interfaces.d.ts +93 -4
- package/dist/lib/execExp.d.ts +4 -4
- package/dist/lib/interfaces.d.ts +1 -0
- package/dist/lib/viewQuery.d.ts +2 -2
- package/dist/renderer/hooks.d.ts +8 -4
- package/dist/renderer/index.d.ts +2 -1
- package/dist/renderer/pureRenderer.d.ts +17 -1
- package/dist/renderer/specRenderer.d.ts +1 -0
- package/dist/services.d.ts +8 -5
- package/dist/store/commonStore.d.ts +2 -2
- package/dist/store/visualSpecStore.d.ts +58 -42
- package/dist/utils/save.d.ts +10 -2
- package/dist/utils/workflow.d.ts +3 -0
- package/dist/vis/react-vega.d.ts +3 -1
- package/dist/workers/sort.d.ts +2 -2
- package/dist/workers/transform.d.ts +5 -2
- package/package.json +2 -2
- package/src/App.tsx +46 -7
- package/src/components/askViz/index.tsx +93 -0
- package/src/components/askViz/schemaTransform.ts +38 -0
- package/src/components/dataTable/index.tsx +53 -11
- package/src/components/limitSetting.tsx +8 -6
- package/src/components/pivotTable/index.tsx +0 -1
- package/src/components/pivotTable/store.tsx +0 -16
- package/src/components/sizeSetting.tsx +9 -7
- package/src/components/spinner.tsx +14 -0
- package/src/components/toggle.tsx +2 -2
- package/src/components/visualConfig/index.tsx +78 -8
- package/src/computation/clientComputation.ts +55 -0
- package/src/computation/serverComputation.ts +158 -0
- package/src/config.ts +15 -2
- package/src/dataSource/datasetConfig/index.tsx +38 -6
- package/src/dataSource/table.tsx +15 -2
- package/src/fields/filterField/filterEditDialog.tsx +11 -10
- package/src/fields/filterField/tabs.tsx +178 -77
- package/src/hooks/index.ts +8 -0
- package/src/index.tsx +2 -0
- package/src/interfaces.ts +108 -5
- package/src/lib/execExp.ts +20 -11
- package/src/lib/interfaces.ts +1 -0
- package/src/lib/op/aggregate.ts +1 -1
- package/src/lib/viewQuery.ts +2 -2
- package/src/locales/en-US.json +11 -2
- package/src/locales/ja-JP.json +11 -2
- package/src/locales/zh-CN.json +11 -2
- package/src/main.tsx +1 -1
- package/src/renderer/hooks.ts +57 -69
- package/src/renderer/index.tsx +10 -6
- package/src/renderer/pureRenderer.tsx +40 -14
- package/src/renderer/specRenderer.tsx +24 -7
- package/src/services.ts +7 -8
- package/src/store/commonStore.ts +7 -7
- package/src/store/visualSpecStore.ts +288 -192
- package/src/utils/save.ts +81 -3
- package/src/utils/workflow.ts +148 -0
- package/src/vis/react-vega.tsx +21 -6
- package/src/vis/spec/aggregate.ts +3 -2
- package/src/vis/spec/stack.ts +7 -6
- package/src/visualSettings/index.tsx +2 -4
- package/src/workers/filter.worker.js +1 -1
- package/src/workers/sort.ts +3 -4
- package/src/workers/sort.worker.ts +2 -2
- package/src/workers/transform.ts +7 -8
- package/src/workers/transform.worker.js +2 -2
- package/src/workers/viewQuery.worker.js +2 -2
- package/dist/assets/sort.worker-4299a6a0.js.map +0 -1
- package/dist/assets/transform.worker-a12fb3d8.js.map +0 -1
|
@@ -1,46 +1,70 @@
|
|
|
1
|
-
import { IReactionDisposer, makeAutoObservable, observable, reaction, toJS } from
|
|
2
|
-
import produce from
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import { IReactionDisposer, makeAutoObservable, observable, computed, reaction, toJS } from 'mobx';
|
|
2
|
+
import produce from 'immer';
|
|
3
|
+
import {
|
|
4
|
+
DataSet,
|
|
5
|
+
DraggableFieldState,
|
|
6
|
+
IFilterRule,
|
|
7
|
+
ISortMode,
|
|
8
|
+
IStackMode,
|
|
9
|
+
IViewField,
|
|
10
|
+
IVisSpec,
|
|
11
|
+
IVisSpecForExport,
|
|
12
|
+
IFilterFieldForExport,
|
|
13
|
+
IVisualConfig,
|
|
14
|
+
Specification,
|
|
15
|
+
IComputationFunction,
|
|
16
|
+
} from '../interfaces';
|
|
17
|
+
import { CHANNEL_LIMIT, GEMO_TYPES, MetaFieldKeys } from '../config';
|
|
18
|
+
import { VisSpecWithHistory } from '../models/visSpecHistory';
|
|
19
|
+
import {
|
|
20
|
+
IStoInfo,
|
|
21
|
+
dumpsGWPureSpec,
|
|
22
|
+
parseGWContent,
|
|
23
|
+
parseGWPureSpec,
|
|
24
|
+
stringifyGWContent,
|
|
25
|
+
initVisualConfig,
|
|
26
|
+
forwardVisualConfigs,
|
|
27
|
+
visSpecDecoder,
|
|
28
|
+
} from '../utils/save';
|
|
29
|
+
import { CommonStore } from './commonStore';
|
|
30
|
+
import { createCountField } from '../utils';
|
|
31
|
+
import { COUNT_FIELD_ID } from '../constants';
|
|
32
|
+
import { nanoid } from 'nanoid';
|
|
33
|
+
import { toWorkflow } from '../utils/workflow';
|
|
10
34
|
|
|
11
35
|
function getChannelSizeLimit(channel: string): number {
|
|
12
|
-
if (typeof CHANNEL_LIMIT[channel] ===
|
|
36
|
+
if (typeof CHANNEL_LIMIT[channel] === 'undefined') return Infinity;
|
|
13
37
|
return CHANNEL_LIMIT[channel];
|
|
14
38
|
}
|
|
15
39
|
|
|
16
40
|
function uniqueId(): string {
|
|
17
|
-
return
|
|
41
|
+
return 'gw_' + nanoid(4);
|
|
18
42
|
}
|
|
19
43
|
|
|
20
44
|
function geomAdapter(geom: string) {
|
|
21
45
|
switch (geom) {
|
|
22
|
-
case
|
|
23
|
-
case
|
|
24
|
-
return
|
|
25
|
-
case
|
|
26
|
-
return
|
|
27
|
-
case
|
|
28
|
-
return
|
|
29
|
-
case
|
|
30
|
-
return
|
|
31
|
-
case
|
|
32
|
-
return
|
|
33
|
-
case
|
|
34
|
-
return
|
|
35
|
-
case
|
|
36
|
-
return
|
|
37
|
-
case
|
|
38
|
-
return
|
|
39
|
-
case
|
|
40
|
-
return
|
|
41
|
-
case
|
|
46
|
+
case 'interval':
|
|
47
|
+
case 'bar':
|
|
48
|
+
return 'bar';
|
|
49
|
+
case 'line':
|
|
50
|
+
return 'line';
|
|
51
|
+
case 'boxplot':
|
|
52
|
+
return 'boxplot';
|
|
53
|
+
case 'area':
|
|
54
|
+
return 'area';
|
|
55
|
+
case 'point':
|
|
56
|
+
return 'point';
|
|
57
|
+
case 'arc':
|
|
58
|
+
return 'arc';
|
|
59
|
+
case 'circle':
|
|
60
|
+
return 'circle';
|
|
61
|
+
case 'heatmap':
|
|
62
|
+
return 'circle';
|
|
63
|
+
case 'rect':
|
|
64
|
+
return 'rect';
|
|
65
|
+
case 'tick':
|
|
42
66
|
default:
|
|
43
|
-
return
|
|
67
|
+
return 'tick';
|
|
44
68
|
}
|
|
45
69
|
}
|
|
46
70
|
|
|
@@ -62,42 +86,33 @@ export function initEncoding(): DraggableFieldState {
|
|
|
62
86
|
};
|
|
63
87
|
}
|
|
64
88
|
|
|
65
|
-
|
|
66
|
-
return
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
89
|
+
function stackValueTransform(vlValue: string | undefined | null): IStackMode {
|
|
90
|
+
if (vlValue === 'center') return 'center';
|
|
91
|
+
if (vlValue === 'normalize') return 'normalize';
|
|
92
|
+
if (vlValue === 'zero') return 'zero';
|
|
93
|
+
return 'none';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function sortValueTransform(vlValue: object | string | null): ISortMode {
|
|
97
|
+
let order: string = 'none';
|
|
98
|
+
if (typeof vlValue === 'string') {
|
|
99
|
+
order = vlValue;
|
|
100
|
+
} else if (vlValue && vlValue instanceof Object) {
|
|
101
|
+
order = vlValue['order'] ?? 'ascending';
|
|
102
|
+
}
|
|
103
|
+
if (order !== 'none') {
|
|
104
|
+
const channels: string[] = ['x', 'y', 'color', 'size', 'opacity'];
|
|
105
|
+
// TODO: support all sorting config in vl
|
|
106
|
+
if (order.startsWith('-') || order === 'descending') return 'descending';
|
|
107
|
+
if (channels.indexOf(order) > -1 || order === 'ascending') return 'ascending';
|
|
108
|
+
}
|
|
109
|
+
return 'none';
|
|
85
110
|
}
|
|
86
111
|
|
|
87
112
|
type DeepReadonly<T extends Record<keyof any, any>> = {
|
|
88
113
|
readonly [K in keyof T]: T[K] extends Record<keyof any, any> ? DeepReadonly<T[K]> : T[K];
|
|
89
114
|
};
|
|
90
115
|
|
|
91
|
-
const forwardVisualConfigs = (backwards: ReturnType<typeof parseGWContent>["specList"]): IVisSpecForExport[] => {
|
|
92
|
-
return backwards.map((content) => ({
|
|
93
|
-
...content,
|
|
94
|
-
config: {
|
|
95
|
-
...initVisualConfig(),
|
|
96
|
-
...content.config,
|
|
97
|
-
},
|
|
98
|
-
}));
|
|
99
|
-
};
|
|
100
|
-
|
|
101
116
|
function isDraggableStateEmpty(state: DeepReadonly<DraggableFieldState>): boolean {
|
|
102
117
|
return Object.values(state).every((value) => value.length === 0);
|
|
103
118
|
}
|
|
@@ -143,6 +158,8 @@ export class VizSpecStore {
|
|
|
143
158
|
public canUndo = false;
|
|
144
159
|
public canRedo = false;
|
|
145
160
|
public editingFilterIdx: number | null = null;
|
|
161
|
+
// TODO
|
|
162
|
+
public computationFuction: IComputationFunction = async () => [];
|
|
146
163
|
constructor(commonStore: CommonStore) {
|
|
147
164
|
this.commonStore = commonStore;
|
|
148
165
|
this.draggableFieldState = initEncoding();
|
|
@@ -157,6 +174,7 @@ export class VizSpecStore {
|
|
|
157
174
|
);
|
|
158
175
|
makeAutoObservable(this, {
|
|
159
176
|
visList: observable.shallow,
|
|
177
|
+
computationFuction: observable.ref,
|
|
160
178
|
// @ts-expect-error private fields are not supported
|
|
161
179
|
reactions: false,
|
|
162
180
|
});
|
|
@@ -204,8 +222,7 @@ export class VizSpecStore {
|
|
|
204
222
|
private useMutable(cb: (tab: { encodings: DraggableFieldState; config: IVisualConfig }) => void) {
|
|
205
223
|
if (this.__dangerous_is_inside_useMutable__) {
|
|
206
224
|
throw new Error(
|
|
207
|
-
|
|
208
|
-
"this is prevented because update will be overwritten by parent execution context."
|
|
225
|
+
'A recursive call of useMutable() is detected, ' + 'this is prevented because update will be overwritten by parent execution context.'
|
|
209
226
|
);
|
|
210
227
|
}
|
|
211
228
|
|
|
@@ -269,7 +286,7 @@ export class VizSpecStore {
|
|
|
269
286
|
(Object.keys(state) as (keyof DraggableFieldState)[])
|
|
270
287
|
.filter((dkey) => !MetaFieldKeys.includes(dkey))
|
|
271
288
|
.forEach((dkey) => {
|
|
272
|
-
fields.push(...state[dkey].filter((f) => f.analyticType ===
|
|
289
|
+
fields.push(...state[dkey].filter((f) => f.analyticType === 'dimension'));
|
|
273
290
|
});
|
|
274
291
|
return fields;
|
|
275
292
|
}
|
|
@@ -283,7 +300,7 @@ export class VizSpecStore {
|
|
|
283
300
|
(Object.keys(state) as (keyof DraggableFieldState)[])
|
|
284
301
|
.filter((dkey) => !MetaFieldKeys.includes(dkey))
|
|
285
302
|
.forEach((dkey) => {
|
|
286
|
-
fields.push(...state[dkey].filter((f) => f.analyticType ===
|
|
303
|
+
fields.push(...state[dkey].filter((f) => f.analyticType === 'measure'));
|
|
287
304
|
});
|
|
288
305
|
return fields;
|
|
289
306
|
}
|
|
@@ -299,7 +316,6 @@ export class VizSpecStore {
|
|
|
299
316
|
return state.filters;
|
|
300
317
|
}
|
|
301
318
|
|
|
302
|
-
|
|
303
319
|
public addVisualization(defaultName?: string) {
|
|
304
320
|
const name = defaultName || 'Chart ' + (this.visList.length + 1);
|
|
305
321
|
this.visList.push(
|
|
@@ -318,8 +334,8 @@ export class VizSpecStore {
|
|
|
318
334
|
public setVisName(visIndex: number, name: string) {
|
|
319
335
|
this.visList[visIndex] = this.visList[visIndex].clone();
|
|
320
336
|
this.visList[visIndex].updateLatest({
|
|
321
|
-
name
|
|
322
|
-
})
|
|
337
|
+
name,
|
|
338
|
+
});
|
|
323
339
|
}
|
|
324
340
|
public initState() {
|
|
325
341
|
this.useMutable((tab) => {
|
|
@@ -331,7 +347,7 @@ export class VizSpecStore {
|
|
|
331
347
|
const countField = createCountField();
|
|
332
348
|
this.useMutable(({ encodings }) => {
|
|
333
349
|
encodings.dimensions = dataset.rawFields
|
|
334
|
-
.filter((f) => f.analyticType ===
|
|
350
|
+
.filter((f) => f.analyticType === 'dimension')
|
|
335
351
|
.map((f) => ({
|
|
336
352
|
dragId: uniqueId(),
|
|
337
353
|
fid: f.fid,
|
|
@@ -341,7 +357,7 @@ export class VizSpecStore {
|
|
|
341
357
|
analyticType: f.analyticType,
|
|
342
358
|
}));
|
|
343
359
|
encodings.measures = dataset.rawFields
|
|
344
|
-
.filter((f) => f.analyticType ===
|
|
360
|
+
.filter((f) => f.analyticType === 'measure')
|
|
345
361
|
.map((f) => ({
|
|
346
362
|
dragId: uniqueId(),
|
|
347
363
|
fid: f.fid,
|
|
@@ -349,13 +365,16 @@ export class VizSpecStore {
|
|
|
349
365
|
basename: f.basename || f.name || f.fid,
|
|
350
366
|
analyticType: f.analyticType,
|
|
351
367
|
semanticType: f.semanticType,
|
|
352
|
-
aggName:
|
|
368
|
+
aggName: 'sum',
|
|
353
369
|
}));
|
|
354
370
|
encodings.measures.push(countField);
|
|
355
371
|
});
|
|
356
372
|
|
|
357
373
|
this.freezeHistory();
|
|
358
374
|
}
|
|
375
|
+
/**
|
|
376
|
+
* clear all config in draggable state
|
|
377
|
+
*/
|
|
359
378
|
public clearState() {
|
|
360
379
|
this.useMutable(({ encodings }) => {
|
|
361
380
|
for (let key in encodings) {
|
|
@@ -368,31 +387,34 @@ export class VizSpecStore {
|
|
|
368
387
|
public setVisualConfig<K extends keyof IVisualConfig>(configKey: K, value: IVisualConfig[K]) {
|
|
369
388
|
this.useMutable(({ config }) => {
|
|
370
389
|
switch (true) {
|
|
371
|
-
case [
|
|
390
|
+
case ['defaultAggregated', 'defaultStack', 'showActions', 'interactiveScale'].includes(configKey): {
|
|
372
391
|
return ((config as unknown as { [k: string]: boolean })[configKey] = Boolean(value));
|
|
373
392
|
}
|
|
374
|
-
case configKey ===
|
|
375
|
-
case configKey ===
|
|
376
|
-
case configKey ===
|
|
377
|
-
case configKey ===
|
|
378
|
-
case configKey ===
|
|
393
|
+
case configKey === 'geoms' && Array.isArray(value):
|
|
394
|
+
case configKey === 'size' && typeof value === 'object':
|
|
395
|
+
case configKey === 'sorted':
|
|
396
|
+
case configKey === 'zeroScale':
|
|
397
|
+
case configKey === 'background':
|
|
398
|
+
case configKey === 'resolve':
|
|
399
|
+
case configKey === 'limit':
|
|
400
|
+
case configKey === 'stack': {
|
|
379
401
|
return (config[configKey] = value);
|
|
380
402
|
}
|
|
381
|
-
case configKey === 'format' && typeof value ===
|
|
382
|
-
return config[configKey] = value
|
|
403
|
+
case configKey === 'format' && typeof value === 'object': {
|
|
404
|
+
return (config[configKey] = value);
|
|
383
405
|
}
|
|
384
406
|
|
|
385
407
|
default: {
|
|
386
|
-
console.error(
|
|
408
|
+
console.error('[unknown key] ' + configKey + ' You should registered visualConfig at setVisualConfig');
|
|
387
409
|
}
|
|
388
410
|
}
|
|
389
411
|
});
|
|
390
412
|
}
|
|
391
|
-
public transformCoord(coord:
|
|
392
|
-
if (coord ===
|
|
413
|
+
public transformCoord(coord: 'cartesian' | 'polar') {
|
|
414
|
+
if (coord === 'polar') {
|
|
393
415
|
}
|
|
394
416
|
}
|
|
395
|
-
public setChartLayout(props: { mode: IVisualConfig[
|
|
417
|
+
public setChartLayout(props: { mode: IVisualConfig['size']['mode']; width?: number; height?: number }) {
|
|
396
418
|
this.useMutable(({ config }) => {
|
|
397
419
|
const { mode = config.size.mode, width = config.size.width, height = config.size.height } = props;
|
|
398
420
|
|
|
@@ -411,15 +433,10 @@ export class VizSpecStore {
|
|
|
411
433
|
fields.splice(destinationIndex, 0, field);
|
|
412
434
|
});
|
|
413
435
|
}
|
|
414
|
-
public moveField(
|
|
415
|
-
sourceKey
|
|
416
|
-
sourceIndex: number,
|
|
417
|
-
destinationKey: keyof DraggableFieldState,
|
|
418
|
-
destinationIndex: number
|
|
419
|
-
) {
|
|
420
|
-
if (sourceKey === "filters") {
|
|
436
|
+
public moveField(sourceKey: keyof DraggableFieldState, sourceIndex: number, destinationKey: keyof DraggableFieldState, destinationIndex: number) {
|
|
437
|
+
if (sourceKey === 'filters') {
|
|
421
438
|
return this.removeField(sourceKey, sourceIndex);
|
|
422
|
-
} else if (destinationKey ===
|
|
439
|
+
} else if (destinationKey === 'filters') {
|
|
423
440
|
return this.appendFilter(destinationIndex, this.draggableFieldState[sourceKey][sourceIndex]);
|
|
424
441
|
}
|
|
425
442
|
|
|
@@ -439,7 +456,7 @@ export class VizSpecStore {
|
|
|
439
456
|
if (MetaFieldKeys.includes(destinationKey)) {
|
|
440
457
|
if (!MetaFieldKeys.includes(sourceKey)) return;
|
|
441
458
|
encodings[sourceKey].splice(sourceIndex, 1);
|
|
442
|
-
movingField.analyticType = destinationKey ===
|
|
459
|
+
movingField.analyticType = destinationKey === 'dimensions' ? 'dimension' : 'measure';
|
|
443
460
|
}
|
|
444
461
|
const limitSize = getChannelSizeLimit(destinationKey);
|
|
445
462
|
const fixedDestinationIndex = Math.min(destinationIndex, limitSize - 1);
|
|
@@ -457,10 +474,7 @@ export class VizSpecStore {
|
|
|
457
474
|
}
|
|
458
475
|
public replaceField(sourceKey: keyof DraggableFieldState, sourceIndex: number, fid: string) {
|
|
459
476
|
if (MetaFieldKeys.includes(sourceKey)) return;
|
|
460
|
-
const enteringField = [
|
|
461
|
-
...this.draggableFieldState.dimensions,
|
|
462
|
-
...this.draggableFieldState.measures
|
|
463
|
-
].find(which => which.fid === fid);
|
|
477
|
+
const enteringField = [...this.draggableFieldState.dimensions, ...this.draggableFieldState.measures].find((which) => which.fid === fid);
|
|
464
478
|
if (!enteringField) {
|
|
465
479
|
return;
|
|
466
480
|
}
|
|
@@ -499,16 +513,23 @@ export class VizSpecStore {
|
|
|
499
513
|
encodings.rows = fieldsInCup as typeof encodings.rows; // assume this as writable
|
|
500
514
|
});
|
|
501
515
|
}
|
|
502
|
-
public createBinField(stateKey: keyof DraggableFieldState, index: number, binType: 'bin' | 'binCount') {
|
|
516
|
+
public createBinField(stateKey: keyof DraggableFieldState, index: number, binType: 'bin' | 'binCount'): string {
|
|
517
|
+
const newVarKey = uniqueId();
|
|
518
|
+
const state = this.draggableFieldState;
|
|
519
|
+
const existedRelatedBinField = state.dimensions.find(
|
|
520
|
+
(f) => f.computed && f.expression && f.expression.op === binType && f.expression.params[0].value === state[stateKey][index].fid
|
|
521
|
+
);
|
|
522
|
+
if (existedRelatedBinField) {
|
|
523
|
+
return existedRelatedBinField.fid;
|
|
524
|
+
}
|
|
503
525
|
this.useMutable(({ encodings }) => {
|
|
504
526
|
const originField = encodings[stateKey][index];
|
|
505
|
-
const newVarKey = uniqueId();
|
|
506
527
|
const binField: IViewField = {
|
|
507
528
|
fid: newVarKey,
|
|
508
529
|
dragId: newVarKey,
|
|
509
530
|
name: `${binType}(${originField.name})`,
|
|
510
|
-
semanticType:
|
|
511
|
-
analyticType:
|
|
531
|
+
semanticType: 'ordinal',
|
|
532
|
+
analyticType: 'dimension',
|
|
512
533
|
computed: true,
|
|
513
534
|
expression: {
|
|
514
535
|
op: binType,
|
|
@@ -516,16 +537,17 @@ export class VizSpecStore {
|
|
|
516
537
|
params: [
|
|
517
538
|
{
|
|
518
539
|
type: 'field',
|
|
519
|
-
value: originField.fid
|
|
520
|
-
}
|
|
521
|
-
]
|
|
522
|
-
}
|
|
540
|
+
value: originField.fid,
|
|
541
|
+
},
|
|
542
|
+
],
|
|
543
|
+
},
|
|
523
544
|
};
|
|
524
545
|
encodings.dimensions.push(binField);
|
|
525
546
|
});
|
|
547
|
+
return newVarKey;
|
|
526
548
|
}
|
|
527
549
|
public createLogField(stateKey: keyof DraggableFieldState, index: number, scaleType: 'log10' | 'log2') {
|
|
528
|
-
if (stateKey ===
|
|
550
|
+
if (stateKey === 'filters') {
|
|
529
551
|
return;
|
|
530
552
|
}
|
|
531
553
|
|
|
@@ -536,7 +558,7 @@ export class VizSpecStore {
|
|
|
536
558
|
fid: newVarKey,
|
|
537
559
|
dragId: newVarKey,
|
|
538
560
|
name: `${scaleType}(${originField.name})`,
|
|
539
|
-
semanticType:
|
|
561
|
+
semanticType: 'quantitative',
|
|
540
562
|
analyticType: originField.analyticType,
|
|
541
563
|
aggName: 'sum',
|
|
542
564
|
computed: true,
|
|
@@ -546,10 +568,10 @@ export class VizSpecStore {
|
|
|
546
568
|
params: [
|
|
547
569
|
{
|
|
548
570
|
type: 'field',
|
|
549
|
-
value: originField.fid
|
|
550
|
-
}
|
|
551
|
-
]
|
|
552
|
-
}
|
|
571
|
+
value: originField.fid,
|
|
572
|
+
},
|
|
573
|
+
],
|
|
574
|
+
},
|
|
553
575
|
};
|
|
554
576
|
encodings[stateKey].push(logField);
|
|
555
577
|
});
|
|
@@ -567,74 +589,150 @@ export class VizSpecStore {
|
|
|
567
589
|
const { rows, columns } = this.draggableFieldState;
|
|
568
590
|
const yField = rows.length > 0 ? rows[rows.length - 1] : null;
|
|
569
591
|
const xField = columns.length > 0 ? columns[columns.length - 1] : null;
|
|
570
|
-
if (
|
|
571
|
-
xField !== null &&
|
|
572
|
-
xField.analyticType === "dimension" &&
|
|
573
|
-
yField !== null &&
|
|
574
|
-
yField.analyticType === "measure"
|
|
575
|
-
) {
|
|
592
|
+
if (xField !== null && xField.analyticType === 'dimension' && yField !== null && yField.analyticType === 'measure') {
|
|
576
593
|
return true;
|
|
577
594
|
}
|
|
578
|
-
if (
|
|
579
|
-
xField !== null &&
|
|
580
|
-
xField.analyticType === "measure" &&
|
|
581
|
-
yField !== null &&
|
|
582
|
-
yField.analyticType === "dimension"
|
|
583
|
-
) {
|
|
595
|
+
if (xField !== null && xField.analyticType === 'measure' && yField !== null && yField.analyticType === 'dimension') {
|
|
584
596
|
return true;
|
|
585
597
|
}
|
|
586
598
|
return false;
|
|
587
599
|
}
|
|
588
|
-
public setFieldSort(
|
|
589
|
-
stateKey: keyof DraggableFieldState,
|
|
590
|
-
index: number,
|
|
591
|
-
sortType: "none" | "ascending" | "descending"
|
|
592
|
-
) {
|
|
600
|
+
public setFieldSort(stateKey: keyof DraggableFieldState, index: number, sortType: ISortMode) {
|
|
593
601
|
this.useMutable(({ encodings }) => {
|
|
594
602
|
encodings[stateKey][index].sort = sortType;
|
|
595
603
|
});
|
|
596
604
|
}
|
|
597
|
-
public applyDefaultSort(sortType:
|
|
605
|
+
public applyDefaultSort(sortType: ISortMode = 'ascending') {
|
|
598
606
|
this.useMutable(({ encodings }) => {
|
|
599
607
|
const { rows, columns } = encodings;
|
|
600
608
|
const yField = rows.length > 0 ? rows[rows.length - 1] : null;
|
|
601
609
|
const xField = columns.length > 0 ? columns[columns.length - 1] : null;
|
|
602
610
|
|
|
603
|
-
if (
|
|
604
|
-
xField !== null &&
|
|
605
|
-
xField.analyticType === "dimension" &&
|
|
606
|
-
yField !== null &&
|
|
607
|
-
yField.analyticType === "measure"
|
|
608
|
-
) {
|
|
611
|
+
if (xField !== null && xField.analyticType === 'dimension' && yField !== null && yField.analyticType === 'measure') {
|
|
609
612
|
encodings.columns[columns.length - 1].sort = sortType;
|
|
610
613
|
return;
|
|
611
614
|
}
|
|
612
|
-
if (
|
|
613
|
-
xField !== null &&
|
|
614
|
-
xField.analyticType === "measure" &&
|
|
615
|
-
yField !== null &&
|
|
616
|
-
yField.analyticType === "dimension"
|
|
617
|
-
) {
|
|
615
|
+
if (xField !== null && xField.analyticType === 'measure' && yField !== null && yField.analyticType === 'dimension') {
|
|
618
616
|
encodings.rows[rows.length - 1].sort = sortType;
|
|
619
617
|
return;
|
|
620
618
|
}
|
|
621
619
|
});
|
|
622
620
|
}
|
|
623
|
-
public appendField(destinationKey: keyof DraggableFieldState, field: IViewField | undefined) {
|
|
621
|
+
public appendField(destinationKey: keyof DraggableFieldState, field: IViewField | undefined, overrideAttr?: Record<string, any>) {
|
|
624
622
|
if (MetaFieldKeys.includes(destinationKey)) return;
|
|
625
|
-
if (typeof field ===
|
|
626
|
-
if (destinationKey ===
|
|
623
|
+
if (typeof field === 'undefined') return;
|
|
624
|
+
if (destinationKey === 'filters') {
|
|
627
625
|
return;
|
|
628
626
|
}
|
|
629
627
|
|
|
630
628
|
this.useMutable(({ encodings }) => {
|
|
631
|
-
const cloneField = { ...toJS(field) };
|
|
629
|
+
const cloneField = { ...toJS(field), ...overrideAttr };
|
|
632
630
|
cloneField.dragId = uniqueId();
|
|
633
631
|
encodings[destinationKey].push(cloneField);
|
|
634
632
|
});
|
|
635
633
|
}
|
|
636
|
-
public setVizFormatConfig
|
|
637
|
-
this.visualConfig[formatKey] = value
|
|
634
|
+
public setVizFormatConfig(formatKey: keyof IVisualConfig['format'], value?: string) {
|
|
635
|
+
this.visualConfig[formatKey] = value;
|
|
636
|
+
}
|
|
637
|
+
public renderVLSubset(vlStruct: any) {
|
|
638
|
+
const tab = this.visList[this.visIndex];
|
|
639
|
+
this.clearState();
|
|
640
|
+
this.setVisualConfig('defaultAggregated', false);
|
|
641
|
+
this.setVisualConfig('stack', 'stack');
|
|
642
|
+
// this.setVisualConfig('sorted', 'none')
|
|
643
|
+
this.applyDefaultSort('none');
|
|
644
|
+
|
|
645
|
+
if (!tab) return;
|
|
646
|
+
const fields = tab.encodings.dimensions.concat(tab.encodings.measures);
|
|
647
|
+
const countField = fields.find((f) => f.fid === COUNT_FIELD_ID);
|
|
648
|
+
const renderVLFacet = (vlFacet) => {
|
|
649
|
+
if (vlFacet.facet) {
|
|
650
|
+
this.appendField('rows', fields.find((f) => f.fid === vlFacet.facet.field) || countField, { analyticType: 'dimension' });
|
|
651
|
+
}
|
|
652
|
+
if (vlFacet.row) {
|
|
653
|
+
this.appendField('rows', fields.find((f) => f.fid === vlFacet.row.field) || countField, { analyticType: 'dimension' });
|
|
654
|
+
}
|
|
655
|
+
if (vlFacet.column) {
|
|
656
|
+
this.appendField('columns', fields.find((f) => f.fid === vlFacet.column.field) || countField, { analyticType: 'dimension' });
|
|
657
|
+
}
|
|
658
|
+
};
|
|
659
|
+
const isValidAggregate = (aggName) => aggName && ['sum', 'count', 'max', 'min', 'mean', 'median', 'variance', 'stdev'].includes(aggName);
|
|
660
|
+
const renderVLSpec = (vlSpec) => {
|
|
661
|
+
if (typeof vlSpec.mark === 'string') {
|
|
662
|
+
this.setVisualConfig('geoms', [geomAdapter(vlSpec.mark)]);
|
|
663
|
+
} else {
|
|
664
|
+
this.setVisualConfig('geoms', [geomAdapter(vlSpec.mark.type)]);
|
|
665
|
+
}
|
|
666
|
+
if (vlSpec.encoding.x) {
|
|
667
|
+
const field = fields.find((f) => f.fid === vlSpec.encoding.x.field) || countField;
|
|
668
|
+
this.appendField('columns', field, { analyticType: 'dimension' });
|
|
669
|
+
if (isValidAggregate(vlSpec.encoding.x.aggregate) || field === countField) {
|
|
670
|
+
this.setVisualConfig('defaultAggregated', true);
|
|
671
|
+
this.setFieldAggregator('columns', this.draggableFieldState.columns.length - 1, vlSpec.encoding.x.aggregate);
|
|
672
|
+
}
|
|
673
|
+
if (vlSpec.encoding.x.bin) {
|
|
674
|
+
const binFid = this.createBinField('columns', this.draggableFieldState.columns.length - 1, 'bin');
|
|
675
|
+
this.replaceField('columns', this.draggableFieldState.columns.length - 1, binFid);
|
|
676
|
+
}
|
|
677
|
+
if (vlSpec.encoding.x.stack) {
|
|
678
|
+
this.setVisualConfig('stack', stackValueTransform(vlSpec.encoding.x.stack));
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
if (vlSpec.encoding.y) {
|
|
682
|
+
const field = fields.find((f) => f.fid === vlSpec.encoding.y.field) || countField;
|
|
683
|
+
this.appendField('rows', field, { analyticType: 'measure' });
|
|
684
|
+
if (isValidAggregate(vlSpec.encoding.y.aggregate) || field === countField) {
|
|
685
|
+
this.setVisualConfig('defaultAggregated', true);
|
|
686
|
+
this.setFieldAggregator('rows', this.draggableFieldState.rows.length - 1, vlSpec.encoding.y.aggregate);
|
|
687
|
+
}
|
|
688
|
+
if (vlSpec.encoding.y.bin) {
|
|
689
|
+
const binFid = this.createBinField('rows', this.draggableFieldState.rows.length - 1, 'bin');
|
|
690
|
+
this.replaceField('rows', this.draggableFieldState.rows.length - 1, binFid);
|
|
691
|
+
}
|
|
692
|
+
if (vlSpec.encoding.y.stack) {
|
|
693
|
+
this.setVisualConfig('stack', stackValueTransform(vlSpec.encoding.y.stack));
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
(['color', 'opacity', 'shape', 'size', 'details', 'theta', 'text', 'radius'] as (keyof DraggableFieldState)[]).forEach((ch) => {
|
|
698
|
+
if (vlSpec.encoding[ch]) {
|
|
699
|
+
const field = fields.find((f) => f.fid === vlSpec.encoding[ch].field) || countField;
|
|
700
|
+
this.appendField(
|
|
701
|
+
ch,
|
|
702
|
+
field,
|
|
703
|
+
field !== countField && ['color', 'opacity', 'size', 'radius'].includes(ch)
|
|
704
|
+
? { analyticType: 'dimension' }
|
|
705
|
+
: field === countField || ['theta'].includes(ch)
|
|
706
|
+
? { analyticType: 'measure' }
|
|
707
|
+
: {}
|
|
708
|
+
);
|
|
709
|
+
const aggregate = isValidAggregate(vlSpec.encoding[ch].aggregate);
|
|
710
|
+
if ((['theta', 'radius'].includes(ch) && aggregate) || field === countField) {
|
|
711
|
+
this.setVisualConfig('defaultAggregated', true);
|
|
712
|
+
if (aggregate) {
|
|
713
|
+
this.setFieldAggregator(ch, this.draggableFieldState[ch].length - 1, vlSpec.encoding[ch].aggregate);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
});
|
|
718
|
+
['x', 'y', 'facet'].forEach((ch) => {
|
|
719
|
+
if (vlSpec.encoding[ch] && vlSpec.encoding[ch].sort) {
|
|
720
|
+
this.applyDefaultSort(sortValueTransform(vlSpec.encoding[ch].sort));
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
if (vlSpec.encoding.order && vlSpec.encoding.order.sort) {
|
|
724
|
+
this.applyDefaultSort(sortValueTransform(vlSpec.encoding.order.sort));
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
if (vlStruct.encoding && vlStruct.mark) {
|
|
728
|
+
renderVLFacet(vlStruct.encoding);
|
|
729
|
+
renderVLSpec(vlStruct);
|
|
730
|
+
} else if (vlStruct.spec) {
|
|
731
|
+
if (vlStruct.facet) {
|
|
732
|
+
renderVLFacet(vlStruct.facet);
|
|
733
|
+
}
|
|
734
|
+
renderVLSpec(vlStruct.spec);
|
|
735
|
+
}
|
|
638
736
|
}
|
|
639
737
|
public renderSpec(spec: Specification) {
|
|
640
738
|
const tab = this.visList[this.visIndex];
|
|
@@ -647,7 +745,7 @@ export class VizSpecStore {
|
|
|
647
745
|
this.setVisualConfig('defaultAggregated', Boolean(spec.aggregate));
|
|
648
746
|
if ((spec.geomType?.length ?? 0) > 0) {
|
|
649
747
|
this.setVisualConfig(
|
|
650
|
-
|
|
748
|
+
'geoms',
|
|
651
749
|
spec.geomType!.map((g) => geomAdapter(g))
|
|
652
750
|
);
|
|
653
751
|
}
|
|
@@ -655,7 +753,7 @@ export class VizSpecStore {
|
|
|
655
753
|
const facets = (spec.facets || []).concat(spec.highFacets || []);
|
|
656
754
|
for (let facet of facets) {
|
|
657
755
|
this.appendField(
|
|
658
|
-
|
|
756
|
+
'rows',
|
|
659
757
|
fields.find((f) => f.fid === facet)
|
|
660
758
|
);
|
|
661
759
|
}
|
|
@@ -664,30 +762,30 @@ export class VizSpecStore {
|
|
|
664
762
|
const [cols, rows] = spec.position;
|
|
665
763
|
if (cols)
|
|
666
764
|
this.appendField(
|
|
667
|
-
|
|
765
|
+
'columns',
|
|
668
766
|
fields.find((f) => f.fid === cols)
|
|
669
767
|
);
|
|
670
768
|
if (rows)
|
|
671
769
|
this.appendField(
|
|
672
|
-
|
|
770
|
+
'rows',
|
|
673
771
|
fields.find((f) => f.fid === rows)
|
|
674
772
|
);
|
|
675
773
|
}
|
|
676
774
|
if ((spec.color?.length ?? 0) > 0) {
|
|
677
775
|
this.appendField(
|
|
678
|
-
|
|
776
|
+
'color',
|
|
679
777
|
fields.find((f) => f.fid === spec.color![0])
|
|
680
778
|
);
|
|
681
779
|
}
|
|
682
780
|
if ((spec.size?.length ?? 0) > 0) {
|
|
683
781
|
this.appendField(
|
|
684
|
-
|
|
782
|
+
'size',
|
|
685
783
|
fields.find((f) => f.fid === spec.size![0])
|
|
686
784
|
);
|
|
687
785
|
}
|
|
688
786
|
if ((spec.opacity?.length ?? 0) > 0) {
|
|
689
787
|
this.appendField(
|
|
690
|
-
|
|
788
|
+
'opacity',
|
|
691
789
|
fields.find((f) => f.fid === spec.opacity![0])
|
|
692
790
|
);
|
|
693
791
|
}
|
|
@@ -710,8 +808,8 @@ export class VizSpecStore {
|
|
|
710
808
|
const pureVisList = dumpsGWPureSpec(this.visList);
|
|
711
809
|
return this.visSpecEncoder(pureVisList);
|
|
712
810
|
}
|
|
713
|
-
public importStoInfo
|
|
714
|
-
this.visList = parseGWPureSpec(
|
|
811
|
+
public importStoInfo(stoInfo: IStoInfo) {
|
|
812
|
+
this.visList = parseGWPureSpec(visSpecDecoder(forwardVisualConfigs(stoInfo.specList)));
|
|
715
813
|
this.visIndex = 0;
|
|
716
814
|
this.commonStore.datasets = stoInfo.datasets;
|
|
717
815
|
this.commonStore.dataSources = stoInfo.dataSources;
|
|
@@ -725,53 +823,35 @@ export class VizSpecStore {
|
|
|
725
823
|
private visSpecEncoder(visList: IVisSpec[]): IVisSpecForExport[] {
|
|
726
824
|
const updatedVisList = visList.map((visSpec) => {
|
|
727
825
|
const updatedFilters = visSpec.encodings.filters.map((filter) => {
|
|
728
|
-
if (filter.rule?.type ===
|
|
729
|
-
const rule =
|
|
730
|
-
...filter.rule,
|
|
731
|
-
value: Array.from(filter.rule.value)
|
|
732
|
-
}
|
|
826
|
+
if (filter.rule?.type === 'one of') {
|
|
827
|
+
const rule = {
|
|
828
|
+
...filter.rule,
|
|
829
|
+
value: Array.from(filter.rule.value),
|
|
830
|
+
};
|
|
733
831
|
return {
|
|
734
|
-
...filter,
|
|
735
|
-
rule
|
|
736
|
-
}
|
|
737
|
-
}
|
|
832
|
+
...filter,
|
|
833
|
+
rule,
|
|
834
|
+
};
|
|
835
|
+
}
|
|
738
836
|
return filter as IFilterFieldForExport;
|
|
739
837
|
});
|
|
740
838
|
return {
|
|
741
839
|
...visSpec,
|
|
742
840
|
encodings: {
|
|
743
841
|
...visSpec.encodings,
|
|
744
|
-
filters: updatedFilters
|
|
745
|
-
}
|
|
746
|
-
}
|
|
842
|
+
filters: updatedFilters,
|
|
843
|
+
},
|
|
844
|
+
};
|
|
747
845
|
});
|
|
748
846
|
return updatedVisList;
|
|
749
847
|
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
rule: {
|
|
757
|
-
...filter.rule,
|
|
758
|
-
value: new Set(filter.rule.value)
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
return filter;
|
|
763
|
-
})
|
|
764
|
-
return {
|
|
765
|
-
...visSpec,
|
|
766
|
-
encodings: {
|
|
767
|
-
...visSpec.encodings,
|
|
768
|
-
filters: updatedFilters
|
|
769
|
-
}
|
|
770
|
-
} as IVisSpec;
|
|
771
|
-
});
|
|
772
|
-
return updatedVisList;
|
|
848
|
+
public get limit() {
|
|
849
|
+
return this.visualConfig.limit;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
public setLimit(value: number) {
|
|
853
|
+
this.setVisualConfig('limit', value);
|
|
773
854
|
}
|
|
774
|
-
public limit = -1;
|
|
775
855
|
|
|
776
856
|
public get sort() {
|
|
777
857
|
const { rows, columns } = this.draggableFieldState;
|
|
@@ -783,4 +863,20 @@ export class VizSpecStore {
|
|
|
783
863
|
}
|
|
784
864
|
return 'none';
|
|
785
865
|
}
|
|
866
|
+
|
|
867
|
+
public getWorkflow() {
|
|
868
|
+
return toWorkflow(
|
|
869
|
+
this.viewFilters,
|
|
870
|
+
this.allFields,
|
|
871
|
+
this.viewDimensions,
|
|
872
|
+
this.viewMeasures,
|
|
873
|
+
this.visualConfig.defaultAggregated,
|
|
874
|
+
this.sort,
|
|
875
|
+
this.limit > 0 ? this.limit : undefined
|
|
876
|
+
);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
public setComputationFunction(f: IComputationFunction) {
|
|
880
|
+
this.computationFuction = f;
|
|
881
|
+
}
|
|
786
882
|
}
|