@kanaries/graphic-walker 0.2.11 → 0.2.13

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 (69) hide show
  1. package/dist/App.d.ts +6 -3
  2. package/dist/assets/explainer.worker-8428eb12.js.map +1 -1
  3. package/dist/components/button/base.d.ts +6 -0
  4. package/dist/components/button/default.d.ts +4 -0
  5. package/dist/components/button/primary.d.ts +4 -0
  6. package/dist/components/dataTable/index.d.ts +10 -0
  7. package/dist/components/dataTable/pagination.d.ts +10 -0
  8. package/dist/components/tabs/defaultTab.d.ts +14 -0
  9. package/dist/components/tabs/{pureTab.d.ts → editableTab.d.ts} +2 -2
  10. package/dist/dataSource/datasetConfig/index.d.ts +3 -0
  11. package/dist/dataSource/utils.d.ts +1 -1
  12. package/dist/graphic-walker.es.js +17347 -18050
  13. package/dist/graphic-walker.es.js.map +1 -1
  14. package/dist/graphic-walker.umd.js +140 -138
  15. package/dist/graphic-walker.umd.js.map +1 -1
  16. package/dist/index.d.ts +2 -2
  17. package/dist/interfaces.d.ts +12 -0
  18. package/dist/lib/inferMeta.d.ts +20 -0
  19. package/dist/segments/segmentNav.d.ts +3 -0
  20. package/dist/store/commonStore.d.ts +8 -2
  21. package/dist/store/index.d.ts +0 -1
  22. package/dist/store/visualSpecStore.d.ts +5 -5
  23. package/dist/utils/dataPrep.d.ts +6 -0
  24. package/dist/utils/index.d.ts +2 -2
  25. package/dist/utils/normalization.d.ts +1 -1
  26. package/dist/utils/save.d.ts +3 -3
  27. package/dist/utils/throttle.d.ts +1 -1
  28. package/dist/vis/temporalFormat.d.ts +10 -0
  29. package/package.json +1 -1
  30. package/src/App.tsx +93 -51
  31. package/src/components/button/base.ts +7 -0
  32. package/src/components/button/default.tsx +17 -0
  33. package/src/components/button/primary.tsx +17 -0
  34. package/src/components/dataTable/index.tsx +187 -0
  35. package/src/components/dataTable/pagination.tsx +44 -0
  36. package/src/components/tabs/defaultTab.tsx +43 -0
  37. package/src/components/tabs/{pureTab.tsx → editableTab.tsx} +3 -3
  38. package/src/dataSource/dataSelection/csvData.tsx +8 -10
  39. package/src/dataSource/dataSelection/index.tsx +1 -1
  40. package/src/dataSource/dataSelection/publicData.tsx +4 -4
  41. package/src/dataSource/datasetConfig/index.tsx +21 -0
  42. package/src/dataSource/index.tsx +10 -12
  43. package/src/dataSource/table.tsx +11 -142
  44. package/src/dataSource/utils.ts +30 -35
  45. package/src/fields/datasetFields/dimFields.tsx +1 -5
  46. package/src/fields/datasetFields/meaFields.tsx +1 -5
  47. package/src/fields/obComponents/obFContainer.tsx +1 -5
  48. package/src/index.tsx +3 -4
  49. package/src/interfaces.ts +14 -0
  50. package/src/lib/inferMeta.ts +88 -0
  51. package/src/locales/en-US.json +14 -0
  52. package/src/locales/zh-CN.json +14 -0
  53. package/src/main.tsx +1 -1
  54. package/src/renderer/index.tsx +1 -0
  55. package/src/segments/segmentNav.tsx +58 -0
  56. package/src/segments/visNav.tsx +2 -2
  57. package/src/store/commonStore.ts +36 -5
  58. package/src/store/index.tsx +0 -2
  59. package/src/store/visualSpecStore.ts +245 -183
  60. package/src/utils/autoMark.ts +14 -14
  61. package/src/utils/dataPrep.ts +44 -0
  62. package/src/utils/index.ts +140 -128
  63. package/src/utils/normalization.ts +59 -51
  64. package/src/utils/save.ts +22 -21
  65. package/src/utils/throttle.ts +5 -1
  66. package/src/vis/react-vega.tsx +6 -10
  67. package/src/vis/temporalFormat.ts +66 -0
  68. package/dist/pitch/dnd-offset.d.ts +0 -2
  69. package/src/pitch/dnd-offset.ts +0 -64
