@kanaries/graphic-walker 0.4.0 → 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.
- package/dist/components/dataTable/index.d.ts +2 -1
- package/dist/graphic-walker.es.js +10338 -10328
- package/dist/graphic-walker.es.js.map +1 -1
- package/dist/graphic-walker.umd.js +113 -113
- package/dist/graphic-walker.umd.js.map +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/package.json +1 -1
- package/src/components/askViz/index.tsx +2 -1
- package/src/components/dataTable/index.tsx +7 -5
- package/src/components/limitSetting.tsx +8 -6
- package/src/components/sizeSetting.tsx +9 -7
- package/src/computation/serverComputation.ts +8 -3
- package/src/dataSource/table.tsx +4 -0
- package/src/hooks/index.ts +8 -0
- package/src/renderer/hooks.ts +1 -47
- package/src/store/visualSpecStore.ts +2 -1
- package/src/visualSettings/index.tsx +0 -1
package/dist/hooks/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -5,6 +5,7 @@ import { PaperAirplaneIcon } from '@heroicons/react/24/outline';
|
|
|
5
5
|
import Spinner from '../spinner';
|
|
6
6
|
import { IViewField } from '../../interfaces';
|
|
7
7
|
import { VisSpecWithHistory } from '../../models/visSpecHistory';
|
|
8
|
+
import { visSpecDecoder, forwardVisualConfigs } from '../../utils/save';
|
|
8
9
|
|
|
9
10
|
type VEGALite = any;
|
|
10
11
|
|
|
@@ -51,7 +52,7 @@ const AskViz: React.FC<{api?: string; headers?: Record<string, string>}> = (prop
|
|
|
51
52
|
setLoading(true);
|
|
52
53
|
vizQuery(props.api ?? api, allFields, query, props.headers ?? {})
|
|
53
54
|
.then((data) => {
|
|
54
|
-
vizStore.visList.push(new VisSpecWithHistory(data));
|
|
55
|
+
vizStore.visList.push(new VisSpecWithHistory(visSpecDecoder(forwardVisualConfigs([data]))[0]));
|
|
55
56
|
vizStore.selectVisualization(vizStore.visList.length - 1);
|
|
56
57
|
// const liteGW = parseGW(spec);
|
|
57
58
|
// vizStore.renderSpec(liteGW);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useMemo, useState, useRef, useEffect } from 'react';
|
|
2
2
|
import styled from 'styled-components';
|
|
3
3
|
import { observer } from 'mobx-react-lite';
|
|
4
|
-
import type { IMutField, IRow, DataSet } from '../../interfaces';
|
|
4
|
+
import type { IMutField, IRow, DataSet, IComputationFunction } from '../../interfaces';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
import LoadingLayer from "../loadingLayer";
|
|
7
7
|
import { useComputationFunc } from "../../renderer/hooks";
|
|
@@ -16,6 +16,7 @@ interface DataTableProps {
|
|
|
16
16
|
/** total count of rows */
|
|
17
17
|
total: number;
|
|
18
18
|
dataset: DataSet;
|
|
19
|
+
computation?: IComputationFunction;
|
|
19
20
|
onMetaChange: (fid: string, fIndex: number, meta: Partial<IMutField>) => void;
|
|
20
21
|
loading?: boolean;
|
|
21
22
|
}
|
|
@@ -117,10 +118,11 @@ const getHeaderKey = (f: wrapMutField) => {
|
|
|
117
118
|
};
|
|
118
119
|
|
|
119
120
|
const DataTable: React.FC<DataTableProps> = (props) => {
|
|
120
|
-
const { size = 10, onMetaChange, dataset, total, loading: statLoading } = props;
|
|
121
|
+
const { size = 10, onMetaChange, dataset, computation, total, loading: statLoading } = props;
|
|
121
122
|
const [pageIndex, setPageIndex] = useState(0);
|
|
122
123
|
const { t } = useTranslation();
|
|
123
|
-
const
|
|
124
|
+
const defaultComputation = useComputationFunc();
|
|
125
|
+
const computationFunction = computation ?? defaultComputation;
|
|
124
126
|
|
|
125
127
|
const analyticTypeList = useMemo<{ value: string; label: string }[]>(() => {
|
|
126
128
|
return ANALYTIC_TYPE_LIST.map((at) => ({
|
|
@@ -149,7 +151,7 @@ const DataTable: React.FC<DataTableProps> = (props) => {
|
|
|
149
151
|
}
|
|
150
152
|
setDataLoading(true);
|
|
151
153
|
const taskId = ++taskIdRef.current;
|
|
152
|
-
dataReadRawServer(
|
|
154
|
+
dataReadRawServer(computationFunction, size, pageIndex).then(data => {
|
|
153
155
|
if (taskId === taskIdRef.current) {
|
|
154
156
|
setDataLoading(false);
|
|
155
157
|
setRows(data);
|
|
@@ -164,7 +166,7 @@ const DataTable: React.FC<DataTableProps> = (props) => {
|
|
|
164
166
|
return () => {
|
|
165
167
|
taskIdRef.current++;
|
|
166
168
|
};
|
|
167
|
-
}, [
|
|
169
|
+
}, [computationFunction, pageIndex, size]);
|
|
168
170
|
|
|
169
171
|
const loading = statLoading || dataLoading;
|
|
170
172
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { useDebounceValueBind } from '../hooks';
|
|
3
4
|
|
|
4
5
|
export default function LimitSetting(props: { value: number; setValue: (v: number) => void }) {
|
|
5
6
|
const { t } = useTranslation('translation', { keyPrefix: 'main.tabpanel.settings' });
|
|
7
|
+
const [innerValue, setInnerValue] = useDebounceValueBind(props.value, v => props.setValue(v));
|
|
6
8
|
|
|
7
9
|
return (
|
|
8
10
|
<div className=" mt-2">
|
|
@@ -10,15 +12,15 @@ export default function LimitSetting(props: { value: number; setValue: (v: numbe
|
|
|
10
12
|
className="w-full h-2 bg-blue-100 appearance-none"
|
|
11
13
|
type="range"
|
|
12
14
|
name="limit"
|
|
13
|
-
value={
|
|
15
|
+
value={innerValue > 0 ? innerValue : 0}
|
|
14
16
|
min="1"
|
|
15
17
|
max="50"
|
|
16
|
-
disabled={
|
|
18
|
+
disabled={innerValue < 0}
|
|
17
19
|
step="1"
|
|
18
20
|
onChange={(e) => {
|
|
19
21
|
const v = parseInt(e.target.value);
|
|
20
22
|
if (!isNaN(v)) {
|
|
21
|
-
|
|
23
|
+
setInnerValue(v);
|
|
22
24
|
}
|
|
23
25
|
}}
|
|
24
26
|
/>
|
|
@@ -26,12 +28,12 @@ export default function LimitSetting(props: { value: number; setValue: (v: numbe
|
|
|
26
28
|
<input
|
|
27
29
|
type="checkbox"
|
|
28
30
|
className="h-4 w-4 mr-1 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600"
|
|
29
|
-
checked={
|
|
31
|
+
checked={innerValue > 0}
|
|
30
32
|
onChange={(e) => {
|
|
31
|
-
|
|
33
|
+
setInnerValue(e.target.checked ? 30 : -1);
|
|
32
34
|
}}
|
|
33
35
|
></input>
|
|
34
|
-
{`${t('limit')}${
|
|
36
|
+
{`${t('limit')}${innerValue > 0 ? `: ${innerValue}` : ''}`}
|
|
35
37
|
</output>
|
|
36
38
|
</div>
|
|
37
39
|
);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ArrowsPointingOutIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
|
2
2
|
import React, { useState, useEffect } from "react";
|
|
3
3
|
import { useTranslation } from "react-i18next";
|
|
4
|
+
import { useDebounceValueBind } from "../hooks";
|
|
4
5
|
|
|
5
6
|
interface SizeSettingProps {
|
|
6
7
|
onWidthChange: (val: number) => void;
|
|
@@ -12,7 +13,8 @@ interface SizeSettingProps {
|
|
|
12
13
|
export const ResizeDialog: React.FC<SizeSettingProps> = (props) => {
|
|
13
14
|
const { onWidthChange, onHeightChange, width, height, children } = props;
|
|
14
15
|
const { t } = useTranslation("translation", { keyPrefix: "main.tabpanel.settings.size_setting" });
|
|
15
|
-
|
|
16
|
+
const [innerWidth, setInnerWidth] = useDebounceValueBind(width, onWidthChange);
|
|
17
|
+
const [innerHeight, setInnerHeight] = useDebounceValueBind(height, onHeightChange);
|
|
16
18
|
return (
|
|
17
19
|
<div className="text-zinc-400">
|
|
18
20
|
{children}
|
|
@@ -22,16 +24,16 @@ export const ResizeDialog: React.FC<SizeSettingProps> = (props) => {
|
|
|
22
24
|
style={{ cursor: "ew-resize" }}
|
|
23
25
|
type="range"
|
|
24
26
|
name="width"
|
|
25
|
-
value={Math.sqrt(
|
|
27
|
+
value={Math.sqrt(innerWidth / 1000)}
|
|
26
28
|
min="0"
|
|
27
29
|
max="1"
|
|
28
30
|
step="0.01"
|
|
29
31
|
onChange={(e) => {
|
|
30
|
-
|
|
32
|
+
setInnerWidth(Math.round(Number(e.target.value) ** 2 * 1000));
|
|
31
33
|
}}
|
|
32
34
|
/>
|
|
33
35
|
<output className="text-sm ml-1" htmlFor="width">
|
|
34
|
-
{`${t("width")}: ${
|
|
36
|
+
{`${t("width")}: ${innerWidth}`}
|
|
35
37
|
</output>
|
|
36
38
|
</div>
|
|
37
39
|
<div className=" mt-2">
|
|
@@ -40,16 +42,16 @@ export const ResizeDialog: React.FC<SizeSettingProps> = (props) => {
|
|
|
40
42
|
style={{ cursor: "ew-resize" }}
|
|
41
43
|
type="range"
|
|
42
44
|
name="height"
|
|
43
|
-
value={Math.sqrt(
|
|
45
|
+
value={Math.sqrt(innerHeight / 1000)}
|
|
44
46
|
min="0"
|
|
45
47
|
max="1"
|
|
46
48
|
step="0.01"
|
|
47
49
|
onChange={(e) => {
|
|
48
|
-
|
|
50
|
+
setInnerHeight(Math.round(Number(e.target.value) ** 2 * 1000));
|
|
49
51
|
}}
|
|
50
52
|
/>
|
|
51
53
|
<output className="text-sm ml-1" htmlFor="height">
|
|
52
|
-
{`${t("height")}: ${
|
|
54
|
+
{`${t("height")}: ${innerHeight}`}
|
|
53
55
|
</output>
|
|
54
56
|
</div>
|
|
55
57
|
</div>
|
|
@@ -29,7 +29,7 @@ export const datasetStatsServer = async (service: IComputationFunction): Promise
|
|
|
29
29
|
],
|
|
30
30
|
})) as [{ count: number }];
|
|
31
31
|
return {
|
|
32
|
-
rowCount: res[0]
|
|
32
|
+
rowCount: res[0]?.count ?? 0,
|
|
33
33
|
};
|
|
34
34
|
};
|
|
35
35
|
|
|
@@ -72,7 +72,7 @@ export const dataQueryServer = async (
|
|
|
72
72
|
}
|
|
73
73
|
const res = await service({
|
|
74
74
|
workflow,
|
|
75
|
-
limit
|
|
75
|
+
limit,
|
|
76
76
|
});
|
|
77
77
|
return res;
|
|
78
78
|
};
|
|
@@ -132,7 +132,12 @@ export const fieldStatServer = async (
|
|
|
132
132
|
},
|
|
133
133
|
],
|
|
134
134
|
};
|
|
135
|
-
const [
|
|
135
|
+
const [
|
|
136
|
+
rangeRes = {
|
|
137
|
+
[MIN_ID]: 0,
|
|
138
|
+
[MAX_ID]: 0,
|
|
139
|
+
},
|
|
140
|
+
] = range
|
|
136
141
|
? await service(rangeQueryPayload)
|
|
137
142
|
: [
|
|
138
143
|
{
|
package/src/dataSource/table.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import { observer } from "mobx-react-lite";
|
|
|
3
3
|
import { useGlobalStore } from "../store";
|
|
4
4
|
import DataTable from "../components/dataTable";
|
|
5
5
|
import { toJS } from "mobx";
|
|
6
|
+
import { getComputation } from "../computation/clientComputation";
|
|
6
7
|
|
|
7
8
|
interface TableProps {
|
|
8
9
|
size?: number;
|
|
@@ -21,11 +22,14 @@ const Table: React.FC<TableProps> = (props) => {
|
|
|
21
22
|
rawFields: toJS(tmpDSRawFields),
|
|
22
23
|
};
|
|
23
24
|
}, [tmpDataSource, tmpDSRawFields]);
|
|
25
|
+
9
|
|
26
|
+
const computation = React.useMemo(() => getComputation(tempDataset.dataSource), [tempDataset])
|
|
24
27
|
|
|
25
28
|
return (
|
|
26
29
|
<DataTable
|
|
27
30
|
size={size}
|
|
28
31
|
dataset={tempDataset}
|
|
32
|
+
computation={computation}
|
|
29
33
|
total={tmpDataSource.length}
|
|
30
34
|
onMetaChange={(fid, fIndex, diffMeta) => {
|
|
31
35
|
commonStore.updateTempDatasetMetas(fid, diffMeta);
|
package/src/hooks/index.ts
CHANGED
|
@@ -8,3 +8,11 @@ export function useDebounceValue<T>(value: T, timeout = 200): T {
|
|
|
8
8
|
}, [value]);
|
|
9
9
|
return innerValue;
|
|
10
10
|
}
|
|
11
|
+
|
|
12
|
+
export function useDebounceValueBind<T>(value: T, setter: (v: T) => void, timeout = 200): [T, (v: T) => void] {
|
|
13
|
+
const [innerValue, setInnerValue] = useState(value);
|
|
14
|
+
const valueToSet = useDebounceValue(innerValue, timeout);
|
|
15
|
+
useEffect(() => setInnerValue(value), [value])
|
|
16
|
+
useEffect(() => setter(valueToSet), [valueToSet]);
|
|
17
|
+
return [innerValue, setInnerValue];
|
|
18
|
+
}
|
package/src/renderer/hooks.ts
CHANGED
|
@@ -4,9 +4,7 @@ import type { DeepReadonly, IFilterField, IRow, IViewField, IDataQueryWorkflowSt
|
|
|
4
4
|
import { useGlobalStore } from '../store';
|
|
5
5
|
import { useAppRootContext } from '../components/appRoot';
|
|
6
6
|
import { toWorkflow } from '../utils/workflow';
|
|
7
|
-
import { dataQueryClient } from '../computation/clientComputation';
|
|
8
7
|
import { dataQueryServer } from '../computation/serverComputation';
|
|
9
|
-
import { useDebounceValue } from '../hooks';
|
|
10
8
|
|
|
11
9
|
export const useComputationFunc = (): IComputationFunction => {
|
|
12
10
|
const { vizStore } = useGlobalStore();
|
|
@@ -40,14 +38,12 @@ export const useRenderer = (props: UseRendererProps): UseRendererResult => {
|
|
|
40
38
|
filters,
|
|
41
39
|
defaultAggregated,
|
|
42
40
|
sort,
|
|
43
|
-
limit
|
|
41
|
+
limit,
|
|
44
42
|
computationFunction,
|
|
45
43
|
} = props;
|
|
46
44
|
const [computing, setComputing] = useState(false);
|
|
47
45
|
const taskIdRef = useRef(0);
|
|
48
46
|
|
|
49
|
-
const limit = useDebounceValue(storeLimit);
|
|
50
|
-
|
|
51
47
|
const workflow = useMemo(() => {
|
|
52
48
|
return toWorkflow(
|
|
53
49
|
filters,
|
|
@@ -65,48 +61,6 @@ export const useRenderer = (props: UseRendererProps): UseRendererResult => {
|
|
|
65
61
|
|
|
66
62
|
const appRef = useAppRootContext();
|
|
67
63
|
|
|
68
|
-
// useEffect(() => {
|
|
69
|
-
// if (computationMode !== 'client') {
|
|
70
|
-
// return;
|
|
71
|
-
// }
|
|
72
|
-
// if (!data) {
|
|
73
|
-
// console.warn('useRenderer error: prop `data` is required for "client" mode, but none is found.');
|
|
74
|
-
// return;
|
|
75
|
-
// }
|
|
76
|
-
// if (!allFields) {
|
|
77
|
-
// console.warn('useRenderer error: prop `fields` is required for "client" mode, but none is found.');
|
|
78
|
-
// return;
|
|
79
|
-
// }
|
|
80
|
-
// const taskId = ++taskIdRef.current;
|
|
81
|
-
// appRef.current?.updateRenderStatus('computing');
|
|
82
|
-
// setComputing(true);
|
|
83
|
-
// dataQueryClient(data, allFields, workflow).then(data => {
|
|
84
|
-
// if (taskId !== taskIdRef.current) {
|
|
85
|
-
// return;
|
|
86
|
-
// }
|
|
87
|
-
// appRef.current?.updateRenderStatus('rendering');
|
|
88
|
-
// unstable_batchedUpdates(() => {
|
|
89
|
-
// setComputing(false);
|
|
90
|
-
// setViewData(data);
|
|
91
|
-
// setParsedWorkflow(workflow);
|
|
92
|
-
// });
|
|
93
|
-
// }).catch((err) => {
|
|
94
|
-
// if (taskId !== taskIdRef.current) {
|
|
95
|
-
// return;
|
|
96
|
-
// }
|
|
97
|
-
// appRef.current?.updateRenderStatus('error');
|
|
98
|
-
// console.error(err);
|
|
99
|
-
// unstable_batchedUpdates(() => {
|
|
100
|
-
// setComputing(false);
|
|
101
|
-
// setViewData([]);
|
|
102
|
-
// setParsedWorkflow([]);
|
|
103
|
-
// });
|
|
104
|
-
// });
|
|
105
|
-
// return () => {
|
|
106
|
-
// taskIdRef.current++;
|
|
107
|
-
// };
|
|
108
|
-
// }, [computationMode, data, allFields, workflow]);
|
|
109
|
-
|
|
110
64
|
useEffect(() => {
|
|
111
65
|
const taskId = ++taskIdRef.current;
|
|
112
66
|
appRef.current?.updateRenderStatus('computing');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IReactionDisposer, makeAutoObservable, observable, reaction, toJS } from 'mobx';
|
|
1
|
+
import { IReactionDisposer, makeAutoObservable, observable, computed, reaction, toJS } from 'mobx';
|
|
2
2
|
import produce from 'immer';
|
|
3
3
|
import {
|
|
4
4
|
DataSet,
|
|
@@ -395,6 +395,7 @@ export class VizSpecStore {
|
|
|
395
395
|
case configKey === 'sorted':
|
|
396
396
|
case configKey === 'zeroScale':
|
|
397
397
|
case configKey === 'background':
|
|
398
|
+
case configKey === 'resolve':
|
|
398
399
|
case configKey === 'limit':
|
|
399
400
|
case configKey === 'stack': {
|
|
400
401
|
return (config[configKey] = value);
|
|
@@ -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);
|