@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
@@ -1,10 +1,13 @@
1
1
  import { observer } from 'mobx-react-lite';
2
- import React, { useMemo, useRef, useState } from 'react';
2
+ import React, { useEffect, useMemo, useRef, useState } from 'react';
3
3
  import { useTranslation } from 'react-i18next';
4
4
  import styled from 'styled-components';
5
5
 
6
- import type { IFilterField, IFilterRule } from '../../interfaces';
6
+ import type { IFilterField, IFilterRule, IRow, DataSet, IFieldStats, IField, IViewField } from '../../interfaces';
7
7
  import { useGlobalStore } from '../../store';
8
+ import LoadingLayer from '../../components/loadingLayer';
9
+ import { useComputationFunc, useRenderer } from '../../renderer/hooks';
10
+ import { fieldStatServer } from '../../computation/serverComputation';
8
11
  import Slider from './slider';
9
12
  import {
10
13
  ChevronDownIcon,
@@ -12,6 +15,7 @@ import {
12
15
  } from '@heroicons/react/24/outline';
13
16
 
14
17
  export type RuleFormProps = {
18
+ dataset: DataSet;
15
19
  field: IFilterField;
16
20
  onChange: (rule: IFilterRule) => void;
17
21
  };
@@ -27,15 +31,15 @@ const Container = styled.div`
27
31
  > * {
28
32
  margin-inline-start: 0.6em;
29
33
 
30
- &:first-child: {
34
+ &:first-child {
31
35
  margin-inline-start: 0;
32
- },
33
- },
34
- },
36
+ }
37
+ }
38
+ }
35
39
  `;
36
40
 
37
41
  export const Button = styled.button`
38
- :hover: {
42
+ :hover {
39
43
  background-color: rgba(243, 244, 246, 0.5);
40
44
  };
41
45
  color: rgb(55, 65, 81);
@@ -127,13 +131,75 @@ const StatusCheckbox: React.FC<{ currentNum: number; totalNum: number; onChange:
127
131
  )
128
132
  }
129
133
 
134
+ type FieldDistributionEntry = IFieldStats['values'][number];
135
+
136
+ const defaultValueComparator = (a: any, b: any) => {
137
+ if (typeof a === 'number' && typeof b === 'number') {
138
+ return a - b;
139
+ } else {
140
+ return String(a).localeCompare(String(b))
141
+ }
142
+ };
143
+
144
+ const countCmp = (a: FieldDistributionEntry, b: FieldDistributionEntry) => {
145
+ return a.count - b.count;
146
+ };
147
+
148
+ const useFieldStats = (
149
+ field: IField,
150
+ attributes: { values: boolean; range: boolean },
151
+ sortBy: 'value' | 'value_dsc' | 'count' | 'count_dsc' | 'none'
152
+ ): IFieldStats | null => {
153
+ const { values, range } = attributes;
154
+ const { fid, cmp = defaultValueComparator } = field;
155
+ const valueCmp = React.useCallback<typeof countCmp>((a, b) => {
156
+ return cmp(a.value, b.value);
157
+ }, [cmp]);
158
+ const comparator = sortBy === "none" ? null : sortBy.startsWith("value") ? valueCmp : countCmp;
159
+ const sortMulti = sortBy.endsWith("dsc") ? -1 : 1;
160
+ const [loading, setLoading] = React.useState(true);
161
+ const [stats, setStats] = React.useState<IFieldStats | null>(null);
162
+ const computationFunction = useComputationFunc();
163
+
164
+ React.useEffect(() => {
165
+ setLoading(true);
166
+ let isCancelled = false;
167
+ fieldStatServer(computationFunction, fid, { values, range }).then(stats => {
168
+ if (isCancelled) {
169
+ return;
170
+ }
171
+ setStats(stats);
172
+ setLoading(false);
173
+ }).catch(reason => {
174
+ console.warn(reason);
175
+ if (isCancelled) {
176
+ return;
177
+ }
178
+ setStats(null);
179
+ setLoading(false);
180
+ });
181
+ return () => {
182
+ isCancelled = true;
183
+ };
184
+ }, [fid, computationFunction, values, range]);
185
+
186
+ const sortedStats = React.useMemo<typeof stats>(() => {
187
+ if (!stats || !comparator) {
188
+ return stats;
189
+ }
190
+ const copy = { ...stats };
191
+ copy.values = copy.values.slice().sort((a,b) => sortMulti * comparator(a,b));
192
+ return copy;
193
+ }, [stats, comparator, sortMulti]);
194
+
195
+ return loading ? null : sortedStats;
196
+ };
197
+
130
198
  export const FilterOneOfRule: React.FC<RuleFormProps & { active: boolean }> = observer(({
131
199
  active,
132
200
  field,
133
201
  onChange,
134
202
  }) => {
135
- const { commonStore } = useGlobalStore();
136
- const { currentDataset: { dataSource } } = commonStore;
137
203
 
138
204
  interface SortConfig {
139
205
  key: 'value' | 'count';
@@ -144,67 +210,41 @@ export const FilterOneOfRule: React.FC<RuleFormProps & { active: boolean }> = ob
144
210
  ascending: true
145
211
  });
146
212
 
147
- const count = React.useMemo(() => {
148
- return dataSource.reduce<Map<string | number, number>>((tmp, d) => {
149
- const val = d[field.fid];
150
-
151
- tmp.set(val, (tmp.get(val) ?? 0) + 1);
152
-
153
- return tmp;
154
- }, new Map<string | number, number>());
155
- }, [dataSource, field]);
156
-
157
- const sortedList = useMemo(() => {
158
- const entries = Array.from(count.entries());
159
- const compare = (a: [string | number, number], b: [string | number, number]) => {
160
- if (sortConfig.key === 'count') {
161
- return a[1] - b[1];
162
- } else {
163
- if (typeof a[0] === 'number' && typeof b[0] === 'number') {
164
- return a[0] - b[0];
165
- } else {
166
- return String(a[0]).localeCompare(String(b[0]))
167
- }
168
- }
169
- }
170
- entries.sort(sortConfig.ascending ? compare : (a, b) => -compare(a, b));
171
- return entries;
172
- }, [count, sortConfig]);
173
-
174
213
  const { t } = useTranslation('translation');
175
214
 
215
+ const stats = useFieldStats(field, { values: true, range: false }, `${sortConfig.key}${sortConfig.ascending ? '': '_dsc'}`);
216
+ const count = stats?.values;
217
+
176
218
  React.useEffect(() => {
177
- if (active && field.rule?.type !== 'one of') {
219
+ if (count && active && field.rule?.type !== 'one of') {
178
220
  onChange({
179
221
  type: 'one of',
180
- value: new Set<string | number>(count.keys()),
222
+ value: new Set<string | number>(count.map(item => item.value)),
181
223
  });
182
224
  }
183
225
  }, [active, onChange, field, count]);
184
226
 
185
227
  const handleToggleFullOrEmptySet = () => {
186
- if (!field.rule || field.rule.type !== 'one of') return;
228
+ if (!field.rule || field.rule.type !== 'one of' || !count) return;
187
229
  const curSet = field.rule.value;
188
230
  onChange({
189
231
  type: 'one of',
190
232
  value: new Set<number | string>(
191
- curSet.size === count.size
192
- ? []
193
- : count.keys()
233
+ curSet.size === count.length ? [] : count.map(c => c.value)
194
234
  ),
195
235
  });
196
236
  }
197
237
  const handleToggleReverseSet = () => {
198
- if (!field.rule || field.rule.type !== 'one of') return;
238
+ if (!field.rule || field.rule.type !== 'one of' || !count) return;
199
239
  const curSet = field.rule.value;
200
240
  onChange({
201
241
  type: 'one of',
202
242
  value: new Set<number | string>(
203
- [...count.keys()].filter(key => !curSet.has(key))
243
+ count.map(c => c.value).filter(key => !curSet.has(key))
204
244
  ),
205
245
  });
206
246
  }
207
- const handleSelectValue = (value, checked) => {
247
+ const handleSelectValue = (value: any, checked: boolean) => {
208
248
  if (!field.rule || field.rule?.type !== 'one of') return;
209
249
  const rule: IFilterRule = {
210
250
  type: 'one of',
@@ -219,12 +259,20 @@ export const FilterOneOfRule: React.FC<RuleFormProps & { active: boolean }> = ob
219
259
  }
220
260
 
221
261
  const selectedValueSum = useMemo(() => {
222
- if (!field.rule) return 0;
262
+ if (!field.rule?.value || !count) return 0;
223
263
  return [...field.rule.value].reduce<number>((sum, key) => {
224
- const s = dataSource.filter(which => which[field.fid] === key).length;
264
+ const s = count.find(c => c.value === key)?.count || 0;
225
265
  return sum + s;
226
266
  }, 0)
227
- }, [field.rule?.value]);
267
+ }, [field.rule?.value, count, field.fid]);
268
+
269
+ if (!stats) {
270
+ return (
271
+ <div className="h-24 w-full relative">
272
+ <LoadingLayer />
273
+ </div>
274
+ );
275
+ }
228
276
 
229
277
  const SortButton: React.FC<{ currentKey: SortConfig["key"] }> = ({ currentKey }) => {
230
278
  const isCurrentKey = sortConfig.key === currentKey;
@@ -249,9 +297,10 @@ export const FilterOneOfRule: React.FC<RuleFormProps & { active: boolean }> = ob
249
297
  <Button
250
298
  className="dark:bg-zinc-900 dark:text-gray-200 dark:hover:bg-gray-800"
251
299
  onClick={() => handleToggleFullOrEmptySet()}
300
+ disabled={!count}
252
301
  >
253
302
  {
254
- field.rule.value.size === count.size
303
+ field.rule.value.size === count?.length
255
304
  ? t('filters.btn.unselect_all')
256
305
  : t('filters.btn.select_all')
257
306
  }
@@ -267,7 +316,7 @@ export const FilterOneOfRule: React.FC<RuleFormProps & { active: boolean }> = ob
267
316
  <div className="flex justify-center items-center">
268
317
  <StatusCheckbox
269
318
  currentNum={field.rule.value.size}
270
- totalNum={count.size}
319
+ totalNum={count?.length ?? 0}
271
320
  onChange={handleToggleFullOrEmptySet}
272
321
  />
273
322
  </div>
@@ -283,7 +332,7 @@ export const FilterOneOfRule: React.FC<RuleFormProps & { active: boolean }> = ob
283
332
  {/* <hr /> */}
284
333
  <Table>
285
334
  {
286
- sortedList.map(([value, count], idx) => {
335
+ count?.map(({ value, count }, idx) => {
287
336
  const id = `rule_checkbox_${idx}`;
288
337
 
289
338
  return (
@@ -339,9 +388,11 @@ interface CalendarInputProps {
339
388
  const CalendarInput: React.FC<CalendarInputProps> = props => {
340
389
  const { min, max, value, onChange } = props;
341
390
  const dateStringFormatter = (timestamp: number) => {
342
- return new Date(timestamp).toISOString().slice(0, 19);
391
+ const date = new Date(timestamp);
392
+ if (Number.isNaN(date.getTime())) return '';
393
+ return date.toISOString().slice(0, 19);
343
394
  }
344
- const handleSubmitDate = (value) => {
395
+ const handleSubmitDate = (value: string) => {
345
396
  if (new Date(value).getTime() <= max && new Date(value).getTime() >= min) {
346
397
  onChange(new Date(value).getTime())
347
398
  }
@@ -358,18 +409,48 @@ const CalendarInput: React.FC<CalendarInputProps> = props => {
358
409
  )
359
410
  }
360
411
 
412
+ const emptyFilters = [] as const;
413
+
361
414
  export const FilterTemporalRangeRule: React.FC<RuleFormProps & { active: boolean }> = observer(({
415
+ dataset,
362
416
  active,
363
417
  field,
364
418
  onChange,
365
419
  }) => {
366
- const { commonStore } = useGlobalStore();
367
- const { currentDataset: { dataSource } } = commonStore;
420
+ const { rawFields } = dataset;
368
421
 
369
422
  const { t } = useTranslation('translation');
370
423
 
424
+ const fields = useMemo(() => {
425
+ return rawFields.map<Omit<IViewField, 'dragId'>>(f => ({
426
+ ...f,
427
+ name: f.name || f.fid,
428
+ }));
429
+ }, [rawFields]);
430
+
431
+ const viewDimensions = useMemo(() => {
432
+ return field.analyticType === 'dimension' ? [field] : [];
433
+ }, [field]);
434
+
435
+ const viewMeasures = useMemo(() => {
436
+ return field.analyticType === 'measure' ? [field] : [];
437
+ }, [field]);
438
+
439
+ const computationFunction = useComputationFunc();
440
+
441
+ const { viewData, loading } = useRenderer({
442
+ allFields: fields,
443
+ viewDimensions,
444
+ viewMeasures,
445
+ filters: emptyFilters,
446
+ defaultAggregated: false,
447
+ computationFunction,
448
+ limit: 1000,
449
+ sort: 'none',
450
+ });
451
+
371
452
  const sorted = React.useMemo(() => {
372
- return dataSource.reduce<number[]>((list, d) => {
453
+ return viewData.reduce<number[]>((list, d) => {
373
454
  try {
374
455
  const time = new Date(d[field.fid]).getTime();
375
456
 
@@ -379,20 +460,30 @@ export const FilterTemporalRangeRule: React.FC<RuleFormProps & { active: boolean
379
460
  }
380
461
  return list;
381
462
  }, []).sort((a, b) => a - b);
382
- }, [dataSource, field]);
463
+ }, [viewData, field.fid]);
383
464
 
384
- const [min, max] = React.useMemo(() => {
385
- return [sorted[0] ?? 0, Math.max(sorted[sorted.length - 1] ?? 0, sorted[0] ?? 0)];
465
+ const [min, max, loaded] = React.useMemo<[min: number, max: number, loaded: boolean]>(() => {
466
+ if (!sorted.length) return [0, 0, false];
467
+ return [sorted[0] ?? 0, Math.max(sorted[sorted.length - 1] ?? 0, sorted[0] ?? 0), true];
386
468
  }, [sorted]);
387
469
 
388
470
  React.useEffect(() => {
389
471
  if (active && field.rule?.type !== 'temporal range') {
390
472
  onChange({
391
473
  type: 'temporal range',
392
- value: [sorted[0] ?? 0, Math.max(sorted[sorted.length - 1] ?? 0, sorted[0] ?? 0)],
474
+ value: [min, max],
393
475
  });
394
476
  }
395
- }, [onChange, field, sorted, active]);
477
+ }, [onChange, field, min, max, active]);
478
+
479
+ React.useEffect(() => {
480
+ if (active && loaded && field.rule?.type === 'temporal range' && field.rule.value[0] !== min && field.rule.value[1] !== max) {
481
+ onChange({
482
+ type: 'temporal range',
483
+ value: [min, max],
484
+ });
485
+ }
486
+ }, [field.rule, min, max, active, loaded]);
396
487
 
397
488
  const handleChange = React.useCallback((value: readonly [number, number]) => {
398
489
  onChange({
@@ -401,6 +492,14 @@ export const FilterTemporalRangeRule: React.FC<RuleFormProps & { active: boolean
401
492
  });
402
493
  }, []);
403
494
 
495
+ if (loading) {
496
+ return (
497
+ <div className="h-24 w-full relative">
498
+ <LoadingLayer />
499
+ </div>
500
+ );
501
+ }
502
+
404
503
  return field.rule?.type === 'temporal range' ? (
405
504
  <Container className="overflow-visible">
406
505
  <div>{t('constant.filter_type.temporal_range')}</div>
@@ -434,27 +533,19 @@ export const FilterRangeRule: React.FC<RuleFormProps & { active: boolean }> = ob
434
533
  field,
435
534
  onChange,
436
535
  }) => {
437
- const { commonStore } = useGlobalStore();
438
- const { currentDataset: { dataSource } } = commonStore;
439
-
440
536
  const { t } = useTranslation('translation', { keyPrefix: 'constant.filter_type' });
441
537
 
442
- const sorted = React.useMemo(() => {
443
- return dataSource.map(d => d[field.fid]).sort((a, b) => a - b);
444
- }, [dataSource, field]);
445
-
446
- const [min, max] = React.useMemo(() => {
447
- return [sorted[0] ?? 0, Math.max(sorted[sorted.length - 1] ?? 0, sorted[0] ?? 0)];
448
- }, [sorted]);
538
+ const stats = useFieldStats(field, { values: false, range: true }, 'none');
539
+ const range = stats?.range;
449
540
 
450
541
  React.useEffect(() => {
451
- if (active && field.rule?.type !== 'range') {
542
+ if (range && active && field.rule?.type !== 'range') {
452
543
  onChange({
453
544
  type: 'range',
454
- value: [min, max],
545
+ value: range,
455
546
  });
456
547
  }
457
- }, [onChange, field, min, max, active]);
548
+ }, [onChange, field, range, active]);
458
549
 
459
550
  const handleChange = React.useCallback((value: readonly [number, number]) => {
460
551
  onChange({
@@ -463,13 +554,21 @@ export const FilterRangeRule: React.FC<RuleFormProps & { active: boolean }> = ob
463
554
  });
464
555
  }, []);
465
556
 
557
+ if (!range) {
558
+ return (
559
+ <div className="h-24 w-full relative">
560
+ <LoadingLayer />
561
+ </div>
562
+ );
563
+ }
564
+
466
565
  return field.rule?.type === 'range' ? (
467
566
  <Container>
468
567
  <div>{t('range')}</div>
469
568
  <div className="text-gray-500">{t('range_desc')}</div>
470
569
  <Slider
471
- min={min}
472
- max={max}
570
+ min={range[0]}
571
+ max={range[1]}
473
572
  value={field.rule.value}
474
573
  onChange={handleChange}
475
574
  />
@@ -503,8 +602,9 @@ export interface TabsProps extends RuleFormProps {
503
602
  }
504
603
 
505
604
  const Tabs: React.FC<TabsProps> = observer(({ field, onChange, tabs }) => {
506
- const { vizStore } = useGlobalStore();
605
+ const { vizStore, commonStore } = useGlobalStore();
507
606
  const { draggableFieldState } = vizStore;
607
+ const { currentDataset } = commonStore;
508
608
 
509
609
  const { t } = useTranslation('translation', { keyPrefix: 'constant.filter_type' });
510
610
 
@@ -563,6 +663,7 @@ const Tabs: React.FC<TabsProps> = observer(({ field, onChange, tabs }) => {
563
663
  field={field}
564
664
  onChange={onChange}
565
665
  active={which === tab}
666
+ dataset={currentDataset}
566
667
  />
567
668
  </TabItem>
568
669
  );
@@ -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/index.tsx CHANGED
@@ -38,3 +38,5 @@ export const GraphicWalker = observer(forwardRef<IGWHandler, IGWProps>((props, r
38
38
  export { default as PureRenderer } from './renderer/pureRenderer';
39
39
  export { embedGraphicWalker } from './vanilla';
40
40
  export type { IGWProps };
41
+ export { ISegmentKey } from './interfaces';
42
+ export { resolveSpecFromStoInfo } from './utils/save';
package/src/interfaces.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import {Config as VgConfig, View} from 'vega';
2
2
  import {Config as VlConfig} from 'vega-lite';
3
+ import type {IViewQuery} from "./lib/viewQuery";
3
4
 
4
5
  export type DeepReadonly<T extends Record<keyof any, any>> = {
5
6
  readonly [K in keyof T]: T[K] extends Record<keyof any, any> ? DeepReadonly<T[K]> : T[K];
@@ -52,6 +53,15 @@ export interface IUncertainMutField {
52
53
  path: string[];
53
54
  }
54
55
 
56
+ export interface IDatasetStats {
57
+ rowCount: number;
58
+ }
59
+
60
+ export interface IFieldStats {
61
+ values: { value: number | string; count: number }[];
62
+ range: [number, number];
63
+ }
64
+
55
65
  export type IExpParamter =
56
66
  | {
57
67
  type: 'field';
@@ -95,12 +105,12 @@ export interface IField {
95
105
  computed?: boolean;
96
106
  expression?: IExpression;
97
107
  basename?: string;
98
- path?: [],
108
+ path?: string[],
99
109
  }
100
-
110
+ export type ISortMode = 'none' | 'ascending' | 'descending';
101
111
  export interface IViewField extends IField {
102
112
  dragId: string;
103
- sort?: 'none' | 'ascending' | 'descending';
113
+ sort?: ISortMode;
104
114
  }
105
115
 
106
116
  export interface DataSet {
@@ -157,6 +167,11 @@ export interface IFilterField extends IViewField {
157
167
  rule: IFilterRule | null;
158
168
  }
159
169
 
170
+ export interface IFilterFiledSimple {
171
+ fid: string;
172
+ rule: IFilterRule | null;
173
+ }
174
+
160
175
  export interface DraggableFieldState {
161
176
  dimensions: IViewField[];
162
177
  measures: IViewField[];
@@ -192,7 +207,7 @@ export type IFilterRule =
192
207
  value: Set<string | number>;
193
208
  };
194
209
 
195
- export type IStackMode = 'none' | 'stack' | 'normalize';
210
+ export type IStackMode = 'none' | 'stack' | 'normalize' | 'zero' | 'center';
196
211
 
197
212
  export interface IVisualConfig {
198
213
  defaultAggregated: boolean;
@@ -200,18 +215,28 @@ export interface IVisualConfig {
200
215
  stack: IStackMode;
201
216
  showActions: boolean;
202
217
  interactiveScale: boolean;
203
- sorted: 'none' | 'ascending' | 'descending';
218
+ sorted: ISortMode;
204
219
  zeroScale: boolean;
220
+ background?: string;
205
221
  format: {
206
222
  numberFormat?: string;
207
223
  timeFormat?: string;
208
224
  normalizedNumberFormat?: string;
209
225
  };
226
+ resolve: {
227
+ x?: boolean;
228
+ y?: boolean;
229
+ color?: boolean;
230
+ opacity?: boolean;
231
+ shape?: boolean;
232
+ size?: boolean;
233
+ };
210
234
  size: {
211
235
  mode: 'auto' | 'fixed';
212
236
  width: number;
213
237
  height: number;
214
238
  };
239
+ limit: number;
215
240
  }
216
241
 
217
242
  export interface IVisSpec {
@@ -238,6 +263,7 @@ export enum ISegmentKey {
238
263
 
239
264
  export type IThemeKey = 'vega' | 'g2';
240
265
  export type IDarkMode = 'media' | 'light' | 'dark';
266
+ export type IComputationFunction = (payload: IDataQueryPayload) => Promise<IRow[]>;
241
267
 
242
268
  export type VegaGlobalConfig = VgConfig | VlConfig;
243
269
 
@@ -345,3 +371,80 @@ export interface IGWHandler {
345
371
  export interface IGWHandlerInsider extends IGWHandler {
346
372
  updateRenderStatus: (renderStatus: IRenderStatus) => void;
347
373
  }
374
+
375
+ export interface IVisField {
376
+ key: string;
377
+ type: ISemanticType;
378
+ name?: string;
379
+ description?: string;
380
+ format?: string;
381
+ expression?: IExpression;
382
+ }
383
+
384
+ export type IVisFieldComputation = {
385
+ field: IVisField['key'];
386
+ expression: NonNullable<IVisField['expression']>;
387
+ name: NonNullable<IVisField['name']>;
388
+ type: IVisField['type'];
389
+ };
390
+
391
+ export interface IVisFilter {
392
+ fid: string;
393
+ rule: SetToArray<IFilterRule>;
394
+ };
395
+
396
+ export interface IFilterWorkflowStep {
397
+ type: 'filter';
398
+ filters: IVisFilter[];
399
+ }
400
+
401
+ export interface ITransformWorkflowStep {
402
+ type: 'transform';
403
+ transform: {
404
+ key: IVisFieldComputation['field'];
405
+ expression: IVisFieldComputation['expression'];
406
+ }[];
407
+ }
408
+
409
+ export interface IViewWorkflowStep {
410
+ type: 'view';
411
+ query: IViewQuery[];
412
+ }
413
+
414
+ export interface ISortWorkflowStep {
415
+ type: 'sort';
416
+ sort: 'ascending' | 'descending';
417
+ by: string[];
418
+ }
419
+
420
+ export type IDataQueryWorkflowStep = IFilterWorkflowStep | ITransformWorkflowStep | IViewWorkflowStep | ISortWorkflowStep;
421
+
422
+ export interface IDataQueryPayload {
423
+ workflow: IDataQueryWorkflowStep[];
424
+ limit?: number;
425
+ offset?: number;
426
+ }
427
+
428
+ export interface ILoadDataPayload {
429
+ pageSize: number;
430
+ pageIndex: number;
431
+ }
432
+
433
+ export interface IGWDatasetStat {
434
+ count: number;
435
+ }
436
+
437
+ export type IResponse<T> = (
438
+ | {
439
+ success: true;
440
+ data: T;
441
+ }
442
+ | {
443
+ success: false;
444
+ message: string;
445
+ error?: {
446
+ code: `ERR_${Uppercase<string>}`;
447
+ options?: Record<string, string>;
448
+ };
449
+ }
450
+ );