@@ -0,0 +1,88 @@
1
+ import { IAnalyticType, ISemanticType, UnivariateSummary } from "visual-insights";
2
+ import { IMutField, IRow, IUncertainMutField } from "../interfaces";
3
+
4
+ const COMMON_TIME_FORMAT: RegExp[] = [
5
+ /^\d{4}-\d{2}-\d{2}$/, // YYYY-MM-DD
6
+ /^\d{2}\/\d{2}\/\d{4}$/, // MM/DD/YYYY
7
+ /^\d{2}\/\d{2}\/\d{4}$/, // DD/MM/YYYY
8
+ /^\d{4}\/\d{2}\/\d{2}$/, // YYYY/MM/DD
9
+ /^\d{4}\.\d{2}\.\d{2}$/, // YYYY.MM.DD
10
+ /^\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}$/, // YYYY-MM-DD HH:MM:SS
11
+ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/ // YYYY-MM-DDTHH:MM:SS (ISO-8601)
12
+ ];
13
+
14
+ /**
15
+ * check if this array is a date time array based on some common date format
16
+ * @param data string array
17
+ * @returns
18
+ */
19
+ export function isDateTimeArray(data: string[]): boolean {
20
+ let isDateTime = true;
21
+ for (let d of data) {
22
+ let isDateTimeItem = false;
23
+ for (let r of COMMON_TIME_FORMAT) {
24
+ if (r.test(d)) {
25
+ isDateTimeItem = true;
26
+ break;
27
+ }
28
+ }
29
+ if (!isDateTimeItem) {
30
+ isDateTime = false;
31
+ break;
32
+ }
33
+ }
34
+ return isDateTime;
35
+ }
36
+
37
+ function inferAnalyticTypeFromSemanticType(semanticType: ISemanticType): IAnalyticType {
38
+ switch (semanticType) {
39
+ case 'quantitative':
40
+ return 'measure';
41
+ default:
42
+ return 'dimension';
43
+ }
44
+ }
45
+
46
+ /**
47
+ * 这里目前暂时包一层,是为了解耦具体的推断实现。后续这里要调整推断的逻辑。
48
+ * 需要讨论这一层是否和交互层有关,如果没有关系,这一层包裹可以不存在这里,而是在visual-insights中。
49
+ * @param data 原始数据
50
+ * @param fid 字段id
51
+ * @returns semantic type 列表
52
+ */
53
+ export function inferSemanticType(data: IRow[], fid: string): ISemanticType {
54
+ let st = UnivariateSummary.getFieldType(data, fid);
55
+ if (st === 'nominal') {
56
+ if (isDateTimeArray(data.map((row) => row[fid]))) st = 'temporal';
57
+ } else if (st === 'ordinal') {
58
+ const valueSet: Set<number> = new Set();
59
+ let _max = -Infinity;
60
+ let _min = Infinity;
61
+ for (let v of valueSet) {
62
+ _max = Math.max(_max, v);
63
+ _min = Math.max(_min, v);
64
+ }
65
+ if (_max - _min + 1 !== valueSet.size) {
66
+ st = 'quantitative';
67
+ }
68
+ }
69
+ return st;
70
+ }
71
+
72
+ export function inferMeta (props: { dataSource: IRow[]; fields: IUncertainMutField[] }): IMutField[] {
73
+ const { dataSource, fields } = props;
74
+ const finalFieldMetas: IMutField[] = []
75
+ for (let field of fields) {
76
+ let semanticType: ISemanticType = field.semanticType === '?' ? inferSemanticType(dataSource, field.fid) : field.semanticType;
77
+ let analyticType: IAnalyticType = inferAnalyticTypeFromSemanticType(semanticType);
78
+
79
+ finalFieldMetas.push({
80
+ fid: field.fid,
81
+ key: field.key ? field.key : field.fid,
82
+ name: field.name ? field.name : field.fid,
83
+ analyticType,
84
+ semanticType,
85
+ })
86
+ }
87
+ return finalFieldMetas
88
+ }
@@ -5,6 +5,12 @@
5
5
  "dimension": "dimension",
6
6
  "measure": "measure"
7
7
  },
8
+ "semantic_type": {
9
+ "nominal": "nominal",
10
+ "ordinal": "ordinal",
11
+ "temporal": "temporal",
12
+ "quantitative": "quantitative"
13
+ },
8
14
  "mark_type": {
9
15
  "__enum__": "Mark Type",
10
16
  "auto": "Auto",
@@ -75,6 +81,10 @@
75
81
  "App": {
76
82
  "labels": {
77
83
  "data_interpretation": "Interpret Data"
84
+ },
85
+ "segments": {
86
+ "vis": "Visualization",
87
+ "data": "Data"
78
88
  }
79
89
  },
80
90
  "DataSource": {
@@ -171,5 +181,9 @@
171
181
  "children_major_factor": "major factor",
172
182
  "children_outlier": "outlier"
173
183
  }
184
+ },
185
+ "actions": {
186
+ "prev": "Previous",
187
+ "next": "Next"
174
188
  }
175
189
  }
