@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.
Files changed (87) hide show
  1. package/dist/App.d.ts +9 -2
  2. package/dist/assets/filter.worker-f09fcd6f.js.map +1 -1
  3. package/dist/assets/sort.worker-f77540ac.js.map +1 -0
  4. package/dist/assets/transform.worker-bae8e910.js.map +1 -0
  5. package/dist/assets/{viewQuery.worker-03404216.js.map → viewQuery.worker-bdb6477c.js.map} +1 -1
  6. package/dist/components/askViz/index.d.ts +6 -0
  7. package/dist/components/askViz/schemaTransform.d.ts +2 -0
  8. package/dist/components/dataTable/index.d.ts +9 -5
  9. package/dist/components/pivotTable/store.d.ts +0 -2
  10. package/dist/components/spinner.d.ts +2 -0
  11. package/dist/computation/clientComputation.d.ts +3 -0
  12. package/dist/computation/serverComputation.d.ts +8 -0
  13. package/dist/config.d.ts +3 -1
  14. package/dist/fields/filterField/tabs.d.ts +2 -1
  15. package/dist/graphic-walker.es.js +22268 -21682
  16. package/dist/graphic-walker.es.js.map +1 -1
  17. package/dist/graphic-walker.umd.js +139 -139
  18. package/dist/graphic-walker.umd.js.map +1 -1
  19. package/dist/hooks/index.d.ts +1 -0
  20. package/dist/index.d.ts +2 -0
  21. package/dist/interfaces.d.ts +93 -4
  22. package/dist/lib/execExp.d.ts +4 -4
  23. package/dist/lib/interfaces.d.ts +1 -0
  24. package/dist/lib/viewQuery.d.ts +2 -2
  25. package/dist/renderer/hooks.d.ts +8 -4
  26. package/dist/renderer/index.d.ts +2 -1
  27. package/dist/renderer/pureRenderer.d.ts +17 -1
  28. package/dist/renderer/specRenderer.d.ts +1 -0
  29. package/dist/services.d.ts +8 -5
  30. package/dist/store/commonStore.d.ts +2 -2
  31. package/dist/store/visualSpecStore.d.ts +58 -42
  32. package/dist/utils/save.d.ts +10 -2
  33. package/dist/utils/workflow.d.ts +3 -0
  34. package/dist/vis/react-vega.d.ts +3 -1
  35. package/dist/workers/sort.d.ts +2 -2
  36. package/dist/workers/transform.d.ts +5 -2
  37. package/package.json +2 -2
  38. package/src/App.tsx +46 -7
  39. package/src/components/askViz/index.tsx +93 -0
  40. package/src/components/askViz/schemaTransform.ts +38 -0
  41. package/src/components/dataTable/index.tsx +53 -11
  42. package/src/components/limitSetting.tsx +8 -6
  43. package/src/components/pivotTable/index.tsx +0 -1
  44. package/src/components/pivotTable/store.tsx +0 -16
  45. package/src/components/sizeSetting.tsx +9 -7
  46. package/src/components/spinner.tsx +14 -0
  47. package/src/components/toggle.tsx +2 -2
  48. package/src/components/visualConfig/index.tsx +78 -8
  49. package/src/computation/clientComputation.ts +55 -0
  50. package/src/computation/serverComputation.ts +158 -0
  51. package/src/config.ts +15 -2
  52. package/src/dataSource/datasetConfig/index.tsx +38 -6
  53. package/src/dataSource/table.tsx +15 -2
  54. package/src/fields/filterField/filterEditDialog.tsx +11 -10
  55. package/src/fields/filterField/tabs.tsx +178 -77
  56. package/src/hooks/index.ts +8 -0
  57. package/src/index.tsx +2 -0
  58. package/src/interfaces.ts +108 -5
  59. package/src/lib/execExp.ts +20 -11
  60. package/src/lib/interfaces.ts +1 -0
  61. package/src/lib/op/aggregate.ts +1 -1
  62. package/src/lib/viewQuery.ts +2 -2
  63. package/src/locales/en-US.json +11 -2
  64. package/src/locales/ja-JP.json +11 -2
  65. package/src/locales/zh-CN.json +11 -2
  66. package/src/main.tsx +1 -1
  67. package/src/renderer/hooks.ts +57 -69
  68. package/src/renderer/index.tsx +10 -6
  69. package/src/renderer/pureRenderer.tsx +40 -14
  70. package/src/renderer/specRenderer.tsx +24 -7
  71. package/src/services.ts +7 -8
  72. package/src/store/commonStore.ts +7 -7
  73. package/src/store/visualSpecStore.ts +288 -192
  74. package/src/utils/save.ts +81 -3
  75. package/src/utils/workflow.ts +148 -0
  76. package/src/vis/react-vega.tsx +21 -6
  77. package/src/vis/spec/aggregate.ts +3 -2
  78. package/src/vis/spec/stack.ts +7 -6
  79. package/src/visualSettings/index.tsx +2 -4
  80. package/src/workers/filter.worker.js +1 -1
  81. package/src/workers/sort.ts +3 -4
  82. package/src/workers/sort.worker.ts +2 -2
  83. package/src/workers/transform.ts +7 -8
  84. package/src/workers/transform.worker.js +2 -2
  85. package/src/workers/viewQuery.worker.js +2 -2
  86. package/dist/assets/sort.worker-4299a6a0.js.map +0 -1
  87. package/dist/assets/transform.worker-a12fb3d8.js.map +0 -1
