@kanaries/graphic-walker 0.3.15 → 0.3.16
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/assets/sort.worker-4299a6a0.js.map +1 -0
- package/dist/components/limitSetting.d.ts +5 -0
- package/dist/graphic-walker.es.js +20820 -20738
- package/dist/graphic-walker.es.js.map +1 -1
- package/dist/graphic-walker.umd.js +136 -136
- package/dist/graphic-walker.umd.js.map +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/renderer/hooks.d.ts +2 -0
- package/dist/services.d.ts +1 -0
- package/dist/store/visualSpecStore.d.ts +2 -0
- package/dist/workers/sort.d.ts +2 -0
- package/dist/workers/sort.worker.d.ts +1 -0
- package/package.json +1 -1
- package/src/components/limitSetting.tsx +38 -0
- package/src/hooks/index.ts +10 -0
- package/src/locales/en-US.json +1 -0
- package/src/locales/ja-JP.json +1 -0
- package/src/locales/zh-CN.json +1 -0
- package/src/renderer/hooks.ts +37 -7
- package/src/renderer/index.tsx +22 -12
- package/src/renderer/pureRenderer.tsx +7 -11
- package/src/services.ts +25 -3
- package/src/store/visualSpecStore.ts +14 -2
- package/src/visualSettings/index.tsx +23 -1
- package/src/workers/sort.ts +23 -0
- package/src/workers/sort.worker.ts +21 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useDebounceValue<T>(value: T, timeout?: number): T;
|
package/dist/renderer/hooks.d.ts
CHANGED
package/dist/services.d.ts
CHANGED
|
@@ -7,3 +7,4 @@ export interface IVisSpace {
|
|
|
7
7
|
export declare const applyFilter: (data: IRow[], filters: readonly IFilterField[]) => Promise<IRow[]>;
|
|
8
8
|
export declare const transformDataService: (data: IRow[], columns: IField[]) => Promise<IRow[]>;
|
|
9
9
|
export declare const applyViewQuery: (data: IRow[], metas: IField[], query: IViewQuery) => Promise<IRow[]>;
|
|
10
|
+
export declare const applySort: (data: IRow[], viewMeasures: IField[], sort: 'ascending' | 'descending') => Promise<IRow[]>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
|
|
4
|
+
export default function LimitSetting(props: { value: number; setValue: (v: number) => void }) {
|
|
5
|
+
const { t } = useTranslation('translation', { keyPrefix: 'main.tabpanel.settings' });
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<div className=" mt-2">
|
|
9
|
+
<input
|
|
10
|
+
className="w-full h-2 bg-blue-100 appearance-none"
|
|
11
|
+
type="range"
|
|
12
|
+
name="limit"
|
|
13
|
+
value={props.value > 0 ? props.value : 0}
|
|
14
|
+
min="1"
|
|
15
|
+
max="50"
|
|
16
|
+
disabled={props.value < 0}
|
|
17
|
+
step="1"
|
|
18
|
+
onChange={(e) => {
|
|
19
|
+
const v = parseInt(e.target.value);
|
|
20
|
+
if (!isNaN(v)) {
|
|
21
|
+
props.setValue(v);
|
|
22
|
+
}
|
|
23
|
+
}}
|
|
24
|
+
/>
|
|
25
|
+
<output className="text-sm ml-1" htmlFor="height">
|
|
26
|
+
<input
|
|
27
|
+
type="checkbox"
|
|
28
|
+
className="h-4 w-4 mr-1 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600"
|
|
29
|
+
checked={props.value > 0}
|
|
30
|
+
onChange={(e) => {
|
|
31
|
+
props.setValue(e.target.checked ? 30 : -1);
|
|
32
|
+
}}
|
|
33
|
+
></input>
|
|
34
|
+
{`${t('limit')}${props.value > 0 ? `: ${props.value}` : ''}`}
|
|
35
|
+
</output>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
export function useDebounceValue<T>(value: T, timeout = 200): T {
|
|
4
|
+
const [innerValue, setInnerValue] = useState(value);
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
const handler = setTimeout(() => setInnerValue(value), timeout);
|
|
7
|
+
return () => clearTimeout(handler);
|
|
8
|
+
}, [value]);
|
|
9
|
+
return innerValue;
|
|
10
|
+
}
|
package/src/locales/en-US.json
CHANGED
package/src/locales/ja-JP.json
CHANGED
package/src/locales/zh-CN.json
CHANGED
package/src/renderer/hooks.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { useState, useEffect, useMemo, useRef } from 'react';
|
|
2
2
|
import { unstable_batchedUpdates } from 'react-dom';
|
|
3
3
|
import type { DeepReadonly, IFilterField, IRow, IViewField } from '../interfaces';
|
|
4
|
-
import { applyFilter, applyViewQuery, transformDataService } from '../services';
|
|
4
|
+
import { applyFilter, applySort, applyViewQuery, transformDataService } from '../services';
|
|
5
5
|
import { getMeaAggKey } from '../utils';
|
|
6
6
|
import { useAppRootContext } from '../components/appRoot';
|
|
7
|
-
|
|
7
|
+
import { useDebounceValue } from '../hooks';
|
|
8
8
|
|
|
9
9
|
interface UseRendererProps {
|
|
10
10
|
data: IRow[];
|
|
@@ -13,6 +13,8 @@ interface UseRendererProps {
|
|
|
13
13
|
viewMeasures: IViewField[];
|
|
14
14
|
filters: readonly DeepReadonly<IFilterField>[];
|
|
15
15
|
defaultAggregated: boolean;
|
|
16
|
+
sort: 'none' | 'ascending' | 'descending';
|
|
17
|
+
limit: number;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
interface UseRendererResult {
|
|
@@ -21,10 +23,21 @@ interface UseRendererResult {
|
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export const useRenderer = (props: UseRendererProps): UseRendererResult => {
|
|
24
|
-
const {
|
|
26
|
+
const {
|
|
27
|
+
data,
|
|
28
|
+
allFields,
|
|
29
|
+
viewDimensions,
|
|
30
|
+
viewMeasures,
|
|
31
|
+
filters,
|
|
32
|
+
defaultAggregated,
|
|
33
|
+
sort,
|
|
34
|
+
limit: storeLimit,
|
|
35
|
+
} = props;
|
|
25
36
|
const [computing, setComputing] = useState(false);
|
|
26
37
|
const taskIdRef = useRef(0);
|
|
27
38
|
|
|
39
|
+
const limit = useDebounceValue(storeLimit);
|
|
40
|
+
|
|
28
41
|
const [viewData, setViewData] = useState<IRow[]>([]);
|
|
29
42
|
|
|
30
43
|
const appRef = useAppRootContext();
|
|
@@ -50,10 +63,26 @@ export const useRenderer = (props: UseRendererProps): UseRendererResult => {
|
|
|
50
63
|
return applyViewQuery(d, dims.concat(meas), {
|
|
51
64
|
op: defaultAggregated ? 'aggregate' : 'raw',
|
|
52
65
|
groupBy: dims.map((f) => f.fid),
|
|
53
|
-
measures: meas.map((f) => ({
|
|
66
|
+
measures: meas.map((f) => ({
|
|
67
|
+
field: f.fid,
|
|
68
|
+
agg: f.aggName as any,
|
|
69
|
+
asFieldKey: getMeaAggKey(f.fid, f.aggName!),
|
|
70
|
+
})),
|
|
54
71
|
});
|
|
55
72
|
})
|
|
56
|
-
.then(data => {
|
|
73
|
+
.then((data) => {
|
|
74
|
+
if (limit > 0 && sort !== 'none' && viewMeasures.length > 0) {
|
|
75
|
+
return applySort(data, viewMeasures, sort);
|
|
76
|
+
}
|
|
77
|
+
return data;
|
|
78
|
+
})
|
|
79
|
+
.then((data) => {
|
|
80
|
+
if (limit > 0) {
|
|
81
|
+
return data.slice(0, limit);
|
|
82
|
+
}
|
|
83
|
+
return data;
|
|
84
|
+
})
|
|
85
|
+
.then((data) => {
|
|
57
86
|
if (taskId !== taskIdRef.current) {
|
|
58
87
|
return;
|
|
59
88
|
}
|
|
@@ -62,7 +91,8 @@ export const useRenderer = (props: UseRendererProps): UseRendererResult => {
|
|
|
62
91
|
setComputing(false);
|
|
63
92
|
setViewData(data);
|
|
64
93
|
});
|
|
65
|
-
})
|
|
94
|
+
})
|
|
95
|
+
.catch((err) => {
|
|
66
96
|
if (taskId !== taskIdRef.current) {
|
|
67
97
|
return;
|
|
68
98
|
}
|
|
@@ -76,7 +106,7 @@ export const useRenderer = (props: UseRendererProps): UseRendererResult => {
|
|
|
76
106
|
return () => {
|
|
77
107
|
taskIdRef.current++;
|
|
78
108
|
};
|
|
79
|
-
}, [data, filters, viewDimensions, viewMeasures, defaultAggregated]);
|
|
109
|
+
}, [data, filters, viewDimensions, viewMeasures, defaultAggregated, limit]);
|
|
80
110
|
|
|
81
111
|
return useMemo(() => {
|
|
82
112
|
return {
|
package/src/renderer/index.tsx
CHANGED
|
@@ -21,7 +21,18 @@ interface RendererProps {
|
|
|
21
21
|
const Renderer = forwardRef<IReactVegaHandler, RendererProps>(function (props, ref) {
|
|
22
22
|
const { themeKey, dark } = props;
|
|
23
23
|
const { vizStore, commonStore } = useGlobalStore();
|
|
24
|
-
const {
|
|
24
|
+
const {
|
|
25
|
+
allFields,
|
|
26
|
+
viewFilters,
|
|
27
|
+
viewDimensions,
|
|
28
|
+
viewMeasures,
|
|
29
|
+
visualConfig,
|
|
30
|
+
draggableFieldState,
|
|
31
|
+
visList,
|
|
32
|
+
visIndex,
|
|
33
|
+
sort,
|
|
34
|
+
limit,
|
|
35
|
+
} = vizStore;
|
|
25
36
|
const chart = visList[visIndex];
|
|
26
37
|
const { currentDataset } = commonStore;
|
|
27
38
|
const { dataSource } = currentDataset;
|
|
@@ -37,6 +48,8 @@ const Renderer = forwardRef<IReactVegaHandler, RendererProps>(function (props, r
|
|
|
37
48
|
viewMeasures,
|
|
38
49
|
filters: viewFilters,
|
|
39
50
|
defaultAggregated: visualConfig.defaultAggregated,
|
|
51
|
+
sort,
|
|
52
|
+
limit: limit,
|
|
40
53
|
});
|
|
41
54
|
|
|
42
55
|
// Dependencies that should not trigger effect individually
|
|
@@ -64,19 +77,16 @@ const Renderer = forwardRef<IReactVegaHandler, RendererProps>(function (props, r
|
|
|
64
77
|
useChartIndexControl({
|
|
65
78
|
count: visList.length,
|
|
66
79
|
index: visIndex,
|
|
67
|
-
onChange: idx => vizStore.selectVisualization(idx),
|
|
80
|
+
onChange: (idx) => vizStore.selectVisualization(idx),
|
|
68
81
|
});
|
|
69
82
|
|
|
70
|
-
const handleGeomClick = useCallback(
|
|
71
|
-
(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
},
|
|
78
|
-
[]
|
|
79
|
-
);
|
|
83
|
+
const handleGeomClick = useCallback((values: any, e: any) => {
|
|
84
|
+
e.stopPropagation();
|
|
85
|
+
runInAction(() => {
|
|
86
|
+
commonStore.showEmbededMenu([e.pageX, e.pageY]);
|
|
87
|
+
commonStore.setFilters(values);
|
|
88
|
+
});
|
|
89
|
+
}, []);
|
|
80
90
|
|
|
81
91
|
const handleChartResize = useCallback(
|
|
82
92
|
(width: number, height: number) => {
|
|
@@ -9,7 +9,6 @@ import type { IReactVegaHandler } from '../vis/react-vega';
|
|
|
9
9
|
import SpecRenderer from './specRenderer';
|
|
10
10
|
import { useRenderer } from './hooks';
|
|
11
11
|
|
|
12
|
-
|
|
13
12
|
interface IPureRendererProps {
|
|
14
13
|
name?: string;
|
|
15
14
|
themeKey?: IThemeKey;
|
|
@@ -17,25 +16,20 @@ interface IPureRendererProps {
|
|
|
17
16
|
rawData?: IRow[];
|
|
18
17
|
visualState: DraggableFieldState;
|
|
19
18
|
visualConfig: IVisualConfig;
|
|
19
|
+
sort?: 'none' | 'ascending' | 'descending';
|
|
20
|
+
limit?: number;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Render a readonly chart with given visualization schema.
|
|
24
25
|
* This is a pure component, which means it will not depend on any global state.
|
|
25
26
|
*/
|
|
26
|
-
const PureRenderer = forwardRef<IReactVegaHandler, IPureRendererProps>(function PureRenderer
|
|
27
|
-
const {
|
|
28
|
-
name,
|
|
29
|
-
themeKey,
|
|
30
|
-
dark,
|
|
31
|
-
rawData,
|
|
32
|
-
visualState,
|
|
33
|
-
visualConfig,
|
|
34
|
-
} = props;
|
|
27
|
+
const PureRenderer = forwardRef<IReactVegaHandler, IPureRendererProps>(function PureRenderer(props, ref) {
|
|
28
|
+
const { name, themeKey, dark, rawData, visualState, visualConfig, sort, limit } = props;
|
|
35
29
|
const defaultAggregated = visualConfig?.defaultAggregated ?? false;
|
|
36
30
|
|
|
37
31
|
const [viewData, setViewData] = useState<IRow[]>([]);
|
|
38
|
-
|
|
32
|
+
|
|
39
33
|
const { allFields, viewDimensions, viewMeasures, filters } = useMemo(() => {
|
|
40
34
|
const viewDimensions: IViewField[] = [];
|
|
41
35
|
const viewMeasures: IViewField[] = [];
|
|
@@ -64,6 +58,8 @@ const PureRenderer = forwardRef<IReactVegaHandler, IPureRendererProps>(function
|
|
|
64
58
|
viewMeasures,
|
|
65
59
|
filters,
|
|
66
60
|
defaultAggregated,
|
|
61
|
+
sort: sort ?? 'none',
|
|
62
|
+
limit: limit ?? -1,
|
|
67
63
|
});
|
|
68
64
|
|
|
69
65
|
// Dependencies that should not trigger effect individually
|
package/src/services.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { toJS } from 'mobx';
|
|
2
|
-
import { IRow, IMutField, IField, IFilterField, Specification } from './interfaces';
|
|
2
|
+
import { IRow, IMutField, IField, IFilterField, Specification, IViewField } from './interfaces';
|
|
3
3
|
/* eslint import/no-webpack-loader-syntax:0 */
|
|
4
4
|
// @ts-ignore
|
|
5
5
|
// eslint-disable-next-line
|
|
@@ -11,6 +11,8 @@ import { IRow, IMutField, IField, IFilterField, Specification } from './interfac
|
|
|
11
11
|
import FilterWorker from './workers/filter.worker?worker&inline';
|
|
12
12
|
import TransformDataWorker from './workers/transform.worker?worker&inline';
|
|
13
13
|
import ViewQueryWorker from './workers/viewQuery.worker?worker&inline';
|
|
14
|
+
import SortWorker from './workers/sort.worker?worker&inline';
|
|
15
|
+
|
|
14
16
|
import { IViewQuery } from './lib/viewQuery';
|
|
15
17
|
|
|
16
18
|
// interface WorkerState {
|
|
@@ -154,7 +156,7 @@ export const transformDataService = async (data: IRow[], columns: IField[]): Pro
|
|
|
154
156
|
} finally {
|
|
155
157
|
worker.terminate();
|
|
156
158
|
}
|
|
157
|
-
}
|
|
159
|
+
};
|
|
158
160
|
|
|
159
161
|
export const applyViewQuery = async (data: IRow[], metas: IField[], query: IViewQuery): Promise<IRow[]> => {
|
|
160
162
|
const worker = new ViewQueryWorker();
|
|
@@ -170,4 +172,24 @@ export const applyViewQuery = async (data: IRow[], metas: IField[], query: IView
|
|
|
170
172
|
} finally {
|
|
171
173
|
worker.terminate();
|
|
172
174
|
}
|
|
173
|
-
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
export const applySort = async (
|
|
178
|
+
data: IRow[],
|
|
179
|
+
viewMeasures: IField[],
|
|
180
|
+
sort: 'ascending' | 'descending'
|
|
181
|
+
): Promise<IRow[]> => {
|
|
182
|
+
const worker = new SortWorker();
|
|
183
|
+
try {
|
|
184
|
+
const res: IRow[] = await workerService(worker, {
|
|
185
|
+
data,
|
|
186
|
+
viewMeasures: viewMeasures.map((x) => toJS(x)),
|
|
187
|
+
sort,
|
|
188
|
+
});
|
|
189
|
+
return res;
|
|
190
|
+
} catch (err) {
|
|
191
|
+
throw new Error('Uncaught error in ViewQueryWorker', { cause: err });
|
|
192
|
+
} finally {
|
|
193
|
+
worker.terminate();
|
|
194
|
+
}
|
|
195
|
+
};
|
|
@@ -264,7 +264,7 @@ export class VizSpecStore {
|
|
|
264
264
|
*/
|
|
265
265
|
public get viewDimensions(): IViewField[] {
|
|
266
266
|
const { draggableFieldState } = this;
|
|
267
|
-
const state = toJS(draggableFieldState);
|
|
267
|
+
const { filters, ...state } = toJS(draggableFieldState);
|
|
268
268
|
const fields: IViewField[] = [];
|
|
269
269
|
(Object.keys(state) as (keyof DraggableFieldState)[])
|
|
270
270
|
.filter((dkey) => !MetaFieldKeys.includes(dkey))
|
|
@@ -278,7 +278,7 @@ export class VizSpecStore {
|
|
|
278
278
|
*/
|
|
279
279
|
public get viewMeasures(): IViewField[] {
|
|
280
280
|
const { draggableFieldState } = this;
|
|
281
|
-
const state = toJS(draggableFieldState);
|
|
281
|
+
const { filters, ...state } = toJS(draggableFieldState);
|
|
282
282
|
const fields: IViewField[] = [];
|
|
283
283
|
(Object.keys(state) as (keyof DraggableFieldState)[])
|
|
284
284
|
.filter((dkey) => !MetaFieldKeys.includes(dkey))
|
|
@@ -771,4 +771,16 @@ export class VizSpecStore {
|
|
|
771
771
|
});
|
|
772
772
|
return updatedVisList;
|
|
773
773
|
}
|
|
774
|
+
public limit = -1;
|
|
775
|
+
|
|
776
|
+
public get sort() {
|
|
777
|
+
const { rows, columns } = this.draggableFieldState;
|
|
778
|
+
if (rows.length && !rows.find((x) => x.analyticType === 'measure')) {
|
|
779
|
+
return rows[rows.length - 1].sort || 'none';
|
|
780
|
+
}
|
|
781
|
+
if (columns.length && !columns.find((x) => x.analyticType === 'measure')) {
|
|
782
|
+
return columns[columns.length - 1].sort || 'none';
|
|
783
|
+
}
|
|
784
|
+
return 'none';
|
|
785
|
+
}
|
|
774
786
|
}
|
|
@@ -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
|
|
|
@@ -493,6 +496,24 @@ const VisualSettings: React.FC<IVisualSettings> = ({
|
|
|
493
496
|
},
|
|
494
497
|
...(extra.length === 0 ? [] : ['-', ...extra]),
|
|
495
498
|
'-',
|
|
499
|
+
{
|
|
500
|
+
key: 'limit_axis',
|
|
501
|
+
label: t('limit'),
|
|
502
|
+
icon: HashtagIcon,
|
|
503
|
+
form: (
|
|
504
|
+
<FormContainer>
|
|
505
|
+
<LimitSetting
|
|
506
|
+
value={limit}
|
|
507
|
+
setValue={(v) => {
|
|
508
|
+
runInAction(() => {
|
|
509
|
+
vizStore.limit = v;
|
|
510
|
+
});
|
|
511
|
+
}}
|
|
512
|
+
/>
|
|
513
|
+
</FormContainer>
|
|
514
|
+
),
|
|
515
|
+
},
|
|
516
|
+
'-',
|
|
496
517
|
{
|
|
497
518
|
key: 'kanaries',
|
|
498
519
|
label: 'kanaries',
|
|
@@ -532,6 +553,7 @@ const VisualSettings: React.FC<IVisualSettings> = ({
|
|
|
532
553
|
dark,
|
|
533
554
|
extra,
|
|
534
555
|
exclude,
|
|
556
|
+
limit,
|
|
535
557
|
]);
|
|
536
558
|
|
|
537
559
|
return (
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { IViewField, IRow } from '../interfaces';
|
|
2
|
+
import { getMeaAggKey } from '../utils';
|
|
3
|
+
|
|
4
|
+
function compareMulti(a: number[], b: number[]): number {
|
|
5
|
+
if (a.length < b.length) return -compareMulti(b, a);
|
|
6
|
+
for (let i = 0; i < a.length; i++) {
|
|
7
|
+
if (!b[i]) return 1;
|
|
8
|
+
const c = a[i] - b[i];
|
|
9
|
+
if (c !== 0) return c;
|
|
10
|
+
}
|
|
11
|
+
return 0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function sortBy(data: IRow[], viewMeasures: IViewField[], sort: 'ascending' | 'descending') {
|
|
15
|
+
const sortM = sort === 'ascending' ? 1 : -1;
|
|
16
|
+
return data
|
|
17
|
+
.map((x) => ({
|
|
18
|
+
data: x,
|
|
19
|
+
value: viewMeasures.map((f) => x[getMeaAggKey(f.fid, f.aggName)]),
|
|
20
|
+
}))
|
|
21
|
+
.sort((a, b) => sortM * compareMulti(a.value, b.value))
|
|
22
|
+
.map((x) => x.data);
|
|
23
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { IRow, IViewField } from '../interfaces';
|
|
2
|
+
import { sortBy } from './sort';
|
|
3
|
+
|
|
4
|
+
const main = (e: {
|
|
5
|
+
data: {
|
|
6
|
+
data: IRow[];
|
|
7
|
+
viewMeasures: IViewField[];
|
|
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);
|