@kanaries/graphic-walker 0.2.11 → 0.2.12

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 (54) 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/dataSource/utils.d.ts +1 -1
  7. package/dist/graphic-walker.es.js +17671 -18577
  8. package/dist/graphic-walker.es.js.map +1 -1
  9. package/dist/graphic-walker.umd.js +134 -134
  10. package/dist/graphic-walker.umd.js.map +1 -1
  11. package/dist/index.d.ts +2 -2
  12. package/dist/interfaces.d.ts +8 -0
  13. package/dist/lib/inferMeta.d.ts +20 -0
  14. package/dist/store/commonStore.d.ts +1 -1
  15. package/dist/store/index.d.ts +0 -1
  16. package/dist/store/visualSpecStore.d.ts +5 -5
  17. package/dist/utils/dataPrep.d.ts +6 -0
  18. package/dist/utils/index.d.ts +2 -2
  19. package/dist/utils/normalization.d.ts +1 -1
  20. package/dist/utils/save.d.ts +3 -3
  21. package/dist/utils/throttle.d.ts +1 -1
  22. package/dist/vis/temporalFormat.d.ts +10 -0
  23. package/package.json +1 -1
  24. package/src/App.tsx +27 -10
  25. package/src/components/button/base.ts +7 -0
  26. package/src/components/button/default.tsx +17 -0
  27. package/src/components/button/primary.tsx +17 -0
  28. package/src/dataSource/dataSelection/csvData.tsx +8 -10
  29. package/src/dataSource/dataSelection/publicData.tsx +4 -4
  30. package/src/dataSource/index.tsx +10 -12
  31. package/src/dataSource/table.tsx +33 -20
  32. package/src/dataSource/utils.ts +30 -35
  33. package/src/fields/datasetFields/dimFields.tsx +1 -5
  34. package/src/fields/datasetFields/meaFields.tsx +1 -5
  35. package/src/fields/obComponents/obFContainer.tsx +1 -5
  36. package/src/index.tsx +3 -4
  37. package/src/interfaces.ts +9 -0
  38. package/src/lib/inferMeta.ts +88 -0
  39. package/src/locales/en-US.json +6 -0
  40. package/src/locales/zh-CN.json +6 -0
  41. package/src/main.tsx +1 -1
  42. package/src/store/commonStore.ts +8 -3
  43. package/src/store/index.tsx +0 -2
  44. package/src/store/visualSpecStore.ts +245 -183
  45. package/src/utils/autoMark.ts +14 -14
  46. package/src/utils/dataPrep.ts +44 -0
  47. package/src/utils/index.ts +140 -128
  48. package/src/utils/normalization.ts +59 -51
  49. package/src/utils/save.ts +22 -21
  50. package/src/utils/throttle.ts +5 -1
  51. package/src/vis/react-vega.tsx +6 -10
  52. package/src/vis/temporalFormat.ts +66 -0
  53. package/dist/pitch/dnd-offset.d.ts +0 -2
  54. package/src/pitch/dnd-offset.ts +0 -64
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
- import { EditorProps } from './App';
2
+ import { IGWProps } from './App';
3
3
  import './empty_sheet.css';
4
4
  export declare const ShadowDomContext: React.Context<{
5
5
  root: ShadowRoot | null;
6
6
  }>;
7
- export declare const GraphicWalker: React.FC<EditorProps>;
7
+ export declare const GraphicWalker: React.FC<IGWProps>;
@@ -22,6 +22,14 @@ export interface IMutField {
22
22
  semanticType: ISemanticType;
23
23
  analyticType: IAnalyticType;
24
24
  }