@@ -1,46 +1,70 @@
1
- import { IReactionDisposer, makeAutoObservable, observable, reaction, toJS } from "mobx";
2
- import produce from "immer";
3
- import { DataSet, DraggableFieldState, IFilterRule, IViewField, IVisSpec, IVisSpecForExport, IFilterFieldForExport, IVisualConfig, Specification } from "../interfaces";
4
- import { CHANNEL_LIMIT, GEMO_TYPES, MetaFieldKeys } from "../config";
5
- import { VisSpecWithHistory } from "../models/visSpecHistory";
6
- import { IStoInfo, dumpsGWPureSpec, parseGWContent, parseGWPureSpec, stringifyGWContent } from "../utils/save";
7
- import { CommonStore } from "./commonStore";
8
- import { createCountField } from "../utils";
9
- import { nanoid } from "nanoid";
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] === "undefined") return Infinity;
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 "gw_" + nanoid(4);
41
+ return 'gw_' + nanoid(4);
18
42
  }
19
43
 
20
44
  function geomAdapter(geom: string) {
21
45
  switch (geom) {
22
- case "interval":
23
- case "bar":
24
- return "bar";
25
- case "line":
26
- return "line";
27
- case "boxplot":
28
- return "boxplot";
29
- case "area":
30
- return "area";
31
- case "point":
32
- return "point";
33
- case "arc":
34
- return "arc";
35
- case "circle":
36
- return "circle";
37
- case "heatmap":
38
- return "circle";
39
- case "rect":
40
- return "rect";
41
- case "tick":
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 "tick";
67
+ return 'tick';
44
68
  }
45
69
  }
46
70
 
@@ -62,42 +86,33 @@ export function initEncoding(): DraggableFieldState {
62
86
  };
63
87
  }
64
88
 
65
- export function initVisualConfig(): IVisualConfig {
66
- return {
67
- defaultAggregated: true,
68
- geoms: [GEMO_TYPES[0]!],
69
- stack: "stack",
70
- showActions: false,
71
- interactiveScale: false,
72
- sorted: "none",
73
- zeroScale: true,
74
- size: {
75
- mode: "auto",
76
- width: 320,
77
- height: 200,
78
- },
79
- format: {
80
- numberFormat: undefined,
81
- timeFormat: undefined,
82
- normalizedNumberFormat: undefined
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
- "A recursive call of useMutable() is detected, " +
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 === "dimension"));
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 === "measure"));
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 === "dimension")
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 === "measure")
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: "sum",
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 ["defaultAggregated", "defaultStack", "showActions", "interactiveScale"].includes(configKey): {
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 === "geoms" && Array.isArray(value):
375
- case configKey === "size" && typeof value === "object":
376
- case configKey === "sorted":
377
- case configKey === "zeroScale":
378
- case configKey === "stack": {
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 === "object": {
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("[unknown key] " + configKey + " You should registered visualConfig at setVisualConfig");
408
+ console.error('[unknown key] ' + configKey + ' You should registered visualConfig at setVisualConfig');
387
409
  }
388
410
  }
389
411
  });
390
412
  }
391
- public transformCoord(coord: "cartesian" | "polar") {
392
- if (coord === "polar") {
413
+ public transformCoord(coord: 'cartesian' | 'polar') {
414
+ if (coord === 'polar') {
393
415
  }
394
416
  }
395
- public setChartLayout(props: { mode: IVisualConfig["size"]["mode"]; width?: number; height?: number }) {
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: keyof DraggableFieldState,
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 === "filters") {
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 === "dimensions" ? "dimension" : "measure";
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: "ordinal",
511
- analyticType: "dimension",
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 === "filters") {
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: "quantitative",
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: "none" | "ascending" | "descending" = "ascending") {
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 === "undefined") return;
626
- if (destinationKey === "filters") {
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 (formatKey: keyof IVisualConfig['format'], value?: string) {
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
- "geoms",
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
- "rows",
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
- "columns",
765
+ 'columns',
668
766
  fields.find((f) => f.fid === cols)
669
767
  );
670
768
  if (rows)
671
769
  this.appendField(
672
- "rows",
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
- "color",
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
- "size",
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
- "opacity",
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 (stoInfo: IStoInfo) {
714
- this.visList = parseGWPureSpec(this.visSpecDecoder(forwardVisualConfigs(stoInfo.specList)));
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 === "one of") {
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
- private visSpecDecoder(visList: IVisSpecForExport[]): IVisSpec[] {
751
- const updatedVisList = visList.map((visSpec) => {
752
- const updatedFilters = visSpec.encodings.filters.map((filter) => {
753
- if (filter.rule?.type === "one of" && Array.isArray(filter.rule.value)) {
754
- return {
755
- ...filter,
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
  }