@@ -5,6 +5,12 @@
5
5
  "dimension": "维度",
6
6
  "measure": "度量"
7
7
  },
8
+ "semantic_type": {
9
+ "nominal": "类别类型",
10
+ "ordinal": "有序类型",
11
+ "temporal": "时间类型",
12
+ "quantitative": "数值类型"
13
+ },
8
14
  "mark_type": {
9
15
  "__enum__": "标记类型",
10
16
  "auto": "自动",
@@ -75,6 +81,10 @@
75
81
  "App": {
76
82
  "labels": {
77
83
  "data_interpretation": "数据解读"
84
+ },
85
+ "segments": {
86
+ "vis": "可视化",
87
+ "data": "数据"
78
88
  }
79
89
  },
80
90
  "DataSource": {
@@ -171,5 +181,9 @@
171
181
  "children_major_factor": "子节点主要因素",
172
182
  "children_outlier": "子节点异常"
173
183
  }
184
+ },
185
+ "actions": {
186
+ "prev": "向前",
187
+ "next": "向后"
174
188
  }
175
189
  }
package/src/main.tsx CHANGED
@@ -9,7 +9,7 @@ inject();
9
9
 
10
10
  ReactDOM.render(
11
11
  <React.StrictMode>
12
- <GraphicWalker />
12
+ <GraphicWalker />
13
13
  </React.StrictMode>,
14
14
  document.getElementById("root")
15
15
  );