25
+ export interface IUncertainMutField {
26
+ fid: string;
27
+ key?: string;
28
+ name?: string;
29
+ disable?: boolean;
30
+ semanticType: ISemanticType | '?';
31
+ analyticType: IAnalyticType | '?';
32
+ }
25
33
  export interface IField {
26
34
  /**
27
35
  * fid: key in data record
@@ -0,0 +1,20 @@
1
+ import { ISemanticType } from "visual-insights";
2
+ import { IMutField, IRow, IUncertainMutField } from "../interfaces";
3
+ /**
4
+ * check if this array is a date time array based on some common date format
5
+ * @param data string array
6
+ * @returns
7
+ */
8
+ export declare function isDateTimeArray(data: string[]): boolean;
9
+ /**
10
+ * 这里目前暂时包一层,是为了解耦具体的推断实现。后续这里要调整推断的逻辑。
11
+ * 需要讨论这一层是否和交互层有关,如果没有关系,这一层包裹可以不存在这里,而是在visual-insights中。
12
+ * @param data 原始数据
13
+ * @param fid 字段id
14
+ * @returns semantic type 列表
15
+ */
16
+ export declare function inferSemanticType(data: IRow[], fid: string): ISemanticType;
17
+ export declare function inferMeta(props: {
18
+ dataSource: IRow[];
19
+ fields: IUncertainMutField[];
20
+ }): IMutField[];
@@ -12,7 +12,6 @@ export declare class CommonStore {
12
12
  show: boolean;
13
13
  position: [number, number];
14
14
  };
15
- rootContainer: HTMLDivElement | null;
16
15
  filters: Filters;
17
16
  constructor();
18
17
  get currentDataset(): DataSet;
@@ -23,6 +22,7 @@ export declare class CommonStore {
23
22
  initTempDS(): void;
24
23
  updateTempFields(fields: IMutField[]): void;
25
24
  updateTempFieldAnalyticType(fieldKey: string, analyticType: IMutField['analyticType']): void;
25
+ updateTempFieldSemanticType(fieldKey: string, semanticType: IMutField['semanticType']): void;
26
26
  updateTempName(name: string): void;
27
27
  updateTempDS(rawData: IRow[]): void;
28
28
  updateTempSTDDS(dataset: IDataSetInfo): void;
@@ -9,7 +9,6 @@ export declare function destroyGWStore(): void;
9
9
  export declare function rebootGWStore(): void;
10
10
  interface StoreWrapperProps {
11
11
  keepAlive?: boolean;
12
- rootContainer?: HTMLDivElement | null;
13
12
  }
14
13
  export declare class StoreWrapper extends React.Component<StoreWrapperProps> {
15
14
  constructor(props: StoreWrapperProps);
@@ -83,13 +83,13 @@ export declare class VizSpecStore {
83
83
  initMetaState(dataset: DataSet): void;
84
84
  clearState(): void;
85
85
  setVisualConfig<K extends keyof IVisualConfig>(configKey: K, value: IVisualConfig[K]): void;
86
- transformCoord(coord: 'cartesian' | 'polar'): void;
86
+ transformCoord(coord: "cartesian" | "polar"): void;
87
87
  setChartLayout(props: {
88
- mode: IVisualConfig['size']['mode'];
88
+ mode: IVisualConfig["size"]["mode"];
89
89
  width?: number;
90
90
  height?: number;
91
91
  }): void;
92
- setExploration(value: Partial<IVisualConfig['exploration']>): void;
92
+ setExploration(value: Partial<IVisualConfig["exploration"]>): void;
93
93
  reorderField(stateKey: keyof DraggableFieldState, sourceIndex: number, destinationIndex: number): void;
94
94
  moveField(sourceKey: keyof DraggableFieldState, sourceIndex: number, destinationKey: keyof DraggableFieldState, destinationIndex: number): void;
95
95
  removeField(sourceKey: keyof DraggableFieldState, sourceIndex: number): void;
@@ -102,8 +102,8 @@ export declare class VizSpecStore {
102
102
  createLogField(stateKey: keyof DraggableFieldState, index: number): void;
103
103
  setFieldAggregator(stateKey: keyof DraggableFieldState, index: number, aggName: string): void;
104
104
  get sortCondition(): boolean;
105
- setFieldSort(stateKey: keyof DraggableFieldState, index: number, sortType: 'none' | 'ascending' | 'descending'): void;
106
- applyDefaultSort(sortType?: 'none' | 'ascending' | 'descending'): void;
105
+ setFieldSort(stateKey: keyof DraggableFieldState, index: number, sortType: "none" | "ascending" | "descending"): void;
106
+ applyDefaultSort(sortType?: "none" | "ascending" | "descending"): void;
107
107
  appendField(destinationKey: keyof DraggableFieldState, field: IViewField | undefined): void;
108
108
  renderSpec(spec: Specification): void;
109
109
  destroy(): void;
@@ -0,0 +1,6 @@
1
+ import { IRow } from "visual-insights";
2
+ import { IMutField } from "../interfaces";
3
+ export declare function guardDataKeys(data: IRow[], metas: IMutField[]): {
4
+ safeData: IRow[];
5
+ safeMetas: IMutField[];
6
+ };
@@ -1,4 +1,4 @@
1
- import { IRow, Filters, IMutField } from '../interfaces';
1
+ import { IRow, Filters, IMutField } from "../interfaces";
2
2
  export declare function checkMajorFactor(data: IRow[], childrenData: Map<any, IRow[]>, dimensions: string[], measures: string[]): {
3
3
  majorKey: string;
4
4
  majorSum: number;
@@ -9,7 +9,7 @@ export declare function checkChildOutlier(data: IRow[], childrenData: Map<any, I
9
9
  };
10
10
  export interface IPredicate {
11
11
  key: string;
12
- type: 'discrete' | 'continuous';
12
+ type: "discrete" | "continuous";
13
13
  range: Set<any> | [number, number];
14
14
  }
15
15
  export declare function getPredicates(selection: IRow[], dimensions: string[], measures: string[]): IPredicate[];
@@ -1,4 +1,4 @@
1
- import { IRow } from '../interfaces';
1
+ import { IRow } from "../interfaces";
2
2
  export declare function normalizeWithParent(data: IRow[], parentData: IRow[], measures: string[], syncScale: boolean): {
3
3
  normalizedData: IRow[];
4
4
  normalizedParentData: IRow[];
@@ -4,9 +4,9 @@ export declare function dumpsGWPureSpec(list: VisSpecWithHistory[]): IVisSpec[];
4
4
  export declare function parseGWPureSpec(list: IVisSpec[]): VisSpecWithHistory[];
5
5
  interface IStoInfo {
6
6
  datasets: IDataSet[];
7
- specList: ({
8
- [K in keyof IVisSpec]: K extends 'config' ? Partial<IVisSpec[K]> : IVisSpec[K];
9
- })[];
7
+ specList: {
8
+ [K in keyof IVisSpec]: K extends "config" ? Partial<IVisSpec[K]> : IVisSpec[K];
9
+ }[];
10
10
  dataSources: IDataSource[];
11
11
  }
12
12
  export declare function stringifyGWContent(info: IStoInfo): string;
@@ -1,5 +1,5 @@
1
1
  declare const throttle: (fn: () => void, time: number, options?: Partial<{
2
2
  leading: boolean;
3
3
  trailing: boolean;
4
- }>) => () => void;
4
+ }>) => (() => void);
5
5
  export default throttle;
@@ -0,0 +1,10 @@
1
+ export declare function getVegaTimeFormatRules(lang: string): {
2
+ dateTime: string;
3
+ date: string;
4
+ time: string;
5
+ periods: string[];
6
+ days: string[];
7
+ shortDays: string[];
8
+ months: string[];
9
+ shortMonths: string[];
10
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kanaries/graphic-walker",
3
- "version": "0.2.11",
3
+ "version": "0.2.12",
4
4
  "scripts": {
5
5
  "dev:front_end": "vite --host",
6
6
  "dev": "npm run dev:front_end",
package/src/App.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useState, useEffect, useRef } from "react";
1
+ import React, { useState, useEffect, useRef, useMemo } from "react";
2
2
  import { Specification } from "visual-insights";
3
3
  import { observer } from "mobx-react-lite";
4
4
  import { LightBulbIcon } from "@heroicons/react/24/outline";
@@ -20,9 +20,9 @@ import { preAnalysis, destroyWorker } from "./services";
20
20
  import VisNav from "./segments/visNav";
21
21
  import { mergeLocaleRes, setLocaleLanguage } from "./locales/i18n";
22
22
  import FilterField from "./fields/filterField";
23
- import PageNav from "./components/pageNav";
23
+ import { guardDataKeys } from "./utils/dataPrep";
24
24
 
25
- export interface EditorProps {
25
+ export interface IGWProps {
26
26
  dataSource?: IRow[];
27
27
  rawFields?: IMutField[];
28
28
  spec?: Specification;
@@ -30,11 +30,14 @@ export interface EditorProps {
30
30
  i18nLang?: string;
31
31
  i18nResources?: { [lang: string]: Record<string, string | any> };
32
32
  keepAlive?: boolean;
33
- fixContainer?: boolean;
33
+ /**
34
+ * auto parse field key into a safe string. default is true
35
+ */
36
+ fieldKeyGuard?: boolean;
34
37
  }
35
38
 
36
- const App: React.FC<EditorProps> = (props) => {
37
- const { dataSource = [], rawFields = [], spec, i18nLang = "en-US", i18nResources, hideDataSourceConfig } = props;
39
+ const App: React.FC<IGWProps> = (props) => {
40
+ const { dataSource = [], rawFields = [], spec, i18nLang = "en-US", i18nResources, hideDataSourceConfig, fieldKeyGuard = true } = props;
38
41
  const { commonStore, vizStore } = useGlobalStore();
39
42
  const [insightReady, setInsightReady] = useState<boolean>(true);
40
43
 
@@ -55,16 +58,30 @@ const App: React.FC<EditorProps> = (props) => {
55
58
  }
56
59
  }, [i18nLang, curLang]);
57
60
 
61
+ const safeDataset = useMemo(() => {
62
+ let safeData = dataSource;
63
+ let safeMetas = rawFields;
64
+ if (fieldKeyGuard) {
65
+ const { safeData: _safeData, safeMetas: _safeMetas } = guardDataKeys(dataSource, rawFields);
66
+ safeData = _safeData;
67
+ safeMetas = _safeMetas;
68
+ }
69
+ return {
70
+ safeData,
71
+ safeMetas,
72
+ };
73
+ }, [rawFields, dataSource, fieldKeyGuard]);
74
+
58
75
  // use as an embeding module, use outside datasource from props.
59
76
  useEffect(() => {
60
- if (dataSource.length > 0) {
77
+ if (safeDataset.safeData.length > 0 && safeDataset.safeMetas.length > 0) {
61
78
  commonStore.addAndUseDS({
62
79
  name: "context dataset",
63
- dataSource: dataSource,
64
- rawFields,
80
+ dataSource: safeDataset.safeData,
81
+ rawFields: safeDataset.safeMetas,
65
82
  });
66
83
  }
67
- }, [dataSource, rawFields]);
84
+ }, [safeDataset]);
68
85
 
69
86
  // do preparation analysis work when using a new dataset
70
87
  useEffect(() => {
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+
3
+ export interface ButtonBaseProps {
4
+ onClick: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
5
+ text: string;
6
+ disabled?: boolean;
7
+ }
@@ -0,0 +1,17 @@
1
+ import React from "react";
2
+ import { ButtonBaseProps } from "./base";
3
+
4
+ const DefaultButton: React.FC<ButtonBaseProps> = (props) => {
5
+ const { text, onClick, disabled } = props;
6
+ return (
7
+ <button
8
+ className="inline-block min-w-96 text-xs ml-2 pt-1 pb-1 pl-6 pr-6 border border-gray-500 rounded-sm hover:bg-gray-800 hover:border-gray-800 hover:text-white disabled:bg-gray-400 disabled:border-gray-400 disabled:text-white disabled:cursor-not-allowed disabled:text-white"
9
+ onClick={onClick}
10
+ disabled={disabled}
11
+ >
12
+ {text}
13
+ </button>
14
+ );
15
+ };
16
+
17
+ export default DefaultButton;
@@ -0,0 +1,17 @@
1
+ import React from "react";
2
+ import { ButtonBaseProps } from "./base";
3
+
4
+ const PrimaryButton: React.FC<ButtonBaseProps> = (props) => {
5
+ const { text, onClick, disabled } = props;
6
+ return (
7
+ <button
8
+ className="inline-block min-w-96 text-xs ml-2 pt-1 pb-1 pl-6 pr-6 border border-gray-800 bg-gray-800 text-white rounded-sm hover:bg-white border-gray-800 hover:text-gray-800 cursor-pointer disabled:bg-gray-400 disabled:border-gray-400 disabled:text-white disabled:cursor-not-allowed disabled:text-white"
9
+ onClick={onClick}
10
+ disabled={disabled}
11
+ >
12
+ {text}
13
+ </button>
14
+ );
15
+ };
16
+
17
+ export default PrimaryButton;
@@ -6,6 +6,8 @@ import styled from "styled-components";
6
6
  import { useGlobalStore } from "../../store";
7
7
  import { observer } from "mobx-react-lite";
8
8
  import { useTranslation } from "react-i18next";
9
+ import DefaultButton from "../../components/button/default";
10
+ import PrimaryButton from "../../components/button/primary";
9
11
 
10
12
  const Container = styled.div`
11
13
  overflow-x: auto;
@@ -44,25 +46,21 @@ const CSVData: React.FC<ICSVData> = (props) => {
44
46
  }}
45
47
  />
46
48
  <div className="mt-1 mb-1">
47
- <button
48
- className="inline-block min-w-96 text-xs mr-2 pt-1 pb-1 pl-6 pr-6 border border-gray-500 rounded-sm cursor-pointer hover:bg-gray-200"
49
+ <DefaultButton
49
50
  onClick={() => {
50
51
  if (fileRef.current) {
51
52
  fileRef.current.click();
52
53
  }
53
54
  }}
54
- >
55
- {t("open")}
56
- </button>
57
- <button
58
- className="inline-block min-w-96 text-xs mr-2 pt-1 pb-1 pl-6 pr-6 bg-black rounded-sm hover:bg-gray-500 text-white font-bold disabled:bg-gray-300"
55
+ text={t("open")}
56
+ />
57
+ <PrimaryButton
58
+ text={t("submit")}
59
59
  disabled={tmpDataSource.length === 0}
60
60
  onClick={() => {
61
61
  onSubmitData();
62
62
  }}
63
- >
64
- {t("submit")}
65
- </button>
63
+ />
66
64
  </div>
67
65
  <div className="my-2">
68
66
  <label className="block text-xs text-gray-800 mb-1 font-bold">{t("dataset_name")}</label>
@@ -4,6 +4,7 @@ import { DemoDataAssets, PUBLIC_DATA_LIST } from '../config'
4
4
  import { useGlobalStore } from '../../store';
5
5
  import { observer } from 'mobx-react-lite';
6
6
  import { useTranslation } from 'react-i18next';
7
+ import PrimaryButton from '../../components/button/primary';
7
8
 
8
9
 
9
10
  interface IPublicDataProps {
@@ -43,12 +44,11 @@ const PublicData: React.FC<IPublicDataProps> = props => {
43
44
  }
44
45
  </div>
45
46
  <hr className="m-1" />
46
- <button className="inline-block min-w-96 text-xs mr-2 pt-1 pb-1 pl-6 pr-6 bg-black rounded-sm hover:bg-gray-500 text-white font-bold disabled:bg-gray-300"
47
+ <PrimaryButton
47
48
  disabled={tmpDataSource.length === 0}
48
49
  onClick={() => { commonStore.commitTempDS() }}
49
- >
50
- {t('submit')}
51
- </button>
50
+ text={t('submit')}
51
+ />
52
52
  <hr className="m-1" />
53
53
  <Table />
54
54
  </div>
@@ -8,6 +8,7 @@ import { useGlobalStore } from '../store';
8
8
  import { download } from '../utils/save';
9
9
  import GwFile from './dataSelection/gwFile';
10
10
  import DataSelection from './dataSelection';
11
+ import DefaultButton from '../components/button/default';
11
12
 
12
13
  interface DSSegmentProps {
13
14
  preWorkDone: boolean;
@@ -39,28 +40,25 @@ const DataSourceSegment: React.FC<DSSegmentProps> = props => {
39
40
  ))}
40
41
  </select>
41
42
 
42
- <button className="inline-block min-w-96 text-xs ml-2 pt-1 pb-1 pl-6 pr-6 border border-gray-500 rounded-sm hover:bg-gray-200"
43
+ <DefaultButton
44
+ text={t('DataSource.buttons.create_dataset')}
43
45
  onClick={() => { commonStore.startDSBuildingTask() }}
44
- >
45
- {t('DataSource.buttons.create_dataset')}
46
- </button>
47
- <button className="inline-block min-w-96 text-xs ml-2 pt-1 pb-1 pl-6 pr-6 border border-gray-500 rounded-sm hover:bg-gray-200"
46
+ />
47
+ <DefaultButton
48
+ text={t('DataSource.buttons.export_as_file')}
48
49
  onClick={() => {
49
50
  const res = vizStore.exportAsRaw();
50
51
  download(res, 'graphic-walker-notebook.json', 'text/plain')
51
52
  }}
52
- >
53
- {t('DataSource.buttons.export_as_file')}
54
- </button>
55
- <button className="inline-block min-w-96 text-xs ml-2 pt-1 pb-1 pl-6 pr-6 border border-gray-500 rounded-sm hover:bg-gray-200"
53
+ />
54
+ <DefaultButton
55
+ text={t('DataSource.buttons.import_file')}
56
56
  onClick={() => {
57
57
  if (gwFileRef.current) {
58
58
  gwFileRef.current.click();
59
59
  }
60
60
  }}
61
- >
62
- {t('DataSource.buttons.import_file')}
63
- </button>
61
+ />
64
62
  {showDSPanel && (
65
63
  <Modal
66
64
  title={t('DataSource.dialog.create_data_source')}
@@ -36,16 +36,8 @@ const Container = styled.div`
36
36
  }
37
37
  }
38
38
  `;
39
- const TYPE_LIST = [
40
- {
41
- value: "dimension",
42
- label: "维度",
43
- },
44
- {
45
- value: "measure",
46
- label: "度量",
47
- },
48
- ];
39
+ const ANALYTIC_TYPE_LIST = ["dimension", "measure"];
40
+ const SEMANTIC_TYPE_LIST = ["nominal", "ordinal", "quantitative", "temporal"];
49
41
  // function getCellType(field: IMutField): 'number' | 'text' {
50
42
  // return field.dataType === 'number' || field.dataType === 'integer' ? 'number' : 'text';
51
43
  // }
@@ -79,9 +71,16 @@ const Table: React.FC<TableProps> = (props) => {
79
71
  const { t } = useTranslation();
80
72
 
81
73
  const analyticTypeList = useMemo<{ value: string; label: string }[]>(() => {
82
- return TYPE_LIST.map((at) => ({
83
- value: at.value,
84
- label: t(`constant.analytic_type.${at.value}`),
74
+ return ANALYTIC_TYPE_LIST.map((at) => ({
75
+ value: at,
76
+ label: t(`constant.analytic_type.${at}`),
77
+ }));
78
+ }, []);
79
+
80
+ const semanticTypeList = useMemo<{ value: string; label: string }[]>(() => {
81
+ return SEMANTIC_TYPE_LIST.map((st) => ({
82
+ value: st,
83
+ label: t(`constant.semantic_type.${st}`),
85
84
  }));
86
85
  }, []);
87
86
 
@@ -121,13 +120,27 @@ const Table: React.FC<TableProps> = (props) => {
121
120
  ))}
122
121
  </select>
123
122
  </div>
124
- <div
125
- className={
126
- "inline-block px-2.5 py-0.5 text-xs font-medium mt-1 rounded-full text-xs text-white " +
127
- getSemanticColors(field)
128
- }
129
- >
130
- {field.semanticType}
123
+ <div>
124
+ <select
125
+ className={
126
+ "inline-block px-2.5 py-0.5 text-xs font-medium mt-1 rounded-full text-xs text-white " +
127
+ getSemanticColors(field)
128
+ }
129
+ // className="border-b border-gray-200 bg-gray-50 pl-0 mt-2 font-light"
130
+ value={field.semanticType}
131
+ onChange={(e) => {
132
+ commonStore.updateTempFieldSemanticType(
133
+ field.fid,
134
+ e.target.value as IMutField["semanticType"]
135
+ );
136
+ }}
137
+ >
138
+ {semanticTypeList.map((type) => (
139
+ <option key={type.value} value={type.value}>
140
+ {type.label}
141
+ </option>
142
+ ))}
143
+ </select>
131
144
  </div>
132
145
  </div>
133
146
  </th>
@@ -1,47 +1,42 @@
1
- import { IRow, IMutField } from '../interfaces';
2
- import { Insight } from 'visual-insights';
1
+ import { IRow, IMutField } from "../interfaces";
2
+ import { inferMeta } from "../lib/inferMeta";
3
+ import { guardDataKeys } from "../utils/dataPrep";
3
4
 
4
5
  export function transData(dataSource: IRow[]): {
5
6
  dataSource: IRow[];
6
- fields: IMutField[]
7
+ fields: IMutField[];
7
8
  } {
8
- if (dataSource.length === 0) return {
9
- dataSource: [],
10
- fields: []
11
- };
12
- let ans: IRow[] = [];
9
+ if (dataSource.length === 0) {
10
+ return {
11
+ dataSource: [],
12
+ fields: [],
13
+ };
14
+ }
13
15
  const keys = Object.keys(dataSource[0]);
14
- // TODO: 冗余设计,单变量统计被进行了多次重复计算。另外对于这种不完整的分析任务,不建议使用VIEngine。
15
- const vie = new Insight.VIEngine();
16
- vie.setData(dataSource)
17
- .setFields(keys.map(k => ({
16
+ const metas = inferMeta({
17
+ dataSource,
18
+ fields: keys.map((k) => ({
19
+ fid: k,
18
20
  key: k,
19
- analyticType: '?',
20
- dataType: '?',
21
- semanticType: '?'
22
- })))
23
- // TODO: 结合上面的TODO,讨论,VIEngine是否要提供不需要进行univarSelection就提供summary的接口。
24
- // 这里我们使用了一种非原API设计时期待的用法,即强制指定单变量选择时要全选字段。但我们无法阻止对变量的转换。
25
- vie.univarSelection('percent', 1);
26
- const fields = vie.fields;
27
- for (let record of dataSource) {
21
+ analyticType: "?",
22
+ semanticType: "?",
23
+ })),
24
+ });
25
+ const { safeData, safeMetas } = guardDataKeys(dataSource, metas);
26
+ const finalData: IRow[] = [];
27
+ for (let record of safeData) {
28
28
  const newRecord: IRow = {};
29
- for (let field of fields) {
30
- if (field.dataType === 'number' || field.dataType === 'integer') {
31
- newRecord[field.key] = Number(record[field.key])
29
+ for (let field of safeMetas) {
30
+ if (field.semanticType === "quantitative") {
31
+ newRecord[field.fid] = Number(record[field.fid]);
32
32
  } else {
33
- newRecord[field.key] = record[field.key]
33
+ newRecord[field.fid] = record[field.fid];
34
34
  }
35
35
  }
36
- ans.push(newRecord);
36
+ finalData.push(newRecord);
37
37
  }
38
38
  return {
39
- dataSource: ans,
40
- fields: fields.map(f => ({
41
- fid: f.key,
42
- analyticType: f.analyticType,
43
- dataType: f.dataType,
44
- semanticType: f.semanticType
45
- }))
46
- }
47
- }
39
+ dataSource: finalData,
40
+ fields: safeMetas
41
+ };
42
+ }
@@ -4,22 +4,18 @@ import { observer } from 'mobx-react-lite';
4
4
  import { useGlobalStore } from '../../store';
5
5
  import DataTypeIcon from '../../components/dataTypeIcon';
6
6
  import { FieldPill } from './fieldPill';
7
- import { fixDraggableOffset } from '../../pitch/dnd-offset';
8
7
 
9
8
  interface Props {
10
9
  provided: DroppableProvided;
11
10
  }
12
11
  const DimFields: React.FC<Props> = props => {
13
12
  const { provided } = props;
14
- const { vizStore, commonStore } = useGlobalStore();
13
+ const { vizStore } = useGlobalStore();
15
14
  const dimensions = vizStore.draggableFieldState.dimensions;
16
15
  return <div {...provided.droppableProps} ref={provided.innerRef}>
17
16
  {dimensions.map((f, index) => (
18
17
  <Draggable key={f.dragId} draggableId={f.dragId} index={index}>
19
18
  {(provided, snapshot) => {
20
- if (snapshot.isDragging && provided.draggableProps.style) {
21
- fixDraggableOffset(provided, commonStore.rootContainer)
22
- }
23
19
 
24
20
  return (
25
21
  <>
@@ -4,22 +4,18 @@ import { observer } from 'mobx-react-lite';
4
4
  import { useGlobalStore } from '../../store';
5
5
  import DataTypeIcon from '../../components/dataTypeIcon';
6
6
  import { FieldPill } from './fieldPill';
7
- import { fixDraggableOffset } from '../../pitch/dnd-offset';
8
7
 
9
8
  interface Props {
10
9
  provided: DroppableProvided;
11
10
  }
12
11
  const MeaFields: React.FC<Props> = props => {
13
12
  const { provided } = props;
14
- const { vizStore, commonStore } = useGlobalStore();
13
+ const { vizStore } = useGlobalStore();
15
14
  const measures = vizStore.draggableFieldState.measures;
16
15
  return <div {...provided.droppableProps} ref={provided.innerRef}>
17
16
  {measures.map((f, index) => (
18
17
  <Draggable key={f.dragId} draggableId={f.dragId} index={index}>
19
18
  {(provided, snapshot) => {
20
- if (snapshot.isDragging && provided.draggableProps.style) {
21
- fixDraggableOffset(provided, commonStore.rootContainer)
22
- }
23
19
  return (
24
20
  <>
25
21
  <FieldPill
@@ -8,7 +8,6 @@ import {
8
8
  } from "@kanaries/react-beautiful-dnd";
9
9
  import { IDraggableStateKey } from '../../interfaces';
10
10
  import OBPill from './obPill';
11
- import { fixDraggableOffset } from '../../pitch/dnd-offset';
12
11
 
13
12
  interface FieldContainerProps {
14
13
  provided: DroppableProvided
@@ -19,7 +18,7 @@ interface FieldContainerProps {
19
18
  }
20
19
  const OBFieldContainer: React.FC<FieldContainerProps> = props => {
21
20
  const { provided, dkey } = props;
22
- const { vizStore, commonStore } = useGlobalStore();
21
+ const { vizStore} = useGlobalStore();
23
22
  const { draggableFieldState } = vizStore;
24
23
  return <FieldsContainer
25
24
  {...provided.droppableProps}
@@ -29,9 +28,6 @@ const OBFieldContainer: React.FC<FieldContainerProps> = props => {
29
28
  {draggableFieldState[dkey.id].map((f, index) => (
30
29
  <Draggable key={f.dragId} draggableId={f.dragId} index={index}>
31
30
  {(provided, snapshot) => {
32
- if (snapshot.isDragging && provided.draggableProps.style) {
33
- fixDraggableOffset(provided, commonStore.rootContainer)
34
- }
35
31
  return (
36
32
  <OBPill dkey={dkey} fIndex={index} provided={provided} />
37
33
  );