@kanaries/graphic-walker 0.3.15 → 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.
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 +8 -5
  9. package/dist/components/limitSetting.d.ts +5 -0
  10. package/dist/components/pivotTable/store.d.ts +0 -2
  11. package/dist/components/spinner.d.ts +2 -0
  12. package/dist/computation/clientComputation.d.ts +3 -0
  13. package/dist/computation/serverComputation.d.ts +8 -0
  14. package/dist/config.d.ts +3 -1
  15. package/dist/fields/filterField/tabs.d.ts +2 -1
  16. package/dist/graphic-walker.es.js +16181 -15523
  17. package/dist/graphic-walker.es.js.map +1 -1
  18. package/dist/graphic-walker.umd.js +144 -144
  19. package/dist/graphic-walker.umd.js.map +1 -1
  20. package/dist/hooks/index.d.ts +1 -0
  21. package/dist/index.d.ts +2 -0
  22. package/dist/interfaces.d.ts +93 -4
  23. package/dist/lib/execExp.d.ts +4 -4
  24. package/dist/lib/interfaces.d.ts +1 -0
  25. package/dist/lib/viewQuery.d.ts +2 -2
  26. package/dist/renderer/hooks.d.ts +10 -4
  27. package/dist/renderer/index.d.ts +2 -1
  28. package/dist/renderer/pureRenderer.d.ts +17 -1
  29. package/dist/renderer/specRenderer.d.ts +1 -0
  30. package/dist/services.d.ts +8 -4
  31. package/dist/store/commonStore.d.ts +2 -2
  32. package/dist/store/visualSpecStore.d.ts +58 -40
  33. package/dist/utils/save.d.ts +10 -2
  34. package/dist/utils/workflow.d.ts +3 -0
  35. package/dist/vis/react-vega.d.ts +3 -1
  36. package/dist/workers/sort.d.ts +2 -0
  37. package/dist/workers/sort.worker.d.ts +1 -0
  38. package/dist/workers/transform.d.ts +5 -2
  39. package/package.json +2 -2
  40. package/src/App.tsx +46 -7
  41. package/src/components/askViz/index.tsx +92 -0
  42. package/src/components/askViz/schemaTransform.ts +38 -0
  43. package/src/components/dataTable/index.tsx +51 -11
  44. package/src/components/limitSetting.tsx +38 -0
  45. package/src/components/pivotTable/index.tsx +0 -1
  46. package/src/components/pivotTable/store.tsx +0 -16
  47. package/src/components/spinner.tsx +14 -0
  48. package/src/components/toggle.tsx +2 -2
  49. package/src/components/visualConfig/index.tsx +78 -8
  50. package/src/computation/clientComputation.ts +55 -0
  51. package/src/computation/serverComputation.ts +153 -0
  52. package/src/config.ts +15 -2
  53. package/src/dataSource/datasetConfig/index.tsx +38 -6
  54. package/src/dataSource/table.tsx +11 -2
  55. package/src/fields/filterField/filterEditDialog.tsx +11 -10
  56. package/src/fields/filterField/tabs.tsx +178 -77
  57. package/src/hooks/index.ts +10 -0
  58. package/src/index.tsx +2 -0
  59. package/src/interfaces.ts +108 -5
  60. package/src/lib/execExp.ts +20 -11
  61. package/src/lib/interfaces.ts +1 -0
  62. package/src/lib/op/aggregate.ts +1 -1
  63. package/src/lib/viewQuery.ts +2 -2
  64. package/src/locales/en-US.json +12 -2
  65. package/src/locales/ja-JP.json +12 -2
  66. package/src/locales/zh-CN.json +12 -2
  67. package/src/main.tsx +1 -1
  68. package/src/renderer/hooks.ts +113 -49
  69. package/src/renderer/index.tsx +32 -18
  70. package/src/renderer/pureRenderer.tsx +44 -22
  71. package/src/renderer/specRenderer.tsx +24 -7
  72. package/src/services.ts +30 -9
  73. package/src/store/commonStore.ts +7 -7
  74. package/src/store/visualSpecStore.ts +300 -193
  75. package/src/utils/save.ts +81 -3
  76. package/src/utils/workflow.ts +148 -0
  77. package/src/vis/react-vega.tsx +21 -6
  78. package/src/vis/spec/aggregate.ts +3 -2
  79. package/src/vis/spec/stack.ts +7 -6
  80. package/src/visualSettings/index.tsx +22 -1
  81. package/src/workers/filter.worker.js +1 -1
  82. package/src/workers/sort.ts +22 -0
  83. package/src/workers/sort.worker.ts +21 -0
  84. package/src/workers/transform.ts +7 -8
  85. package/src/workers/transform.worker.js +2 -2
  86. package/src/workers/viewQuery.worker.js +2 -2
  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
  }
