@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
package/src/utils/save.ts CHANGED
@@ -1,5 +1,7 @@
1
- import { IDataSet, IDataSource, IVisSpec, IVisSpecForExport } from "../interfaces";
1
+ import { DraggableFieldState, IDataSet, IDataSource, IVisSpec, IVisSpecForExport, IVisualConfig } from "../interfaces";
2
2
  import { VisSpecWithHistory } from "../models/visSpecHistory";
3
+ import { toJS } from 'mobx';
4
+ import { GEMO_TYPES } from '../config';
3
5
 
4
6
  export function dumpsGWPureSpec(list: VisSpecWithHistory[]): IVisSpec[] {
5
7
  return list.map((l) => l.exportGW());
@@ -9,10 +11,86 @@ export function parseGWPureSpec(list: IVisSpec[]): VisSpecWithHistory[] {
9
11
  return list.map((l) => new VisSpecWithHistory(l));
10
12
  }
11
13
 
14
+ export function initVisualConfig(): IVisualConfig {
15
+ return {
16
+ defaultAggregated: true,
17
+ geoms: [GEMO_TYPES[0]!],
18
+ stack: 'stack',
19
+ showActions: false,
20
+ interactiveScale: false,
21
+ sorted: 'none',
22
+ zeroScale: true,
23
+ background: undefined,
24
+ size: {
25
+ mode: "auto",
26
+ width: 320,
27
+ height: 200,
28
+ },
29
+ format: {
30
+ numberFormat: undefined,
31
+ timeFormat: undefined,
32
+ normalizedNumberFormat: undefined,
33
+ },
34
+ resolve: {
35
+ x: false,
36
+ y: false,
37
+ color: false,
38
+ opacity: false,
39
+ shape: false,
40
+ size: false,
41
+ },
42
+ limit: -1,
43
+ };
44
+ }
45
+
46
+ export function visSpecDecoder(visList: IVisSpecForExport[]): IVisSpec[] {
47
+ const updatedVisList = visList.map((visSpec) => {
48
+ const updatedFilters = visSpec.encodings.filters.map((filter) => {
49
+ if (filter.rule?.type === 'one of' && Array.isArray(filter.rule.value)) {
50
+ return {
51
+ ...filter,
52
+ rule: {
53
+ ...filter.rule,
54
+ value: new Set(filter.rule.value),
55
+ },
56
+ };
57
+ }
58
+ return filter;
59
+ });
60
+ return {
61
+ ...visSpec,
62
+ encodings: {
63
+ ...visSpec.encodings,
64
+ filters: updatedFilters,
65
+ },
66
+ } as IVisSpec;
67
+ });
68
+ return updatedVisList;
69
+ }
70
+
71
+ export const forwardVisualConfigs = (backwards: ReturnType<typeof parseGWContent>['specList']): IVisSpecForExport[] => {
72
+ return backwards.map((content) => ({
73
+ ...content,
74
+ config: {
75
+ ...initVisualConfig(),
76
+ ...content.config,
77
+ },
78
+ }));
79
+ };
80
+
81
+ export function resolveSpecFromStoInfo(info: IStoInfo) {
82
+ const spec = parseGWPureSpec(visSpecDecoder(forwardVisualConfigs(info.specList)))[0];
83
+ return {
84
+ config: toJS(spec.config) as IVisualConfig,
85
+ encodings: toJS(spec.encodings) as DraggableFieldState,
86
+ name: spec.name,
87
+ };
88
+ }
89
+
12
90
  export interface IStoInfo {
13
91
  datasets: IDataSet[];
14
92
  specList: {
15
- [K in keyof IVisSpecForExport]: K extends "config" ? Partial<IVisSpecForExport[K]> : IVisSpecForExport[K];
93
+ [K in keyof IVisSpecForExport]: K extends 'config' ? Partial<IVisSpecForExport[K]> : IVisSpecForExport[K];
16
94
  }[];
17
95
  dataSources: IDataSource[];
18
96
  }
@@ -34,7 +112,7 @@ export function download(data: string, filename: string, type: string) {
34
112
  window.navigator.msSaveOrOpenBlob(file, filename);
35
113
  else {
36
114
  // Others
37
- var a = document.createElement("a"),
115
+ var a = document.createElement('a'),
38
116
  url = URL.createObjectURL(file);
39
117
  a.href = url;
40
118
  a.download = filename;
@@ -0,0 +1,148 @@
1
+ import type { IDataQueryWorkflowStep, IExpression, IFilterWorkflowStep, ITransformWorkflowStep, IViewField, IViewWorkflowStep, IVisFilter, ISortWorkflowStep } from "../interfaces";
2
+ import type { VizSpecStore } from "../store/visualSpecStore";
3
+ import { getMeaAggKey } from ".";
4
+
5
+
6
+ const walkExpression = (expression: IExpression, each: (field: string) => void): void => {
7
+ for (const param of expression.params) {
8
+ if (param.type === 'field') {
9
+ each(param.value);
10
+ } else if (param.type === 'expression') {
11
+ walkExpression(param.value, each);
12
+ }
13
+ }
14
+ };
15
+
16
+ const treeShake = (computedFields: readonly { key: string; expression: IExpression }[], viewKeys: readonly string[]): { key: string; expression: IExpression }[] => {
17
+ const usedFields = new Set(viewKeys);
18
+ const result = computedFields.filter(f => usedFields.has(f.key));
19
+ let currentFields = result.slice();
20
+ let rest = computedFields.filter(f => !usedFields.has(f.key));
21
+ while (currentFields.length && rest.length) {
22
+ const dependencies = new Set<string>();
23
+ for (const f of currentFields) {
24
+ walkExpression(f.expression, field => dependencies.add(field));
25
+ }
26
+ const nextFields = rest.filter(f => dependencies.has(f.key));
27
+ currentFields = nextFields;
28
+ rest = rest.filter(f => !dependencies.has(f.key));
29
+ }
30
+ return result;
31
+ };
32
+
33
+ export const toWorkflow = (
34
+ viewFilters: VizSpecStore['viewFilters'],
35
+ allFields: Omit<IViewField, 'dragId'>[],
36
+ viewDimensions: Omit<IViewField, 'dragId'>[],
37
+ viewMeasures: Omit<IViewField, 'dragId'>[],
38
+ defaultAggregated: VizSpecStore['visualConfig']['defaultAggregated'],
39
+ sort: 'none' | 'ascending' | 'descending',
40
+ limit?: number,
41
+ ): IDataQueryWorkflowStep[] => {
42
+ const viewKeys = new Set<string>([...viewDimensions, ...viewMeasures].map(f => f.fid));
43
+
44
+ let filterWorkflow: IFilterWorkflowStep | null = null;
45
+ let transformWorkflow: ITransformWorkflowStep | null = null;
46
+ let viewQueryWorkflow: IViewWorkflowStep | null = null;
47
+ let sortWorkflow: ISortWorkflowStep | null = null;
48
+
49
+ // TODO: apply **fold** before filter
50
+
51
+ // First, to apply filters on the detailed data
52
+ const filters = viewFilters.filter(f => f.rule).map<IVisFilter>(f => {
53
+ viewKeys.add(f.fid);
54
+ const rule = f.rule!;
55
+ if (rule.type === 'one of') {
56
+ return {
57
+ fid: f.fid,
58
+ rule: {
59
+ type: 'one of',
60
+ value: [...rule.value],
61
+ },
62
+ };
63
+ } else if (rule.type === 'temporal range') {
64
+ const range = [new Date(rule.value[0]).getTime(), new Date(rule.value[1]).getTime()] as const;
65
+ return {
66
+ fid: f.fid,
67
+ rule: {
68
+ type: 'temporal range',
69
+ value: range,
70
+ },
71
+ };
72
+ } else {
73
+ const range = [Number(rule.value[0]), Number(rule.value[1])] as const;
74
+ return {
75
+ fid: f.fid,
76
+ rule: {
77
+ type: 'range',
78
+ value: range,
79
+ },
80
+ };
81
+ }
82
+ });
83
+ if (filters.length) {
84
+ filterWorkflow = {
85
+ type: 'filter',
86
+ filters,
87
+ };
88
+ }
89
+
90
+ // Second, to transform the data by rows 1 by 1
91
+ const computedFields = treeShake(allFields.filter(f => f.computed && f.expression).map(f => ({
92
+ key: f.fid,
93
+ expression: f.expression!,
94
+ })), [...viewKeys]);
95
+ if (computedFields.length) {
96
+ transformWorkflow = {
97
+ type: 'transform',
98
+ transform: computedFields,
99
+ };
100
+ }
101
+
102
+ // Finally, to apply the aggregation
103
+ // When aggregation is enabled, there're 2 cases:
104
+ // 1. If any of the measures is aggregated, then we apply the aggregation
105
+ // 2. If there's no measure in the view, then we apply the aggregation
106
+ const aggregateOn = viewMeasures.filter(f => f.aggName).map(f => [f.fid, f.aggName as string]);
107
+ const aggergated = defaultAggregated && (aggregateOn.length || (viewMeasures.length === 0 && viewDimensions.length > 0));
108
+ if (aggergated) {
109
+ viewQueryWorkflow = {
110
+ type: 'view',
111
+ query: [{
112
+ op: 'aggregate',
113
+ groupBy: viewDimensions.map(f => f.fid),
114
+ measures: viewMeasures.map((f) => ({
115
+ field: f.fid,
116
+ agg: f.aggName as any,
117
+ asFieldKey: getMeaAggKey(f.fid, f.aggName!),
118
+ })),
119
+ }],
120
+ };
121
+ } else {
122
+ viewQueryWorkflow = {
123
+ type: 'view',
124
+ query: [{
125
+ op: 'raw',
126
+ fields: [...new Set([...viewDimensions, ...viewMeasures])].map(f => f.fid),
127
+ }],
128
+ };
129
+ }
130
+
131
+ if (sort !== "none" && limit) {
132
+ sortWorkflow = {
133
+ type: 'sort',
134
+ by: viewMeasures.map(f => aggergated ? getMeaAggKey(f.fid, f.aggName) : f.fid),
135
+ sort,
136
+ };
137
+ }
138
+
139
+
140
+ const steps: IDataQueryWorkflowStep[] = [
141
+ filterWorkflow!,
142
+ transformWorkflow!,
143
+ viewQueryWorkflow!,
144
+ sortWorkflow!,
145
+ ].filter(Boolean);
146
+
147
+ return steps;
148
+ };
@@ -4,10 +4,9 @@ import { Subject, Subscription } from 'rxjs'
4
4
  import * as op from 'rxjs/operators';
5
5
  import type { ScenegraphEvent } from 'vega';
6
6
  import styled from 'styled-components';
7
-
7
+ import { NonPositionChannelConfigList, PositionChannelConfigList } from '../config';
8
8
  import { useVegaExportApi } from '../utils/vegaApiExport';
9
9
  import { IViewField, IRow, IStackMode, VegaGlobalConfig, IVegaChartRef } from '../interfaces';
10
- import { useTranslation } from 'react-i18next';
11
10
  import { getVegaTimeFormatRules } from './temporalFormat';
12
11
  import { getSingleView } from './spec/view';
13
12
  import { NULL_FIELD } from './spec/field';
@@ -29,7 +28,7 @@ interface ReactVegaProps {
29
28
  name?: string;
30
29
  rows: Readonly<IViewField[]>;
31
30
  columns: Readonly<IViewField[]>;
32
- dataSource: IRow[];
31
+ dataSource: readonly IRow[];
33
32
  defaultAggregate?: boolean;
34
33
  stack: IStackMode;
35
34
  interactiveScale: boolean;
@@ -48,6 +47,8 @@ interface ReactVegaProps {
48
47
  height: number;
49
48
  onGeomClick?: (values: any, e: any) => void
50
49
  vegaConfig: VegaGlobalConfig;
50
+ /** @default "en-US" */
51
+ locale?: string;
51
52
  }
52
53
 
53
54
  const click$ = new Subject<ScenegraphEvent>();
@@ -100,9 +101,9 @@ const ReactVega = forwardRef<IReactVegaHandler, ReactVegaProps>(function ReactVe
100
101
  // dark = 'media',
101
102
  vegaConfig,
102
103
  // format
104
+ locale = 'en-US',
103
105
  } = props;
104
106
  const [viewPlaceholders, setViewPlaceholders] = useState<React.MutableRefObject<HTMLDivElement>[]>([]);
105
- const { i18n } = useTranslation();
106
107
  // const mediaTheme = useCurrentMediaTheme(dark);
107
108
  // const themeConfig = builtInThemes[themeKey]?.[mediaTheme];
108
109
 
@@ -221,8 +222,22 @@ const ReactVega = forwardRef<IReactVegaHandler, ReactVegaProps>(function ReactVe
221
222
  spec.encoding = singleView.encoding;
222
223
  }
223
224
 
225
+ spec.resolve ||= {};
226
+ // @ts-ignore
227
+ let resolve = vegaConfig.resolve;
228
+ for (let v in resolve) {
229
+ let value = resolve[v] ? 'independent' : 'shared';
230
+ // @ts-ignore
231
+ spec.resolve.scale = { ...spec.resolve.scale, [v]: value };
232
+ if((PositionChannelConfigList as string[]).includes(v)) {
233
+ spec.resolve.axis = { ...spec.resolve.axis, [v]: value };
234
+ }else if((NonPositionChannelConfigList as string[]).includes(v)){
235
+ spec.resolve.legend = { ...spec.resolve.legend, [v]: value };
236
+ }
237
+ }
238
+
224
239
  if (viewPlaceholders.length > 0 && viewPlaceholders[0].current) {
225
- const task = embed(viewPlaceholders[0].current, spec, { mode: 'vega-lite', actions: showActions, timeFormatLocale: getVegaTimeFormatRules(i18n.language), config: vegaConfig }).then(res => {
240
+ const task = embed(viewPlaceholders[0].current, spec, { mode: 'vega-lite', actions: showActions, timeFormatLocale: getVegaTimeFormatRules(locale), config: vegaConfig }).then(res => {
226
241
  const container = res.view.container();
227
242
  const canvas = container?.querySelector('canvas') ?? null;
228
243
  vegaRefs.current = [{
@@ -301,7 +316,7 @@ const ReactVega = forwardRef<IReactVegaHandler, ReactVegaProps>(function ReactVe
301
316
  }
302
317
  if (node) {
303
318
  const id = index;
304
- const task = embed(node, ans, { mode: 'vega-lite', actions: showActions, timeFormatLocale: getVegaTimeFormatRules(i18n.language), config: vegaConfig }).then(res => {
319
+ const task = embed(node, ans, { mode: 'vega-lite', actions: showActions, timeFormatLocale: getVegaTimeFormatRules(locale), config: vegaConfig }).then(res => {
305
320
  const container = res.view.container();
306
321
  const canvas = container?.querySelector('canvas') ?? null;
307
322
  vegaRefs.current[id] = {
@@ -4,11 +4,12 @@ import { getMeaAggKey } from '../../utils';
4
4
 
5
5
  export function channelAggregate(encoding: { [key: string]: any }, fields: IViewField[]) {
6
6
  Object.values(encoding).forEach((c) => {
7
- const targetField = fields.find((f) => f.fid === c.field && !('aggregate' in c));
7
+ if (c.aggregate === null) return;
8
+ const targetField = fields.find((f) => f.fid === c.field && (f.analyticType === 'measure' || f.fid === COUNT_FIELD_ID));
8
9
  if (targetField && targetField.fid === COUNT_FIELD_ID) {
9
10
  c.title = 'Count';
10
11
  c.field = getMeaAggKey(targetField.fid, targetField.aggName)
11
- } else if (targetField && targetField.analyticType === 'measure') {
12
+ } else if (targetField) {
12
13
  c.title = `${targetField.aggName}(${targetField.name})`;
13
14
  c.field = getMeaAggKey(targetField.fid, targetField.aggName)
14
15
  }
@@ -1,11 +1,12 @@
1
1
  import { IStackMode } from "../../interfaces";
2
2
 
3
3
  export function channelStack(encoding: { [key: string]: any }, stackMode: IStackMode) {
4
- if (stackMode === 'stack') return;
5
- if (encoding.x && encoding.x.type === 'quantitative') {
6
- encoding.x.stack = stackMode === 'none' ? null : 'normalize';
7
- }
8
- if (encoding.y && encoding.y.type === 'quantitative') {
9
- encoding.y.stack = stackMode === 'none' ? null : 'normalize';
4
+ if (stackMode === 'stack' || stackMode === 'zero') return;
5
+ let stackValue = stackMode === 'none' ? null : stackMode;
6
+ const stackableChannels = ['x', 'y', 'theta', 'radius'];
7
+ for (let ch of stackableChannels) {
8
+ if (encoding[ch] && encoding[ch].type === 'quantitative') {
9
+ encoding[ch].stack = stackValue
10
+ }
10
11
  }
11
12
  }
@@ -37,7 +37,6 @@ import throttle from '../utils/throttle';
37
37
  import KanariesLogo from '../assets/kanaries.png';
38
38
  import { ImageWithFallback } from '../components/timeoutImg';
39
39
  import LimitSetting from '../components/limitSetting';
40
- import { runInAction } from 'mobx';
41
40
 
42
41
  const Invisible = styled.div`
43
42
  clip: rect(1px, 1px, 1px, 1px);
@@ -367,6 +366,7 @@ const VisualSettings: React.FC<IVisualSettings> = ({
367
366
  none: XMarkIcon,
368
367
  stack: ChevronDoubleUpIcon,
369
368
  normalize: ArrowsUpDownIcon,
369
+ center: ChevronUpDownIcon, // TODO: fix unsafe extends
370
370
  }[g],
371
371
  })),
372
372
  value: stack,
@@ -505,9 +505,7 @@ const VisualSettings: React.FC<IVisualSettings> = ({
505
505
  <LimitSetting
506
506
  value={limit}
507
507
  setValue={(v) => {
508
- runInAction(() => {
509
- vizStore.limit = v;
510
- });
508
+ vizStore.setLimit(v);
511
509
  }}
512
510
  />
513
511
  </FormContainer>
@@ -3,7 +3,7 @@
3
3
 
4
4
  /**
5
5
  * @param {import('../interfaces').IRow[]} dataSource
6
- * @param {import('../interfaces').IFilterField[]} filters
6
+ * @param {import('../interfaces').IFilterFiledSimple[]} filters
7
7
  * @return {import('../interfaces').IRow[]}
8
8
  */
9
9
  const filter = (dataSource, filters) => {
@@ -1,5 +1,4 @@
1
- import { IViewField, IRow } from '../interfaces';
2
- import { getMeaAggKey } from '../utils';
1
+ import { IRow } from '../interfaces';
3
2
 
4
3
  function compareMulti(a: number[], b: number[]): number {
5
4
  if (a.length < b.length) return -compareMulti(b, a);
@@ -11,12 +10,12 @@ function compareMulti(a: number[], b: number[]): number {
11
10
  return 0;
12
11
  }
13
12
 
14
- export function sortBy(data: IRow[], viewMeasures: IViewField[], sort: 'ascending' | 'descending') {
13
+ export function sortBy(data: IRow[], viewMeasures: string[], sort: 'ascending' | 'descending') {
15
14
  const sortM = sort === 'ascending' ? 1 : -1;
16
15
  return data
17
16
  .map((x) => ({
18
17
  data: x,
19
- value: viewMeasures.map((f) => x[getMeaAggKey(f.fid, f.aggName)]),
18
+ value: viewMeasures.map((f) => x[f]),
20
19
  }))
21
20
  .sort((a, b) => sortM * compareMulti(a.value, b.value))
22
21
  .map((x) => x.data);
@@ -1,10 +1,10 @@
1
- import { IRow, IViewField } from '../interfaces';
1
+ import { IRow } from '../interfaces';
2
2
  import { sortBy } from './sort';
3
3
 
4
4
  const main = (e: {
5
5
  data: {
6
6
  data: IRow[];
7
- viewMeasures: IViewField[];
7
+ viewMeasures: string[];
8
8
  sort: 'ascending' | 'descending';
9
9
  };
10
10
  }) => {
@@ -1,12 +1,11 @@
1
- import { IField, IRow } from "../interfaces";
1
+ import { ITransformWorkflowStep, IRow, IExpression } from "../interfaces";
2
2
  import { dataframe2Dataset, dataset2DataFrame, execExpression } from "../lib/execExp";
3
3
 
4
- export function transformData(data: IRow[], columns: IField[]) {
5
- const computedFields = columns.filter((f) => f.computed);
6
- let df = dataset2DataFrame(data, columns);
7
- for (let i = 0; i < computedFields.length; i++) {
8
- const field = computedFields[i];
9
- df = execExpression(field.expression!, df, columns);
4
+ export function transformData(data: IRow[], trans: { key: string, expression: IExpression }[]) {
5
+ let df = dataset2DataFrame(data);
6
+ for (let i = 0; i < trans.length; i++) {
7
+ const field = trans[i];
8
+ df = execExpression(field.expression, df);
10
9
  }
11
- return dataframe2Dataset(df, columns);
10
+ return dataframe2Dataset(df);
12
11
  }
@@ -1,9 +1,9 @@
1
1
  import { transformData } from './transform'
2
2
  const main = e => {
3
- const { dataSource, columns } = e.data;
3
+ const { dataSource, trans } = e.data;
4
4
 
5
5
  try {
6
- const ans = transformData(dataSource, columns);
6
+ const ans = transformData(dataSource, trans);
7
7
  self.postMessage(ans);
8
8
  } catch (error) {
9
9
  self.postMessage({ error: error.message });
@@ -1,8 +1,8 @@
1
1
  import { queryView } from '../lib/viewQuery'
2
2
  const main = e => {
3
3
  try {
4
- const { dataSource, metas, query } = e.data;
5
- const ans = queryView(dataSource, metas, query);
4
+ const { dataSource, query } = e.data;
5
+ const ans = queryView(dataSource, query);
6
6
  self.postMessage(ans);
7
7
 
8
8
  } catch (err) {