@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kanaries/graphic-walker",
3
- "version": "0.3.16",
3
+ "version": "0.4.1",
4
4
  "scripts": {
5
5
  "dev:front_end": "vite --host",
6
6
  "dev": "npm run dev:front_end",
@@ -29,7 +29,7 @@
29
29
  },
30
30
  "prettier": {
31
31
  "tabWidth": 4,
32
- "printWidth": 120,
32
+ "printWidth": 160,
33
33
  "singleQuote": true
34
34
  },
35
35
  "types": "./dist/index.d.ts",
package/src/App.tsx CHANGED
@@ -1,7 +1,7 @@
1
- import React, { useEffect, useRef, useMemo } from 'react';
1
+ import React, { useEffect, useRef, useMemo, useState } from 'react';
2
2
  import { observer } from 'mobx-react-lite';
3
3
  import { useTranslation } from 'react-i18next';
4
- import { IDarkMode, IMutField, IRow, ISegmentKey, IThemeKey, Specification } from './interfaces';
4
+ import { IComputationFunction, IDarkMode, IMutField, IRow, ISegmentKey, IThemeKey, Specification } from './interfaces';
5
5
  import type { IReactVegaHandler } from './vis/react-vega';
6
6
  import VisualSettings from './visualSettings';
7
7
  import PosFields from './fields/posFields';
@@ -20,6 +20,8 @@ import { useCurrentMediaTheme } from './utils/media';
20
20
  import CodeExport from './components/codeExport';
21
21
  import VisualConfig from './components/visualConfig';
22
22
  import type { ToolbarItemProps } from './components/toolbar';
23
+ import AskViz from './components/askViz';
24
+ import { getComputation } from './computation/clientComputation';
23
25
 