@@ -32,6 +32,7 @@ const ReactiveRenderer = forwardRef<IReactVegaHandler, {}>(function ReactiveRend
32
32
 
33
33
  const handleGeomClick = useCallback((values: any, e: any) => {
34
34
  if (shouldTriggerMenu) {
35
+ e.stopPropagation();
35
36
  runInAction(() => {
36
37
  commonStore.showEmbededMenu([e.pageX, e.pageY])
37
38
  commonStore.setFilters(values);
@@ -0,0 +1,58 @@
1
+ import React, { Fragment, useCallback } from "react";
2
+ import { observer } from "mobx-react-lite";
3
+ import DefaultTab, { ITabOption } from "../components/tabs/defaultTab";
4
+ import { useGlobalStore } from "../store";
5
+ import { ChartBarIcon, ChartPieIcon, CircleStackIcon } from "@heroicons/react/24/outline";
6
+ import { ISegmentKey } from "../interfaces";
7
+ import { useTranslation } from "react-i18next";
8
+
9
+
10
+ const ADD_KEY = '_add';
11
+
12
+ const SegmentNav: React.FC = (props) => {
13
+ const { vizStore, commonStore } = useGlobalStore();
14
+ const { visIndex, visList } = vizStore;
15
+ const { currentDataset, segmentKey } = commonStore;
16
+ const { t } = useTranslation();
17
+
18
+ const tabs: ITabOption[] = [
19
+ {
20
+ key: ISegmentKey.data,
21
+ label: <div className="flex">
22
+ <CircleStackIcon className="w-4 mr-2" /> {t('App.segments.data')}
23
+ </div>
24
+ },
25
+ {
26
+ key: ISegmentKey.vis,
27
+ label: <div className="flex">
28
+ <ChartPieIcon className="w-4 mr-2" /> {t('App.segments.vis')}
29
+ </div>
30
+ }
31
+ ]
32
+
33
+ const visSelectionHandler = useCallback((tabKey: string, tabIndex: number) => {
34
+ if (tabKey === ADD_KEY) {
35
+ vizStore.addVisualization();
36
+ vizStore.initMetaState(currentDataset)
37
+ } else {
38
+ vizStore.selectVisualization(tabIndex);
39
+ }
40
+ }, [currentDataset, vizStore])
41
+
42
+ const editLabelHandler = useCallback((content: string, tabIndex: number) => {
43
+ vizStore.setVisName(tabIndex, content)
44
+ }, [])
45
+
46
+ return (
47
+ <DefaultTab
48
+ selectedKey={segmentKey}
49
+ tabs={tabs}
50
+ onEditLabel={editLabelHandler}
51
+ onSelected={(k) => {
52
+ commonStore.setSegmentKey(k as ISegmentKey)
53
+ }}
54
+ />
55
+ );
56
+ };
57
+
58
+ export default observer(SegmentNav);
@@ -1,6 +1,6 @@
1
1
  import React, { useCallback } from "react";
2
2
  import { observer } from "mobx-react-lite";
3
- import PureTabs, { ITabOption } from "../components/tabs/pureTab";
3
+ import EditableTabs, { ITabOption } from "../components/tabs/editableTab";
4
4
  import { useGlobalStore } from "../store";
5
5
 
6
6
 
@@ -36,7 +36,7 @@ const VisNav: React.FC = (props) => {
36
36
  }, [])
37
37
 
38
38
  return (
39
- <PureTabs
39
+ <EditableTabs
40
40
  selectedKey={visList[visIndex].visId}
41
41
  tabs={tabs}
42
42
  onEditLabel={editLabelHandler}
@@ -1,4 +1,4 @@
1
- import { DataSet, Filters, IDataSet, IDataSetInfo, IDataSource, IMutField, IRow } from '../interfaces';
1
+ import { DataSet, Filters, IDataSet, IDataSetInfo, IDataSource, IMutField, IRow, ISegmentKey } from '../interfaces';
2
2
  import { makeAutoObservable, observable, toJS } from 'mobx';
3
3
  import { transData } from '../dataSource/utils';
4
4
  import { extendCountField } from '../utils';
@@ -13,17 +13,16 @@ export class CommonStore {
13
13
  public showDSPanel: boolean = false;
14
14
  public showInsightBoard: boolean = false;
15
15
  public vizEmbededMenu: { show: boolean; position: [number, number] } = { show: false, position: [0, 0] };
16
- public rootContainer: HTMLDivElement | null = null;
17
-
16
+ public showDataConfig: boolean = false;
18
17
  public filters: Filters = {};
18
+ public segmentKey: ISegmentKey = ISegmentKey.vis;
19
19
  constructor () {
20
20
  this.datasets = [];
21
21
  this.dataSources = [];
22
22
  makeAutoObservable(this, {
23
23
  dataSources: observable.ref,
24
24
  tmpDataSource: observable.ref,
25
- filters: observable.ref,
26
- rootContainer: false
25
+ filters: observable.ref
27
26
  });
28
27
  }
29
28
  public get currentDataset (): DataSet {
@@ -46,9 +45,15 @@ export class CommonStore {
46
45
  dataSource: []
47
46
  }
48
47
  }
48
+ public setSegmentKey (sk: ISegmentKey) {
49
+ this.segmentKey = sk;
50
+ }
49
51
  public setShowDSPanel (show: boolean) {
50
52
  this.showDSPanel = show;
51
53
  }
54
+ public setShowDataConfig (show: boolean) {
55
+ this.showDataConfig = show;
56
+ }
52
57
  public setShowInsightBoard (show: boolean) {
53
58
  this.showInsightBoard = show;
54
59
  }
@@ -68,6 +73,25 @@ export class CommonStore {
68
73
  this.tmpDSRawFields = fields;
69
74
  }
70
75
 
76
+ public updateCurrentDatasetMetas (fid: string, diffMeta: Partial<IMutField>) {
77
+ const dataset = this.datasets[this.dsIndex];
78
+ const field = dataset.rawFields.find(f => f.fid === fid);
79
+ if (field) {
80
+ for (let mk in diffMeta) {
81
+ field[mk] = diffMeta[mk];
82
+ }
83
+ }
84
+ }
85
+
86
+ public updateTempDatasetMetas (fid: string, diffMeta: Partial<IMutField>) {
87
+ const field = this.tmpDSRawFields.find(f => f.fid === fid);
88
+ if (field) {
89
+ for (let mk in diffMeta) {
90
+ field[mk] = diffMeta[mk];
91
+ }
92
+ }
93
+ }
94
+
71
95
  public updateTempFieldAnalyticType (fieldKey: string, analyticType: IMutField['analyticType']) {
72
96
  const field = this.tmpDSRawFields.find(f => f.fid === fieldKey);
73
97
  if (field) {
@@ -75,6 +99,13 @@ export class CommonStore {
75
99
  }
76
100
  }
77
101
 
102
+ public updateTempFieldSemanticType (fieldKey: string, semanticType: IMutField['semanticType']) {
103
+ const field = this.tmpDSRawFields.find(f => f.fid === fieldKey);
104
+ if (field) {
105
+ field.semanticType = semanticType;
106
+ }
107
+ }
108
+
78
109
  public updateTempName (name: string) {
79
110
  this.tmpDSName = name;
80
111
  }
@@ -31,7 +31,6 @@ export function rebootGWStore() {
31
31
 
32
32
  interface StoreWrapperProps {
33
33
  keepAlive?: boolean;
34
- rootContainer?: HTMLDivElement | null;
35
34
  }
36
35
  export class StoreWrapper extends React.Component<StoreWrapperProps> {
37
36
  constructor(props: StoreWrapperProps) {
@@ -46,7 +45,6 @@ export class StoreWrapper extends React.Component<StoreWrapperProps> {
46
45
  }
47
46
  }
48
47
  render() {
49
- initStore.commonStore.rootContainer = this.props.rootContainer ?? null;
50
48
  return <StoreContext.Provider value={initStore}>
51
49
  { this.props.children }
52
50
  </StoreContext.Provider>