@kanaries/graphic-walker 0.3.16 → 0.4.0
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 +8 -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 +22226 -21650
- package/dist/graphic-walker.es.js.map +1 -1
- package/dist/graphic-walker.umd.js +137 -137
- package/dist/graphic-walker.umd.js.map +1 -1
- 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 +92 -0
- package/src/components/askViz/schemaTransform.ts +38 -0
- package/src/components/dataTable/index.tsx +51 -11
- package/src/components/pivotTable/index.tsx +0 -1
- package/src/components/pivotTable/store.tsx +0 -16
- 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 +153 -0
- package/src/config.ts +15 -2
- package/src/dataSource/datasetConfig/index.tsx +38 -6
- package/src/dataSource/table.tsx +11 -2
- package/src/fields/filterField/filterEditDialog.tsx +11 -10
- package/src/fields/filterField/tabs.tsx +178 -77
- 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 +100 -66
- 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 +287 -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 -3
- 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, 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,33 @@ 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 === 'limit':
|
|
399
|
+
case configKey === 'stack': {
|
|
379
400
|
return (config[configKey] = value);
|
|
380
401
|
}
|
|
381
|
-
case configKey === 'format' && typeof value ===
|
|
382
|
-
return config[configKey] = value
|
|
402
|
+
case configKey === 'format' && typeof value === 'object': {
|
|
403
|
+
return (config[configKey] = value);
|
|
383
404
|
}
|
|
384
405
|
|
|
385
406
|
default: {
|
|
386
|
-
console.error(
|
|
407
|
+
console.error('[unknown key] ' + configKey + ' You should registered visualConfig at setVisualConfig');
|
|
387
408
|
}
|
|
388
409
|
}
|
|
389
410
|
});
|
|
390
411
|
}
|
|
391
|
-
public transformCoord(coord:
|
|
392
|
-
if (coord ===
|
|
412
|
+
public transformCoord(coord: 'cartesian' | 'polar') {
|
|
413
|
+
if (coord === 'polar') {
|
|
393
414
|
}
|
|
394
415
|
}
|
|
395
|
-
public setChartLayout(props: { mode: IVisualConfig[
|
|
416
|
+
public setChartLayout(props: { mode: IVisualConfig['size']['mode']; width?: number; height?: number }) {
|
|
396
417
|
this.useMutable(({ config }) => {
|
|
397
418
|
const { mode = config.size.mode, width = config.size.width, height = config.size.height } = props;
|
|
398
419
|
|
|
@@ -411,15 +432,10 @@ export class VizSpecStore {
|
|
|
411
432
|
fields.splice(destinationIndex, 0, field);
|
|
412
433
|
});
|
|
413
434
|
}
|
|
414
|
-
public moveField(
|
|
415
|
-
sourceKey
|
|
416
|
-
sourceIndex: number,
|
|
417
|
-
destinationKey: keyof DraggableFieldState,
|
|
418
|
-
destinationIndex: number
|
|
419
|
-
) {
|
|
420
|
-
if (sourceKey === "filters") {
|
|
435
|
+
public moveField(sourceKey: keyof DraggableFieldState, sourceIndex: number, destinationKey: keyof DraggableFieldState, destinationIndex: number) {
|
|
436
|
+
if (sourceKey === 'filters') {
|
|
421
437
|
return this.removeField(sourceKey, sourceIndex);
|
|
422
|
-
} else if (destinationKey ===
|
|
438
|
+
} else if (destinationKey === 'filters') {
|
|
423
439
|
return this.appendFilter(destinationIndex, this.draggableFieldState[sourceKey][sourceIndex]);
|
|
424
440
|
}
|
|
425
441
|
|
|
@@ -439,7 +455,7 @@ export class VizSpecStore {
|
|
|
439
455
|
if (MetaFieldKeys.includes(destinationKey)) {
|
|
440
456
|
if (!MetaFieldKeys.includes(sourceKey)) return;
|
|
441
457
|
encodings[sourceKey].splice(sourceIndex, 1);
|
|
442
|
-
movingField.analyticType = destinationKey ===
|
|
458
|
+
movingField.analyticType = destinationKey === 'dimensions' ? 'dimension' : 'measure';
|
|
443
459
|
}
|
|
444
460
|
const limitSize = getChannelSizeLimit(destinationKey);
|
|
445
461
|
const fixedDestinationIndex = Math.min(destinationIndex, limitSize - 1);
|
|
@@ -457,10 +473,7 @@ export class VizSpecStore {
|
|
|
457
473
|
}
|
|
458
474
|
public replaceField(sourceKey: keyof DraggableFieldState, sourceIndex: number, fid: string) {
|
|
459
475
|
if (MetaFieldKeys.includes(sourceKey)) return;
|
|
460
|
-
const enteringField = [
|
|
461
|
-
...this.draggableFieldState.dimensions,
|
|
462
|
-
...this.draggableFieldState.measures
|
|
463
|
-
].find(which => which.fid === fid);
|
|
476
|
+
const enteringField = [...this.draggableFieldState.dimensions, ...this.draggableFieldState.measures].find((which) => which.fid === fid);
|
|
464
477
|
if (!enteringField) {
|
|
465
478
|
return;
|
|
466
479
|
}
|
|
@@ -499,16 +512,23 @@ export class VizSpecStore {
|
|
|
499
512
|
encodings.rows = fieldsInCup as typeof encodings.rows; // assume this as writable
|
|
500
513
|
});
|
|
501
514
|
}
|
|
502
|
-
public createBinField(stateKey: keyof DraggableFieldState, index: number, binType: 'bin' | 'binCount') {
|
|
515
|
+
public createBinField(stateKey: keyof DraggableFieldState, index: number, binType: 'bin' | 'binCount'): string {
|
|
516
|
+
const newVarKey = uniqueId();
|
|
517
|
+
const state = this.draggableFieldState;
|
|
518
|
+
const existedRelatedBinField = state.dimensions.find(
|
|
519
|
+
(f) => f.computed && f.expression && f.expression.op === binType && f.expression.params[0].value === state[stateKey][index].fid
|
|
520
|
+
);
|
|
521
|
+
if (existedRelatedBinField) {
|
|
522
|
+
return existedRelatedBinField.fid;
|
|
523
|
+
}
|
|
503
524
|
this.useMutable(({ encodings }) => {
|
|
504
525
|
const originField = encodings[stateKey][index];
|
|
505
|
-
const newVarKey = uniqueId();
|
|
506
526
|
const binField: IViewField = {
|
|
507
527
|
fid: newVarKey,
|
|
508
528
|
dragId: newVarKey,
|
|
509
529
|
name: `${binType}(${originField.name})`,
|
|
510
|
-
semanticType:
|
|
511
|
-
analyticType:
|
|
530
|
+
semanticType: 'ordinal',
|
|
531
|
+
analyticType: 'dimension',
|
|
512
532
|
computed: true,
|
|
513
533
|
expression: {
|
|
514
534
|
op: binType,
|
|
@@ -516,16 +536,17 @@ export class VizSpecStore {
|
|
|
516
536
|
params: [
|
|
517
537
|
{
|
|
518
538
|
type: 'field',
|
|
519
|
-
value: originField.fid
|
|
520
|
-
}
|
|
521
|
-
]
|
|
522
|
-
}
|
|
539
|
+
value: originField.fid,
|
|
540
|
+
},
|
|
541
|
+
],
|
|
542
|
+
},
|
|
523
543
|
};
|
|
524
544
|
encodings.dimensions.push(binField);
|
|
525
545
|
});
|
|
546
|
+
return newVarKey;
|
|
526
547
|
}
|
|
527
548
|
public createLogField(stateKey: keyof DraggableFieldState, index: number, scaleType: 'log10' | 'log2') {
|
|
528
|
-
if (stateKey ===
|
|
549
|
+
if (stateKey === 'filters') {
|
|
529
550
|
return;
|
|
530
551
|
}
|
|
531
552
|
|
|
@@ -536,7 +557,7 @@ export class VizSpecStore {
|
|
|
536
557
|
fid: newVarKey,
|
|
537
558
|
dragId: newVarKey,
|
|
538
559
|
name: `${scaleType}(${originField.name})`,
|
|
539
|
-
semanticType:
|
|
560
|
+
semanticType: 'quantitative',
|
|
540
561
|
analyticType: originField.analyticType,
|
|
541
562
|
aggName: 'sum',
|
|
542
563
|
computed: true,
|
|
@@ -546,10 +567,10 @@ export class VizSpecStore {
|
|
|
546
567
|
params: [
|
|
547
568
|
{
|
|
548
569
|
type: 'field',
|
|
549
|
-
value: originField.fid
|
|
550
|
-
}
|
|
551
|
-
]
|
|
552
|
-
}
|
|
570
|
+
value: originField.fid,
|
|
571
|
+
},
|
|
572
|
+
],
|
|
573
|
+
},
|
|
553
574
|
};
|
|
554
575
|
encodings[stateKey].push(logField);
|
|
555
576
|
});
|
|
@@ -567,74 +588,150 @@ export class VizSpecStore {
|
|
|
567
588
|
const { rows, columns } = this.draggableFieldState;
|
|
568
589
|
const yField = rows.length > 0 ? rows[rows.length - 1] : null;
|
|
569
590
|
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
|
-
) {
|
|
591
|
+
if (xField !== null && xField.analyticType === 'dimension' && yField !== null && yField.analyticType === 'measure') {
|
|
576
592
|
return true;
|
|
577
593
|
}
|
|
578
|
-
if (
|
|
579
|
-
xField !== null &&
|
|
580
|
-
xField.analyticType === "measure" &&
|
|
581
|
-
yField !== null &&
|
|
582
|
-
yField.analyticType === "dimension"
|
|
583
|
-
) {
|
|
594
|
+
if (xField !== null && xField.analyticType === 'measure' && yField !== null && yField.analyticType === 'dimension') {
|
|
584
595
|
return true;
|
|
585
596
|
}
|
|
586
597
|
return false;
|
|
587
598
|
}
|
|
588
|
-
public setFieldSort(
|
|
589
|
-
stateKey: keyof DraggableFieldState,
|
|
590
|
-
index: number,
|
|
591
|
-
sortType: "none" | "ascending" | "descending"
|
|
592
|
-
) {
|
|
599
|
+
public setFieldSort(stateKey: keyof DraggableFieldState, index: number, sortType: ISortMode) {
|
|
593
600
|
this.useMutable(({ encodings }) => {
|
|
594
601
|
encodings[stateKey][index].sort = sortType;
|
|
595
602
|
});
|
|
596
603
|
}
|
|
597
|
-
public applyDefaultSort(sortType:
|
|
604
|
+
public applyDefaultSort(sortType: ISortMode = 'ascending') {
|
|
598
605
|
this.useMutable(({ encodings }) => {
|
|
599
606
|
const { rows, columns } = encodings;
|
|
600
607
|
const yField = rows.length > 0 ? rows[rows.length - 1] : null;
|
|
601
608
|
const xField = columns.length > 0 ? columns[columns.length - 1] : null;
|
|
602
609
|
|
|
603
|
-
if (
|
|
604
|
-
xField !== null &&
|
|
605
|
-
xField.analyticType === "dimension" &&
|
|
606
|
-
yField !== null &&
|
|
607
|
-
yField.analyticType === "measure"
|
|
608
|
-
) {
|
|
610
|
+
if (xField !== null && xField.analyticType === 'dimension' && yField !== null && yField.analyticType === 'measure') {
|
|
609
611
|
encodings.columns[columns.length - 1].sort = sortType;
|
|
610
612
|
return;
|
|
611
613
|
}
|
|
612
|
-
if (
|
|
613
|
-
xField !== null &&
|
|
614
|
-
xField.analyticType === "measure" &&
|
|
615
|
-
yField !== null &&
|
|
616
|
-
yField.analyticType === "dimension"
|
|
617
|
-
) {
|
|
614
|
+
if (xField !== null && xField.analyticType === 'measure' && yField !== null && yField.analyticType === 'dimension') {
|
|
618
615
|
encodings.rows[rows.length - 1].sort = sortType;
|
|
619
616
|
return;
|
|
620
617
|
}
|
|
621
618
|
});
|
|
622
619
|
}
|
|
623
|
-
public appendField(destinationKey: keyof DraggableFieldState, field: IViewField | undefined) {
|
|
620
|
+
public appendField(destinationKey: keyof DraggableFieldState, field: IViewField | undefined, overrideAttr?: Record<string, any>) {
|
|
624
621
|
if (MetaFieldKeys.includes(destinationKey)) return;
|
|
625
|
-
if (typeof field ===
|
|
626
|
-
if (destinationKey ===
|
|
622
|
+
if (typeof field === 'undefined') return;
|
|
623
|
+
if (destinationKey === 'filters') {
|
|
627
624
|
return;
|
|
628
625
|
}
|
|
629
626
|
|
|
630
627
|
this.useMutable(({ encodings }) => {
|
|
631
|
-
const cloneField = { ...toJS(field) };
|
|
628
|
+
const cloneField = { ...toJS(field), ...overrideAttr };
|
|
632
629
|
cloneField.dragId = uniqueId();
|
|
633
630
|
encodings[destinationKey].push(cloneField);
|
|
634
631
|
});
|
|
635
632
|
}
|
|
636
|
-
public setVizFormatConfig
|
|
637
|
-
this.visualConfig[formatKey] = value
|
|
633
|
+
public setVizFormatConfig(formatKey: keyof IVisualConfig['format'], value?: string) {
|
|
634
|
+
this.visualConfig[formatKey] = value;
|
|
635
|
+
}
|
|
636
|
+
public renderVLSubset(vlStruct: any) {
|
|
637
|
+
const tab = this.visList[this.visIndex];
|
|
638
|
+
this.clearState();
|
|
639
|
+
this.setVisualConfig('defaultAggregated', false);
|
|
640
|
+
this.setVisualConfig('stack', 'stack');
|
|
641
|
+
// this.setVisualConfig('sorted', 'none')
|
|
642
|
+
this.applyDefaultSort('none');
|
|
643
|
+
|
|
644
|
+
if (!tab) return;
|
|
645
|
+
const fields = tab.encodings.dimensions.concat(tab.encodings.measures);
|
|
646
|
+
const countField = fields.find((f) => f.fid === COUNT_FIELD_ID);
|
|
647
|
+
const renderVLFacet = (vlFacet) => {
|
|
648
|
+
if (vlFacet.facet) {
|
|
649
|
+
this.appendField('rows', fields.find((f) => f.fid === vlFacet.facet.field) || countField, { analyticType: 'dimension' });
|
|
650
|
+
}
|
|
651
|
+
if (vlFacet.row) {
|
|
652
|
+
this.appendField('rows', fields.find((f) => f.fid === vlFacet.row.field) || countField, { analyticType: 'dimension' });
|
|
653
|
+
}
|
|
654
|
+
if (vlFacet.column) {
|
|
655
|
+
this.appendField('columns', fields.find((f) => f.fid === vlFacet.column.field) || countField, { analyticType: 'dimension' });
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
const isValidAggregate = (aggName) => aggName && ['sum', 'count', 'max', 'min', 'mean', 'median', 'variance', 'stdev'].includes(aggName);
|
|
659
|
+
const renderVLSpec = (vlSpec) => {
|
|
660
|
+
if (typeof vlSpec.mark === 'string') {
|
|
661
|
+
this.setVisualConfig('geoms', [geomAdapter(vlSpec.mark)]);
|
|
662
|
+
} else {
|
|
663
|
+
this.setVisualConfig('geoms', [geomAdapter(vlSpec.mark.type)]);
|
|
664
|
+
}
|
|
665
|
+
if (vlSpec.encoding.x) {
|
|
666
|
+
const field = fields.find((f) => f.fid === vlSpec.encoding.x.field) || countField;
|
|
667
|
+
this.appendField('columns', field, { analyticType: 'dimension' });
|
|
668
|
+
if (isValidAggregate(vlSpec.encoding.x.aggregate) || field === countField) {
|
|
669
|
+
this.setVisualConfig('defaultAggregated', true);
|
|
670
|
+
this.setFieldAggregator('columns', this.draggableFieldState.columns.length - 1, vlSpec.encoding.x.aggregate);
|
|
671
|
+
}
|
|
672
|
+
if (vlSpec.encoding.x.bin) {
|
|
673
|
+
const binFid = this.createBinField('columns', this.draggableFieldState.columns.length - 1, 'bin');
|
|
674
|
+
this.replaceField('columns', this.draggableFieldState.columns.length - 1, binFid);
|
|
675
|
+
}
|
|
676
|
+
if (vlSpec.encoding.x.stack) {
|
|
677
|
+
this.setVisualConfig('stack', stackValueTransform(vlSpec.encoding.x.stack));
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
if (vlSpec.encoding.y) {
|
|
681
|
+
const field = fields.find((f) => f.fid === vlSpec.encoding.y.field) || countField;
|
|
682
|
+
this.appendField('rows', field, { analyticType: 'measure' });
|
|
683
|
+
if (isValidAggregate(vlSpec.encoding.y.aggregate) || field === countField) {
|
|
684
|
+
this.setVisualConfig('defaultAggregated', true);
|
|
685
|
+
this.setFieldAggregator('rows', this.draggableFieldState.rows.length - 1, vlSpec.encoding.y.aggregate);
|
|
686
|
+
}
|
|
687
|
+
if (vlSpec.encoding.y.bin) {
|
|
688
|
+
const binFid = this.createBinField('rows', this.draggableFieldState.rows.length - 1, 'bin');
|
|
689
|
+
this.replaceField('rows', this.draggableFieldState.rows.length - 1, binFid);
|
|
690
|
+
}
|
|
691
|
+
if (vlSpec.encoding.y.stack) {
|
|
692
|
+
this.setVisualConfig('stack', stackValueTransform(vlSpec.encoding.y.stack));
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
(['color', 'opacity', 'shape', 'size', 'details', 'theta', 'text', 'radius'] as (keyof DraggableFieldState)[]).forEach((ch) => {
|
|
697
|
+
if (vlSpec.encoding[ch]) {
|
|
698
|
+
const field = fields.find((f) => f.fid === vlSpec.encoding[ch].field) || countField;
|
|
699
|
+
this.appendField(
|
|
700
|
+
ch,
|
|
701
|
+
field,
|
|
702
|
+
field !== countField && ['color', 'opacity', 'size', 'radius'].includes(ch)
|
|
703
|
+
? { analyticType: 'dimension' }
|
|
704
|
+
: field === countField || ['theta'].includes(ch)
|
|
705
|
+
? { analyticType: 'measure' }
|
|
706
|
+
: {}
|
|
707
|
+
);
|
|
708
|
+
const aggregate = isValidAggregate(vlSpec.encoding[ch].aggregate);
|
|
709
|
+
if ((['theta', 'radius'].includes(ch) && aggregate) || field === countField) {
|
|
710
|
+
this.setVisualConfig('defaultAggregated', true);
|
|
711
|
+
if (aggregate) {
|
|
712
|
+
this.setFieldAggregator(ch, this.draggableFieldState[ch].length - 1, vlSpec.encoding[ch].aggregate);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
});
|
|
717
|
+
['x', 'y', 'facet'].forEach((ch) => {
|
|
718
|
+
if (vlSpec.encoding[ch] && vlSpec.encoding[ch].sort) {
|
|
719
|
+
this.applyDefaultSort(sortValueTransform(vlSpec.encoding[ch].sort));
|
|
720
|
+
}
|
|
721
|
+
});
|
|
722
|
+
if (vlSpec.encoding.order && vlSpec.encoding.order.sort) {
|
|
723
|
+
this.applyDefaultSort(sortValueTransform(vlSpec.encoding.order.sort));
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
if (vlStruct.encoding && vlStruct.mark) {
|
|
727
|
+
renderVLFacet(vlStruct.encoding);
|
|
728
|
+
renderVLSpec(vlStruct);
|
|
729
|
+
} else if (vlStruct.spec) {
|
|
730
|
+
if (vlStruct.facet) {
|
|
731
|
+
renderVLFacet(vlStruct.facet);
|
|
732
|
+
}
|
|
733
|
+
renderVLSpec(vlStruct.spec);
|
|
734
|
+
}
|
|
638
735
|
}
|
|
639
736
|
public renderSpec(spec: Specification) {
|
|
640
737
|
const tab = this.visList[this.visIndex];
|
|
@@ -647,7 +744,7 @@ export class VizSpecStore {
|
|
|
647
744
|
this.setVisualConfig('defaultAggregated', Boolean(spec.aggregate));
|
|
648
745
|
if ((spec.geomType?.length ?? 0) > 0) {
|
|
649
746
|
this.setVisualConfig(
|
|
650
|
-
|
|
747
|
+
'geoms',
|
|
651
748
|
spec.geomType!.map((g) => geomAdapter(g))
|
|
652
749
|
);
|
|
653
750
|
}
|
|
@@ -655,7 +752,7 @@ export class VizSpecStore {
|
|
|
655
752
|
const facets = (spec.facets || []).concat(spec.highFacets || []);
|
|
656
753
|
for (let facet of facets) {
|
|
657
754
|
this.appendField(
|
|
658
|
-
|
|
755
|
+
'rows',
|
|
659
756
|
fields.find((f) => f.fid === facet)
|
|
660
757
|
);
|
|
661
758
|
}
|
|
@@ -664,30 +761,30 @@ export class VizSpecStore {
|
|
|
664
761
|
const [cols, rows] = spec.position;
|
|
665
762
|
if (cols)
|
|
666
763
|
this.appendField(
|
|
667
|
-
|
|
764
|
+
'columns',
|
|
668
765
|
fields.find((f) => f.fid === cols)
|
|
669
766
|
);
|
|
670
767
|
if (rows)
|
|
671
768
|
this.appendField(
|
|
672
|
-
|
|
769
|
+
'rows',
|
|
673
770
|
fields.find((f) => f.fid === rows)
|
|
674
771
|
);
|
|
675
772
|
}
|
|
676
773
|
if ((spec.color?.length ?? 0) > 0) {
|
|
677
774
|
this.appendField(
|
|
678
|
-
|
|
775
|
+
'color',
|
|
679
776
|
fields.find((f) => f.fid === spec.color![0])
|
|
680
777
|
);
|
|
681
778
|
}
|
|
682
779
|
if ((spec.size?.length ?? 0) > 0) {
|
|
683
780
|
this.appendField(
|
|
684
|
-
|
|
781
|
+
'size',
|
|
685
782
|
fields.find((f) => f.fid === spec.size![0])
|
|
686
783
|
);
|
|
687
784
|
}
|
|
688
785
|
if ((spec.opacity?.length ?? 0) > 0) {
|
|
689
786
|
this.appendField(
|
|
690
|
-
|
|
787
|
+
'opacity',
|
|
691
788
|
fields.find((f) => f.fid === spec.opacity![0])
|
|
692
789
|
);
|
|
693
790
|
}
|
|
@@ -710,8 +807,8 @@ export class VizSpecStore {
|
|
|
710
807
|
const pureVisList = dumpsGWPureSpec(this.visList);
|
|
711
808
|
return this.visSpecEncoder(pureVisList);
|
|
712
809
|
}
|
|
713
|
-
public importStoInfo
|
|
714
|
-
this.visList = parseGWPureSpec(
|
|
810
|
+
public importStoInfo(stoInfo: IStoInfo) {
|
|
811
|
+
this.visList = parseGWPureSpec(visSpecDecoder(forwardVisualConfigs(stoInfo.specList)));
|
|
715
812
|
this.visIndex = 0;
|
|
716
813
|
this.commonStore.datasets = stoInfo.datasets;
|
|
717
814
|
this.commonStore.dataSources = stoInfo.dataSources;
|
|
@@ -725,53 +822,35 @@ export class VizSpecStore {
|
|
|
725
822
|
private visSpecEncoder(visList: IVisSpec[]): IVisSpecForExport[] {
|
|
726
823
|
const updatedVisList = visList.map((visSpec) => {
|
|
727
824
|
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
|
-
}
|
|
825
|
+
if (filter.rule?.type === 'one of') {
|
|
826
|
+
const rule = {
|
|
827
|
+
...filter.rule,
|
|
828
|
+
value: Array.from(filter.rule.value),
|
|
829
|
+
};
|
|
733
830
|
return {
|
|
734
|
-
...filter,
|
|
735
|
-
rule
|
|
736
|
-
}
|
|
737
|
-
}
|
|
831
|
+
...filter,
|
|
832
|
+
rule,
|
|
833
|
+
};
|
|
834
|
+
}
|
|
738
835
|
return filter as IFilterFieldForExport;
|
|
739
836
|
});
|
|
740
837
|
return {
|
|
741
838
|
...visSpec,
|
|
742
839
|
encodings: {
|
|
743
840
|
...visSpec.encodings,
|
|
744
|
-
filters: updatedFilters
|
|
745
|
-
}
|
|
746
|
-
}
|
|
841
|
+
filters: updatedFilters,
|
|
842
|
+
},
|
|
843
|
+
};
|
|
747
844
|
});
|
|
748
845
|
return updatedVisList;
|
|
749
846
|
}
|
|
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;
|
|
847
|
+
public get limit() {
|
|
848
|
+
return this.visualConfig.limit;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
public setLimit(value: number) {
|
|
852
|
+
this.setVisualConfig('limit', value);
|
|
773
853
|
}
|
|
774
|
-
public limit = -1;
|
|
775
854
|
|
|
776
855
|
public get sort() {
|
|
777
856
|
const { rows, columns } = this.draggableFieldState;
|
|
@@ -783,4 +862,20 @@ export class VizSpecStore {
|
|
|
783
862
|
}
|
|
784
863
|
return 'none';
|
|
785
864
|
}
|
|
865
|
+
|
|
866
|
+
public getWorkflow() {
|
|
867
|
+
return toWorkflow(
|
|
868
|
+
this.viewFilters,
|
|
869
|
+
this.allFields,
|
|
870
|
+
this.viewDimensions,
|
|
871
|
+
this.viewMeasures,
|
|
872
|
+
this.visualConfig.defaultAggregated,
|
|
873
|
+
this.sort,
|
|
874
|
+
this.limit > 0 ? this.limit : undefined
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
public setComputationFunction(f: IComputationFunction) {
|
|
879
|
+
this.computationFuction = f;
|
|
880
|
+
}
|
|
786
881
|
}
|