@@ -19,6 +19,7 @@ import {
19
19
  LightBulbIcon,
20
20
  CodeBracketSquareIcon,
21
21
  Cog6ToothIcon,
22
+ HashtagIcon,
22
23
  } from '@heroicons/react/24/outline';
23
24
  import { observer } from 'mobx-react-lite';
24
25
  import React, { SVGProps, useCallback, useMemo } from 'react';
@@ -35,6 +36,8 @@ import { useCurrentMediaTheme } from '../utils/media';
35
36
  import throttle from '../utils/throttle';
36
37
  import KanariesLogo from '../assets/kanaries.png';
37
38
  import { ImageWithFallback } from '../components/timeoutImg';
39
+ import LimitSetting from '../components/limitSetting';
40
+ import { runInAction } from 'mobx';
38
41
 
39
42
  const Invisible = styled.div`
40
43
  clip: rect(1px, 1px, 1px, 1px);
@@ -73,7 +76,7 @@ const VisualSettings: React.FC<IVisualSettings> = ({
73
76
  exclude = [],
74
77
  }) => {
75
78
  const { vizStore, commonStore } = useGlobalStore();
76
- const { visualConfig, canUndo, canRedo } = vizStore;
79
+ const { visualConfig, canUndo, canRedo, limit } = vizStore;
77
80
  const { t: tGlobal } = useTranslation();
78
81
  const { t } = useTranslation('translation', { keyPrefix: 'main.tabpanel.settings' });
79
82
 
@@ -364,6 +367,7 @@ const VisualSettings: React.FC<IVisualSettings> = ({
364
367
  none: XMarkIcon,
365
368
  stack: ChevronDoubleUpIcon,
366
369
  normalize: ArrowsUpDownIcon,
370
+ center: ChevronUpDownIcon, // TODO: fix unsafe extends
367
371
  }[g],
368
372
  })),
369
373
  value: stack,
@@ -493,6 +497,22 @@ const VisualSettings: React.FC<IVisualSettings> = ({
493
497
  },
494
498
  ...(extra.length === 0 ? [] : ['-', ...extra]),
495
499
  '-',
500
+ {
501
+ key: 'limit_axis',
502
+ label: t('limit'),
503
+ icon: HashtagIcon,
504
+ form: (
505
+ <FormContainer>
506
+ <LimitSetting
507
+ value={limit}
508
+ setValue={(v) => {
509
+ vizStore.setLimit(v);
510
+ }}
511
+ />
512
+ </FormContainer>
513
+ ),
514
+ },
515
+ '-',
496
516
  {
497
517
  key: 'kanaries',
498
518
  label: 'kanaries',
@@ -532,6 +552,7 @@ const VisualSettings: React.FC<IVisualSettings> = ({
532
552
  dark,
533
553
  extra,
534
554
  exclude,
555
+ limit,
535
556
  ]);
536
557
 
537
558
  return (
@@ -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) => {
@@ -0,0 +1,22 @@
1
+ import { IRow } from '../interfaces';
2
+
3
+ function compareMulti(a: number[], b: number[]): number {
4
+ if (a.length < b.length) return -compareMulti(b, a);
5
+ for (let i = 0; i < a.length; i++) {
6
+ if (!b[i]) return 1;
7
+ const c = a[i] - b[i];
8
+ if (c !== 0) return c;
9
+ }
10
+ return 0;
11
+ }
12
+
13
+ export function sortBy(data: IRow[], viewMeasures: string[], sort: 'ascending' | 'descending') {
14
+ const sortM = sort === 'ascending' ? 1 : -1;
15
+ return data
16
+ .map((x) => ({
17
+ data: x,
18
+ value: viewMeasures.map((f) => x[f]),
19
+ }))
20
+ .sort((a, b) => sortM * compareMulti(a.value, b.value))
21
+ .map((x) => x.data);
22
+ }
@@ -0,0 +1,21 @@
1
+ import { IRow } from '../interfaces';
2
+ import { sortBy } from './sort';
3
+
4
+ const main = (e: {
5
+ data: {
6
+ data: IRow[];
7
+ viewMeasures: string[];
8
+ sort: 'ascending' | 'descending';
9
+ };
10
+ }) => {
11
+ try {
12
+ const { data, viewMeasures, sort } = e.data;
13
+ const ans = sortBy(data, viewMeasures, sort);
14
+ self.postMessage(ans);
15
+ } catch (err: any) {
16
+ console.error(err.stack);
17
+ self.postMessage(err.stack);
18
+ }
19
+ };
20
+
21
+ self.addEventListener('message', main, false);
@@ -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) {
@@ -1 +0,0 @@
1
- {"version":3,"file":"transform.worker-a12fb3d8.js","sources":["../src/lib/execExp.ts","../src/workers/transform.ts","../src/workers/transform.worker.js"],"sourcesContent":["import { IExpParamter, IExpression, IField, IRow } from \"../interfaces\";\n\ninterface IDataFrame {\n [key: string]: any[];\n}\n\nexport function execExpression (exp: IExpression, dataFrame: IDataFrame, columns: IField[]): IDataFrame {\n const { op, params } = exp;\n const subFrame: IDataFrame = { ...dataFrame };\n const len = dataFrame[Object.keys(dataFrame)[0]].length;\n for (let param of params) {\n switch (param.type) {\n case 'field':\n subFrame[param.value] = dataFrame[param.value];\n break;\n case 'constant':\n subFrame[param.value] = new Array(len).fill(param.value);\n break;\n case 'expression':\n let f = execExpression(param.value, dataFrame, columns);\n Object.keys(f).forEach(key => {\n subFrame[key] = f[key];\n })\n break;\n case 'value':\n default:\n break;\n }\n }\n switch (op) {\n case 'one':\n return one(exp.as, params, subFrame);\n case 'bin':\n return bin(exp.as, params, subFrame);\n case 'log2':\n return log2(exp.as, params, subFrame);\n case 'log10':\n return log10(exp.as, params, subFrame);\n case 'binCount':\n return binCount(exp.as, params, subFrame);\n default:\n return subFrame;\n }\n}\n\nfunction bin(resKey: string, params: IExpParamter[], data: IDataFrame, binSize: number | undefined = 10): IDataFrame {\n const { value: fieldKey } = params[0];\n const fieldValues = data[fieldKey] as number[];\n let _min = Infinity;\n let _max = -Infinity;\n for (let i = 0; i < fieldValues.length; i++) {\n let val = fieldValues[i];\n if (val > _max) _max = val;\n if (val < _min) _min = val;\n }\n const step = (_max - _min) / binSize;\n const beaStep = Math.max(-Math.round(Math.log10(_max - _min)) + 2, 0)\n const newValues = fieldValues.map((v: number) => {\n let bIndex = Math.floor((v - _min) / step);\n if (bIndex === binSize) bIndex = binSize - 1;\n return Number(((bIndex * step + _min)).toFixed(beaStep))\n });\n return {\n ...data,\n [resKey]: newValues,\n }\n}\n\nfunction binCount(resKey: string, params: IExpParamter[], data: IDataFrame, binSize: number | undefined = 10): IDataFrame {\n const { value: fieldKey } = params[0];\n const fieldValues = data[fieldKey] as number[];\n\n const valueWithIndices: {val: number; index: number; orderIndex: number }[] = fieldValues.map((v, i) => ({\n val: v,\n index: i\n })).sort((a, b) => a.val - b.val)\n .map((item, i) => ({\n val: item.val,\n index: item.index,\n orderIndex: i\n }))\n\n const groupSize = valueWithIndices.length / binSize;\n\n const newValues = valueWithIndices.sort((a, b) => a.index - b.index).map(item => {\n let bIndex = Math.floor(item.orderIndex / groupSize);\n if (bIndex === binSize) bIndex = binSize - 1;\n return bIndex + 1\n })\n return {\n ...data,\n [resKey]: newValues,\n }\n}\n\nfunction log2(resKey: string, params: IExpParamter[], data: IDataFrame): IDataFrame {\n const { value } = params[0];\n const field = data[value];\n const newField = field.map((v: number) => Math.log2(v));\n return {\n ...data,\n [resKey]: newField,\n }\n}\n\nfunction log10(resKey: string, params: IExpParamter[], data: IDataFrame): IDataFrame {\n const { value: fieldKey } = params[0];\n const fieldValues = data[fieldKey];\n const newField = fieldValues.map((v: number) => Math.log10(v));\n return {\n ...data,\n [resKey]: newField,\n }\n}\n\nfunction one(resKey: string, params: IExpParamter[], data: IDataFrame): IDataFrame {\n // const { value: fieldKey } = params[0];\n if (Object.keys(data).length === 0) return data;\n const len = data[Object.keys(data)[0]].length;\n const newField = new Array(len).fill(1);\n return {\n ...data,\n [resKey]: newField,\n }\n}\n\nexport function dataset2DataFrame(dataset: IRow[], columns: IField[]): IDataFrame {\n const dataFrame: IDataFrame = {};\n columns.forEach((col) => {\n dataFrame[col.fid] = dataset.map((row) => row[col.fid]);\n });\n return dataFrame;\n}\n\nexport function dataframe2Dataset(dataFrame: IDataFrame, columns: IField[]): IRow[] {\n if (columns.length === 0) return [];\n const dataset: IRow[] = [];\n const len = dataFrame[Object.keys(dataFrame)[0]].length;\n for (let i = 0; i < len; i++) {\n const row: IRow = {};\n columns.forEach((col) => {\n row[col.fid] = dataFrame[col.fid][i];\n });\n dataset.push(row);\n }\n return dataset;\n}\n","import { IField, IRow } from \"../interfaces\";\nimport { dataframe2Dataset, dataset2DataFrame, execExpression } from \"../lib/execExp\";\n\nexport function transformData(data: IRow[], columns: IField[]) {\n const computedFields = columns.filter((f) => f.computed);\n let df = dataset2DataFrame(data, columns);\n for (let i = 0; i < computedFields.length; i++) {\n const field = computedFields[i];\n df = execExpression(field.expression!, df, columns);\n }\n return dataframe2Dataset(df, columns);\n}\n","import { transformData } from './transform'\nconst main = e => {\n const { dataSource, columns } = e.data;\n\n try {\n const ans = transformData(dataSource, columns);\n self.postMessage(ans);\n } catch (error) {\n self.postMessage({ error: error.message });\n }\n};\n\nself.addEventListener('message', main, false);"],"names":["execExpression","exp","dataFrame","columns","op","params","subFrame","len","param","f","key","one","bin","log2","log10","binCount","resKey","data","binSize","fieldKey","fieldValues","_min","_max","i","val","step","beaStep","newValues","v","bIndex","valueWithIndices","b","item","groupSize","value","newField","dataset2DataFrame","dataset","col","row","dataframe2Dataset","transformData","computedFields","df","field","main","e","dataSource","ans","error"],"mappings":"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"}