24
26
  export interface IGWProps {
25
27
  dataSource?: IRow[];
@@ -28,7 +30,7 @@ export interface IGWProps {
28
30
  hideDataSourceConfig?: boolean;
29
31
  i18nLang?: string;
30
32
  i18nResources?: { [lang: string]: Record<string, string | any> };
31
- keepAlive?: boolean;
33
+ keepAlive?: boolean | string;
32
34
  /**
33
35
  * auto parse field key into a safe string. default is true
34
36
  */
@@ -37,10 +39,17 @@ export interface IGWProps {
37
39
  themeKey?: IThemeKey;
38
40
  dark?: IDarkMode;
39
41
  storeRef?: React.MutableRefObject<IGlobalStore | null>;
42
+ computation?: IComputationFunction;
40
43
  toolbar?: {
41
44
  extra?: ToolbarItemProps[];
42
45
  exclude?: string[];
43
46
  };
47
+ enhanceAPI?: {
48
+ header?: Record<string, string>;
49
+ features?: {
50
+ askviz?: string | boolean;
51
+ }
52
+ };
44
53
  }
45
54
 
46
55
  const App = observer<IGWProps>(function App(props) {
@@ -54,7 +63,9 @@ const App = observer<IGWProps>(function App(props) {
54
63
  fieldKeyGuard = true,
55
64
  themeKey = 'vega',
56
65
  dark = 'media',
66
+ computation,
57
67
  toolbar,
68
+ enhanceAPI,
58
69
  } = props;
59
70
  const { commonStore, vizStore } = useGlobalStore();
60
71
 
@@ -75,11 +86,28 @@ const App = observer<IGWProps>(function App(props) {
75
86
  }
76
87
  }, [i18nLang, curLang]);
77
88
 
89
+ const [sampleRemoteData, setSampleRemoteData] = useState<IRow[]>([]);
90
+ const remoteDataContext = useRef(0);
91
+
92
+ useEffect(() => {
93
+ if (!computation) return;
94
+ async () => {
95
+ const ts = Date.now();
96
+ remoteDataContext.current = ts;
97
+ const resp = await computation({ workflow: [{ type: 'view', query: [{ op: 'raw', fields: ['*'] }] }], limit: 1 });
98
+ if (remoteDataContext.current === ts) {
99
+ setSampleRemoteData(resp);
100
+ }
101
+ };
102
+ }, [computation]);
103
+
104
+ const remoteDataSource = dataSource.length > 0 ? dataSource : sampleRemoteData;
105
+
78
106
  const safeDataset = useMemo(() => {
79
- let safeData = dataSource;
107
+ let safeData = remoteDataSource;
80
108
  let safeMetas = rawFields;
81
109
  if (fieldKeyGuard) {
82
- const { safeData: _safeData, safeMetas: _safeMetas } = guardDataKeys(dataSource, rawFields);
110
+ const { safeData: _safeData, safeMetas: _safeMetas } = guardDataKeys(remoteDataSource, rawFields);
83
111
  safeData = _safeData;
84
112
  safeMetas = _safeMetas;
85
113
  }
@@ -87,7 +115,7 @@ const App = observer<IGWProps>(function App(props) {
87
115
  safeData,
88
116
  safeMetas,
89
117
  };
90
- }, [rawFields, dataSource, fieldKeyGuard]);
118
+ }, [rawFields, remoteDataSource, computation, fieldKeyGuard]);
91
119
 
92
120
  // use as an embeding module, use outside datasource from props.
93
121
  useEffect(() => {
@@ -106,6 +134,14 @@ const App = observer<IGWProps>(function App(props) {
106
134
  }
107
135
  }, [spec, safeDataset]);
108
136
 
137
+ useEffect(() => {
138
+ if (computation) {
139
+ vizStore.setComputationFunction(computation);
140
+ } else {
141
+ vizStore.setComputationFunction(getComputation(commonStore.currentDataset.dataSource));
142
+ }
143
+ }, [vizStore, computation ?? commonStore.currentDataset.dataSource]);
144
+
109
145
  const darkMode = useCurrentMediaTheme(dark);
110
146
 
111
147
  const rendererRef = useRef<IReactVegaHandler>(null);
@@ -130,6 +166,9 @@ const App = observer<IGWProps>(function App(props) {
130
166
  style={{ marginTop: '0em', borderTop: 'none' }}
131
167
  className="m-4 p-4 border border-gray-200 dark:border-gray-700"
132
168
  >
169
+ {enhanceAPI?.features?.askviz && (
170
+ <AskViz api={typeof enhanceAPI.features.askviz === 'string' ? enhanceAPI.features.askviz : ''} headers={enhanceAPI?.header} />
171
+ )}
133
172
  <VisualSettings rendererHandler={rendererRef} darkModePreference={dark} exclude={toolbar?.exclude} extra={toolbar?.extra} />
134
173
  <CodeExport />
135
174
  <VisualConfig />
@@ -156,7 +195,7 @@ const App = observer<IGWProps>(function App(props) {
156
195
  // }}
157
196
  >
158
197
  {datasets.length > 0 && (
159
- <ReactiveRenderer ref={rendererRef} themeKey={themeKey} dark={dark} />
198
+ <ReactiveRenderer ref={rendererRef} themeKey={themeKey} dark={dark} computationFunction={vizStore.computationFuction} />
160
199
  )}
161
200
  {/* {vizEmbededMenu.show && (
162
201
  <ClickMenu x={vizEmbededMenu.position[0]} y={vizEmbededMenu.position[1]}>
@@ -0,0 +1,93 @@
1
+ import { observer } from 'mobx-react-lite';
2
+ import React, { useCallback, useState } from 'react';
3
+ import { useGlobalStore } from '../../store';
4
+ import { PaperAirplaneIcon } from '@heroicons/react/24/outline';
5
+ import Spinner from '../spinner';
6
+ import { IViewField } from '../../interfaces';
7
+ import { VisSpecWithHistory } from '../../models/visSpecHistory';
8
+ import { visSpecDecoder, forwardVisualConfigs } from '../../utils/save';
9
+
10
+ type VEGALite = any;
11
+
12
+ const api = import.meta.env.DEV ? 'http://localhost:2023/api/vis/text2gw' : 'https://enhanceai.kanaries.net/api/vis/text2gw'
13
+
14
+ async function vizQuery(api: string, metas: IViewField[], query: string, headers: Record<string, string>) {
15
+ const res = await fetch(api, {
16
+ headers: {
17
+ 'Content-Type': 'application/json',
18
+ ...headers,
19
+ },
20
+ credentials: 'include',
21
+ method: 'POST',
22
+ body: JSON.stringify({
23
+ metas,
24
+ messages: [
25
+ {
26
+ role: 'user',
27
+ content: query,
28
+ },
29
+ ],
30
+ }),
31
+ });
32
+ const result: {
33
+ success: boolean;
34
+ data: VEGALite;
35
+ message?: string;
36
+ } = await res.json();
37
+ if (result.success) {
38
+ return result.data;
39
+ } else {
40
+ throw new Error(result.message);
41
+ }
42
+ }
43
+
44
+ const AskViz: React.FC<{api?: string; headers?: Record<string, string>}> = (props) => {
45
+ const [query, setQuery] = useState<string>('');
46
+ const [loading, setLoading] = useState<boolean>(false);
47
+ const { vizStore } = useGlobalStore();
48
+
49
+ const allFields = vizStore.allFields;
50
+
51
+ const startQuery = useCallback(() => {
52
+ setLoading(true);
53
+ vizQuery(props.api ?? api, allFields, query, props.headers ?? {})
54
+ .then((data) => {
55
+ vizStore.visList.push(new VisSpecWithHistory(visSpecDecoder(forwardVisualConfigs([data]))[0]));
56
+ vizStore.selectVisualization(vizStore.visList.length - 1);
57
+ // const liteGW = parseGW(spec);
58
+ // vizStore.renderSpec(liteGW);
59
+ })
60
+ .finally(() => {
61
+ setLoading(false);
62
+ });
63
+ }, [query, allFields]);
64
+ return (
65
+ <div className="right-0 flex">
66
+ <input
67
+ type="text"
68
+ className="rounded-l-md px-4 block w-full border-0 py-1.5 text-gray-900 dark:text-gray-50 shadow-sm ring-1 ring-inset ring-gray-300 dark:ring-gray-600 placeholder:text-gray-400 focus:ring-1 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6 dark:bg-gray-900"
69
+ placeholder="What visualization your want to draw from the dataset"
70
+ value={query}
71
+ onChange={(e) => setQuery(e.target.value)}
72
+ onKeyDown={(e) => {
73
+ if (e.key === 'Enter' && loading === false && query.length > 0) {
74
+ startQuery();
75
+ }
76
+ }}
77
+ disabled={loading || allFields.length === 0}
78
+ />
79
+ <button
80
+ type="button"
81
+ className="flex items-center grow-0 rounded-r-md bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500 px-4 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 disabled:opacity-50 disabled:cursor-not-allowed"
82
+ disabled={loading || query.length === 0 || allFields.length === 0}
83
+ onClick={startQuery}
84
+ >
85
+ Ask
86
+ {!loading && <PaperAirplaneIcon className="w-4 ml-1" />}
87
+ {loading && <Spinner />}
88
+ </button>
89
+ </div>
90
+ );
91
+ };
92
+
93
+ export default observer(AskViz);
@@ -0,0 +1,38 @@
1
+ import { Specification } from "../../interfaces";
2
+
3
+ export function parseGW(vlSpec: any): Specification {
4
+ const spec: Specification = {};
5
+ if (vlSpec.encoding && vlSpec.mark) {
6
+ spec.geomType = [vlSpec.mark];
7
+ spec.position = [];
8
+ if (vlSpec.encoding.x && vlSpec.encoding.x.field) {
9
+ spec.position.push(vlSpec.encoding.x.field);
10
+ if (vlSpec.encoding.x.aggregate) {
11
+ spec.aggregate = true;
12
+ }
13
+ }
14
+ if (vlSpec.encoding.y && vlSpec.encoding.y.field) {
15
+ spec.position.push(vlSpec.encoding.y.field);
16
+ if (vlSpec.encoding.y.aggregate) {
17
+ spec.aggregate = true;
18
+ }
19
+ }
20
+ spec.facets = [];
21
+ if (vlSpec.encoding.row && vlSpec.encoding.row) {
22
+ spec.facets.push(vlSpec.encoding.row);
23
+ }
24
+ if (vlSpec.encoding.column && vlSpec.encoding.column) {
25
+ spec.facets.push(vlSpec.encoding.column);
26
+ }
27
+
28
+ ['color', 'opacity', 'shape', 'size'].forEach((ch) => {
29
+ if (vlSpec.encoding[ch] && vlSpec.encoding[ch].field) {
30
+ spec[ch] = [vlSpec.encoding[ch].field];
31
+ if (vlSpec.encoding[ch].aggregate) {
32
+ spec.aggregate = true;
33
+ }
34
+ }
35
+ });
36
+ }
37
+ return spec;
38
+ }
@@ -1,16 +1,24 @@
1
- import React, { useMemo, useState } from 'react';
1
+ import React, { useMemo, useState, useRef, useEffect } from 'react';
2
2
  import styled from 'styled-components';
3
- import { IMutField, IRow } from '../../interfaces';
3
+ import { observer } from 'mobx-react-lite';
4
+ import type { IMutField, IRow, DataSet, IComputationFunction } from '../../interfaces';
4
5
  import { useTranslation } from 'react-i18next';
6
+ import LoadingLayer from "../loadingLayer";
7
+ import { useComputationFunc } from "../../renderer/hooks";
8
+ import { dataReadRawServer } from "../../computation/serverComputation";
5
9
  import Pagination from './pagination';
6
10
  import { ChevronUpDownIcon } from '@heroicons/react/24/outline';
7
11
  import DropdownContext from '../dropdownContext';
8
12
 
9
13
  interface DataTableProps {
14
+ /** page limit */
10
15
  size?: number;
11
- metas: IMutField[];
12
- data: IRow[];
16
+ /** total count of rows */
17
+ total: number;
18
+ dataset: DataSet;
19
+ computation?: IComputationFunction;
13
20
  onMetaChange: (fid: string, fIndex: number, meta: Partial<IMutField>) => void;
21
+ loading?: boolean;
14
22
  }
15
23
  const Container = styled.div`
16
24
  overflow-x: auto;
@@ -110,9 +118,11 @@ const getHeaderKey = (f: wrapMutField) => {
110
118
  };
111
119
 
112
120
  const DataTable: React.FC<DataTableProps> = (props) => {
113
- const { size = 10, data, metas, onMetaChange } = props;
121
+ const { size = 10, onMetaChange, dataset, computation, total, loading: statLoading } = props;
114
122
  const [pageIndex, setPageIndex] = useState(0);
115
123
  const { t } = useTranslation();
124
+ const defaultComputation = useComputationFunc();
125
+ const computationFunction = computation ?? defaultComputation;
116
126
 
117
127
  const analyticTypeList = useMemo<{ value: string; label: string }[]>(() => {
118
128
  return ANALYTIC_TYPE_LIST.map((at) => ({
@@ -129,18 +139,49 @@ const DataTable: React.FC<DataTableProps> = (props) => {
129
139
  }, []);
130
140
 
131
141
  const from = pageIndex * size;
132
- const to = Math.min((pageIndex + 1) * size, data.length - 1);
142
+ const to = Math.min((pageIndex + 1) * size - 1, total - 1);
143
+
144
+ const [rows, setRows] = useState<IRow[]>([]);
145
+ const [dataLoading, setDataLoading] = useState(false);
146
+ const taskIdRef = useRef(0);
147
+
148
+ useEffect(() => {
149
+ if (statLoading) {
150
+ return;
151
+ }
152
+ setDataLoading(true);
153
+ const taskId = ++taskIdRef.current;
154
+ dataReadRawServer(computationFunction, size, pageIndex).then(data => {
155
+ if (taskId === taskIdRef.current) {
156
+ setDataLoading(false);
157
+ setRows(data);
158
+ }
159
+ }).catch(err => {
160
+ if (taskId === taskIdRef.current) {
161
+ console.error(err);
162
+ setDataLoading(false);
163
+ setRows([]);
164
+ }
165
+ });
166
+ return () => {
167
+ taskIdRef.current++;
168
+ };
169
+ }, [computationFunction, pageIndex, size]);
170
+
171
+ const loading = statLoading || dataLoading;
172
+
173
+ const metas = dataset.rawFields;
133
174
 
134
175
  const headers = useMemo(() => getHeaders(metas), [metas]);
135
176
 
136
177
  return (
137
- <Container className="rounded border-gray-200 dark:border-gray-700 border">
178
+ <Container className="rounded border-gray-200 dark:border-gray-700 border relative">
138
179
  <Pagination
139
- total={data.length}
180
+ total={total}
140
181
  from={from + 1}
141
182
  to={to + 1}
142
183
  onNext={() => {
143
- setPageIndex(Math.min(Math.ceil(data.length / size) - 1, pageIndex + 1));
184
+ setPageIndex(Math.min(Math.ceil(total / size) - 1, pageIndex + 1));
144
185
  }}
145
186
  onPrev={() => {
146
187
  setPageIndex(Math.max(0, pageIndex - 1));
@@ -227,7 +268,7 @@ const DataTable: React.FC<DataTableProps> = (props) => {
227
268
  ))}
228
269
  </thead>
229
270
  <tbody className="divide-y divide-gray-100 dark:divide-gray-700 bg-white dark:bg-zinc-900">
230
- {data.slice(from, to + 1).map((row, index) => (
271
+ {rows.map((row, index) => (
231
272
  <tr
232
273
  className={
233
274
  'divide-x divide-gray-200 dark:divide-gray-700 ' +
@@ -250,8 +291,9 @@ const DataTable: React.FC<DataTableProps> = (props) => {
250
291
  ))}
251
292
  </tbody>
252
293
  </table>
294
+ {loading && <LoadingLayer />}
253
295
  </Container>
254
296
  );
255
297
  };
256
298
 
257
- export default DataTable;
299
+ export default observer(DataTable);
@@ -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={props.value > 0 ? props.value : 0}
15
+ value={innerValue > 0 ? innerValue : 0}
14
16
  min="1"
15
17
  max="50"
16
- disabled={props.value < 0}
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
- props.setValue(v);
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={props.value > 0}
31
+ checked={innerValue > 0}
30
32
  onChange={(e) => {
31
- props.setValue(e.target.checked ? 30 : -1);
33
+ setInnerValue(e.target.checked ? 30 : -1);
32
34
  }}
33
35
  ></input>
34
- {`${t('limit')}${props.value > 0 ? `: ${props.value}` : ''}`}
36
+ {`${t('limit')}${innerValue > 0 ? `: ${innerValue}` : ''}`}
35
37
  </output>
36
38
  </div>
37
39
  );
@@ -78,7 +78,6 @@ const PivotTable: React.FC<PivotTableProps> = (props) => {
78
78
  data
79
79
  );
80
80
  const metric = buildMetricTableFromNestTree(lt, tt, data);
81
- // debugger
82
81
  unstable_batchedUpdates(() => {
83
82
  setLeftTree(lt);
84
83
  setTopTree(tt);
@@ -1,10 +1,7 @@
1
1
  import { makeAutoObservable, observable } from 'mobx';
2
2
  import { INestNode } from './inteface';
3
- import { IAggQuery } from '../../lib/interfaces';
4
- import { queryView } from '../../lib/viewQuery';
5
3
  import { IField, IRow } from '../../interfaces';
6
4
  import React, { createContext, useContext, useEffect } from 'react';
7
- import { getMeaAggKey } from '../../utils';
8
5
 
9
6
  class PivotTableStore {
10
7
  public leftTree: INestNode | null = null;
@@ -30,19 +27,6 @@ class PivotTableStore {
30
27
  this.topTree = null;
31
28
  this.viewData = [];
32
29
  }
33
- public async queryData(leftQuery: IAggQuery, topQuery: IAggQuery) {
34
- const viewQuery: IAggQuery = {
35
- op: 'aggregate',
36
- groupBy: leftQuery.groupBy.concat(topQuery.groupBy),
37
- measures: leftQuery.measures.concat(topQuery.measures).map(mea => ({
38
- field: mea.field,
39
- agg: mea.agg,
40
- asFieldKey: getMeaAggKey(mea.field, mea.agg)
41
- }))
42
- };
43
- const viewData = queryView(this.dataSource, this.metas, viewQuery);
44
- this.viewData = viewData;
45
- }
46
30
  }
47
31
 
48
32
  const initStore = new PivotTableStore();
@@ -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(width / 1000)}
27
+ value={Math.sqrt(innerWidth / 1000)}
26
28
  min="0"
27
29
  max="1"
28
30
  step="0.01"
29
31
  onChange={(e) => {
30
- onWidthChange(Math.round(Number(e.target.value) ** 2 * 1000));
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")}: ${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(height / 1000)}
45
+ value={Math.sqrt(innerHeight / 1000)}
44
46
  min="0"
45
47
  max="1"
46
48
  step="0.01"
47
49
  onChange={(e) => {
48
- onHeightChange(Math.round(Number(e.target.value) ** 2 * 1000));
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")}: ${height}`}
54
+ {`${t("height")}: ${innerHeight}`}
53
55
  </output>
54
56
  </div>
55
57
  </div>
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+
3
+ export default function Spinner() {
4
+ return (
5
+ <svg className="animate-spin ml-2 mr-2 h-5 w-5 text-white" viewBox="0 0 24 24">
6
+ <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
7
+ <path
8
+ className="opacity-75"
9
+ fill="currentColor"
10
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
11
+ ></path>
12
+ </svg>
13
+ );
14
+ }
@@ -20,7 +20,7 @@ export default function Toggle(props: ToggleProps) {
20
20
  checked={enabled}
21
21
  onChange={onChange}
22
22
  className={classNames(
23
- enabled ? 'bg-indigo-600' : 'bg-gray-200',
23
+ enabled ? 'bg-indigo-600' : 'bg-gray-200 dark:bg-gray-700',
24
24
  'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2'
25
25
  )}
26
26
  >
@@ -33,7 +33,7 @@ export default function Toggle(props: ToggleProps) {
33
33
  />
34
34
  </Switch>
35
35
  <Switch.Label as="span" className="ml-3 text-sm">
36
- <span className="font-medium text-gray-900">{label}</span>
36
+ <span className="font-medium">{label}</span>
37
37
  </Switch.Label>
38
38
  </Switch.Group>
39
39
  );