@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.
- package/dist/App.d.ts +6 -3
- package/dist/assets/explainer.worker-8428eb12.js.map +1 -1
- package/dist/components/button/base.d.ts +6 -0
- package/dist/components/button/default.d.ts +4 -0
- package/dist/components/button/primary.d.ts +4 -0
- package/dist/components/dataTable/index.d.ts +10 -0
- package/dist/components/dataTable/pagination.d.ts +10 -0
- package/dist/components/tabs/defaultTab.d.ts +14 -0
- package/dist/components/tabs/{pureTab.d.ts → editableTab.d.ts} +2 -2
- package/dist/dataSource/datasetConfig/index.d.ts +3 -0
- package/dist/dataSource/utils.d.ts +1 -1
- package/dist/graphic-walker.es.js +17347 -18050
- package/dist/graphic-walker.es.js.map +1 -1
- package/dist/graphic-walker.umd.js +140 -138
- package/dist/graphic-walker.umd.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/interfaces.d.ts +12 -0
- package/dist/lib/inferMeta.d.ts +20 -0
- package/dist/segments/segmentNav.d.ts +3 -0
- package/dist/store/commonStore.d.ts +8 -2
- package/dist/store/index.d.ts +0 -1
- package/dist/store/visualSpecStore.d.ts +5 -5
- package/dist/utils/dataPrep.d.ts +6 -0
- package/dist/utils/index.d.ts +2 -2
- package/dist/utils/normalization.d.ts +1 -1
- package/dist/utils/save.d.ts +3 -3
- package/dist/utils/throttle.d.ts +1 -1
- package/dist/vis/temporalFormat.d.ts +10 -0
- package/package.json +1 -1
- package/src/App.tsx +93 -51
- package/src/components/button/base.ts +7 -0
- package/src/components/button/default.tsx +17 -0
- package/src/components/button/primary.tsx +17 -0
- package/src/components/dataTable/index.tsx +187 -0
- package/src/components/dataTable/pagination.tsx +44 -0
- package/src/components/tabs/defaultTab.tsx +43 -0
- package/src/components/tabs/{pureTab.tsx → editableTab.tsx} +3 -3
- package/src/dataSource/dataSelection/csvData.tsx +8 -10
- package/src/dataSource/dataSelection/index.tsx +1 -1
- package/src/dataSource/dataSelection/publicData.tsx +4 -4
- package/src/dataSource/datasetConfig/index.tsx +21 -0
- package/src/dataSource/index.tsx +10 -12
- package/src/dataSource/table.tsx +11 -142
- package/src/dataSource/utils.ts +30 -35
- package/src/fields/datasetFields/dimFields.tsx +1 -5
- package/src/fields/datasetFields/meaFields.tsx +1 -5
- package/src/fields/obComponents/obFContainer.tsx +1 -5
- package/src/index.tsx +3 -4
- package/src/interfaces.ts +14 -0
- package/src/lib/inferMeta.ts +88 -0
- package/src/locales/en-US.json +14 -0
- package/src/locales/zh-CN.json +14 -0
- package/src/main.tsx +1 -1
- package/src/renderer/index.tsx +1 -0
- package/src/segments/segmentNav.tsx +58 -0
- package/src/segments/visNav.tsx +2 -2
- package/src/store/commonStore.ts +36 -5
- package/src/store/index.tsx +0 -2
- package/src/store/visualSpecStore.ts +245 -183
- package/src/utils/autoMark.ts +14 -14
- package/src/utils/dataPrep.ts +44 -0
- package/src/utils/index.ts +140 -128
- package/src/utils/normalization.ts +59 -51
- package/src/utils/save.ts +22 -21
- package/src/utils/throttle.ts +5 -1
- package/src/vis/react-vega.tsx +6 -10
- package/src/vis/temporalFormat.ts +66 -0
- package/dist/pitch/dnd-offset.d.ts +0 -2
- package/src/pitch/dnd-offset.ts +0 -64
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
interface PaginationProps {
|
|
4
|
+
from: number;
|
|
5
|
+
to: number;
|
|
6
|
+
total: number;
|
|
7
|
+
onPrev: () => void;
|
|
8
|
+
onNext: () => void;
|
|
9
|
+
}
|
|
10
|
+
export default function Pagination(props: PaginationProps) {
|
|
11
|
+
const { from , to, total, onNext, onPrev } = props;
|
|
12
|
+
const { t } = useTranslation();
|
|
13
|
+
return (
|
|
14
|
+
<nav
|
|
15
|
+
className="flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6"
|
|
16
|
+
aria-label="Pagination"
|
|
17
|
+
>
|
|
18
|
+
<div className="hidden sm:block">
|
|
19
|
+
<p className="text-sm text-gray-700">
|
|
20
|
+
Showing <span className="font-medium">{from}</span> to <span className="font-medium">{to}</span> of{" "}
|
|
21
|
+
<span className="font-medium">{total}</span> results
|
|
22
|
+
</p>
|
|
23
|
+
</div>
|
|
24
|
+
<div className="flex flex-1 justify-between sm:justify-end">
|
|
25
|
+
<button
|
|
26
|
+
onClick={() => {
|
|
27
|
+
onPrev();
|
|
28
|
+
}}
|
|
29
|
+
className="relative inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50"
|
|
30
|
+
>
|
|
31
|
+
{t('actions.prev')}
|
|
32
|
+
</button>
|
|
33
|
+
<button
|
|
34
|
+
onClick={() => {
|
|
35
|
+
onNext()
|
|
36
|
+
}}
|
|
37
|
+
className="relative ml-3 inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50"
|
|
38
|
+
>
|
|
39
|
+
{t('actions.next')}
|
|
40
|
+
</button>
|
|
41
|
+
</div>
|
|
42
|
+
</nav>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React, { ReactElement } from "react";
|
|
2
|
+
|
|
3
|
+
function classNames(...classes: string[]) {
|
|
4
|
+
return classes.filter(Boolean).join(' ')
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface ITabOption {
|
|
8
|
+
label: string | ReactElement;
|
|
9
|
+
key: string;
|
|
10
|
+
}
|
|
11
|
+
interface DefaultProps {
|
|
12
|
+
tabs: ITabOption[];
|
|
13
|
+
selectedKey: string;
|
|
14
|
+
onSelected: (selectedKey: string, index: number) => void;
|
|
15
|
+
allowEdit?: boolean;
|
|
16
|
+
onEditLabel?: (label: string, index: number) => void;
|
|
17
|
+
}
|
|
18
|
+
export default function Default(props: DefaultProps) {
|
|
19
|
+
const { tabs, selectedKey, onSelected } = props;
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div className="border-b border-gray-200 mb-2" >
|
|
23
|
+
<nav className="-mb-px flex space-x-8" role="tablist" aria-label="Tabs">
|
|
24
|
+
{tabs.map((tab, tabIndex) => (
|
|
25
|
+
<span
|
|
26
|
+
role="tab"
|
|
27
|
+
tabIndex={0}
|
|
28
|
+
onClick={() => {
|
|
29
|
+
onSelected(tab.key, tabIndex)
|
|
30
|
+
}}
|
|
31
|
+
key={tab.key}
|
|
32
|
+
className={classNames(
|
|
33
|
+
tab.key === selectedKey
|
|
34
|
+
? 'border-indigo-500 text-indigo-600'
|
|
35
|
+
: 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300',
|
|
36
|
+
'whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm'
|
|
37
|
+
)}
|
|
38
|
+
>{tab.label}</span>
|
|
39
|
+
))}
|
|
40
|
+
</nav>
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
@@ -11,14 +11,14 @@ export interface ITabOption {
|
|
|
11
11
|
key: string;
|
|
12
12
|
options?: Record<string, any>;
|
|
13
13
|
}
|
|
14
|
-
interface
|
|
14
|
+
interface EditableTabsProps {
|
|
15
15
|
tabs: ITabOption[];
|
|
16
16
|
selectedKey: string;
|
|
17
17
|
onSelected: (selectedKey: string, index: number) => void;
|
|
18
18
|
allowEdit?: boolean;
|
|
19
19
|
onEditLabel?: (label: string, index: number) => void;
|
|
20
20
|
}
|
|
21
|
-
export default function
|
|
21
|
+
export default function EditableTabs(props: EditableTabsProps) {
|
|
22
22
|
const { tabs, selectedKey, onSelected, allowEdit, onEditLabel } = props;
|
|
23
23
|
const [editList, setEditList] = useState<boolean[]>([]);
|
|
24
24
|
const { t } = useTranslation();
|
|
@@ -66,7 +66,7 @@ export default function PureTabs(props: PureTabsProps) {
|
|
|
66
66
|
tab.key === selectedKey
|
|
67
67
|
? "text-black bg-gray-100"
|
|
68
68
|
: "text-gray-500 hover:text-gray-700",
|
|
69
|
-
"whitespace-nowrap border-gray-200 py-1 px-2 border-
|
|
69
|
+
"whitespace-nowrap border-gray-200 py-1 px-2 border-r border-t border-b pr-6 text-sm cursor-pointer"
|
|
70
70
|
)}
|
|
71
71
|
/>
|
|
72
72
|
))}
|
|
@@ -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
|
-
<
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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>
|
|
@@ -3,7 +3,7 @@ import { useState } from "react";
|
|
|
3
3
|
import CSVData from "./csvData";
|
|
4
4
|
import PublicData from "./publicData";
|
|
5
5
|
import { useTranslation } from "react-i18next";
|
|
6
|
-
import PureTabs from "../../components/tabs/
|
|
6
|
+
import PureTabs from "../../components/tabs/editableTab";
|
|
7
7
|
|
|
8
8
|
const DataSelection: React.FC = (props) => {
|
|
9
9
|
const [sourceType, setSourceType] = useState<"file" | "public">("file");
|
|
@@ -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
|
-
<
|
|
47
|
+
<PrimaryButton
|
|
47
48
|
disabled={tmpDataSource.length === 0}
|
|
48
49
|
onClick={() => { commonStore.commitTempDS() }}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
</button>
|
|
50
|
+
text={t('submit')}
|
|
51
|
+
/>
|
|
52
52
|
<hr className="m-1" />
|
|
53
53
|
<Table />
|
|
54
54
|
</div>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import DatasetTable from "../../components/dataTable";
|
|
3
|
+
import { observer } from "mobx-react-lite";
|
|
4
|
+
import { useGlobalStore } from "../../store";
|
|
5
|
+
|
|
6
|
+
const DatasetConfig: React.FC = (props) => {
|
|
7
|
+
const { commonStore, vizStore } = useGlobalStore();
|
|
8
|
+
const { currentDataset } = commonStore;
|
|
9
|
+
const { dataSource, rawFields } = currentDataset;
|
|
10
|
+
return (
|
|
11
|
+
<div>
|
|
12
|
+
<DatasetTable size={100} data={dataSource} metas={rawFields}
|
|
13
|
+
onMetaChange={(fid, fIndex, diffMeta) => {
|
|
14
|
+
commonStore.updateCurrentDatasetMetas(fid, diffMeta)
|
|
15
|
+
}}
|
|
16
|
+
/>
|
|
17
|
+
</div>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default observer(DatasetConfig);
|
package/src/dataSource/index.tsx
CHANGED
|
@@ -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
|
-
<
|
|
43
|
+
<DefaultButton
|
|
44
|
+
text={t('DataSource.buttons.create_dataset')}
|
|
43
45
|
onClick={() => { commonStore.startDSBuildingTask() }}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
54
|
-
|
|
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')}
|
package/src/dataSource/table.tsx
CHANGED
|
@@ -1,158 +1,27 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import styled from "styled-components";
|
|
1
|
+
import React from "react";
|
|
3
2
|
import { observer } from "mobx-react-lite";
|
|
4
|
-
import { IMutField } from "../interfaces";
|
|
5
3
|
import { useGlobalStore } from "../store";
|
|
6
|
-
import
|
|
4
|
+
import DataTable from "../components/dataTable";
|
|
5
|
+
import { toJS } from "mobx";
|
|
7
6
|
|
|
8
7
|
interface TableProps {
|
|
9
8
|
size?: number;
|
|
10
9
|
}
|
|
11
|
-
const Container = styled.div`
|
|
12
|
-
overflow-x: auto;
|
|
13
|
-
table {
|
|
14
|
-
box-sizing: content-box;
|
|
15
|
-
border-collapse: collapse;
|
|
16
|
-
font-size: 12px;
|
|
17
|
-
thead {
|
|
18
|
-
th {
|
|
19
|
-
}
|
|
20
|
-
th.number {
|
|
21
|
-
border-top: 3px solid #5cdbd3;
|
|
22
|
-
}
|
|
23
|
-
th.text {
|
|
24
|
-
border-top: 3px solid #69c0ff;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
tbody {
|
|
28
|
-
td {
|
|
29
|
-
}
|
|
30
|
-
td.number {
|
|
31
|
-
text-align: right;
|
|
32
|
-
}
|
|
33
|
-
td.text {
|
|
34
|
-
text-align: left;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
`;
|
|
39
|
-
const TYPE_LIST = [
|
|
40
|
-
{
|
|
41
|
-
value: "dimension",
|
|
42
|
-
label: "维度",
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
value: "measure",
|
|
46
|
-
label: "度量",
|
|
47
|
-
},
|
|
48
|
-
];
|
|
49
|
-
// function getCellType(field: IMutField): 'number' | 'text' {
|
|
50
|
-
// return field.dataType === 'number' || field.dataType === 'integer' ? 'number' : 'text';
|
|
51
|
-
// }
|
|
52
|
-
function getHeaderType(field: IMutField): "number" | "text" {
|
|
53
|
-
return field.analyticType === "dimension" ? "text" : "number";
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function getHeaderClassNames(field: IMutField) {
|
|
57
|
-
return field.analyticType === "dimension" ? "border-t-4 border-blue-400" : "border-t-4 border-teal-400";
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function getSemanticColors(field: IMutField): string {
|
|
61
|
-
switch (field.semanticType) {
|
|
62
|
-
case "nominal":
|
|
63
|
-
return "bg-indigo-100 text-indigo-800";
|
|
64
|
-
case "ordinal":
|
|
65
|
-
return "bg-purple-100 text-purple-800"
|
|
66
|
-
case "quantitative":
|
|
67
|
-
return "bg-green-100 text-green-800"
|
|
68
|
-
case "temporal":
|
|
69
|
-
return "bg-yellow-100 text-yellow-800"
|
|
70
|
-
default:
|
|
71
|
-
return "bg-gray-400";
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
10
|
|
|
75
11
|
const Table: React.FC<TableProps> = (props) => {
|
|
76
12
|
const { size = 10 } = props;
|
|
77
13
|
const { commonStore } = useGlobalStore();
|
|
78
14
|
const { tmpDSRawFields, tmpDataSource } = commonStore;
|
|
79
|
-
const { t } = useTranslation();
|
|
80
|
-
|
|
81
|
-
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}`),
|
|
85
|
-
}));
|
|
86
|
-
}, []);
|
|
87
15
|
|
|
88
16
|
return (
|
|
89
|
-
<
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
getHeaderClassNames(field) +
|
|
98
|
-
" whitespace-nowrap py-3.5 px-6 text-left text-xs font-semibold text-gray-900 sm:pl-6"
|
|
99
|
-
}
|
|
100
|
-
>
|
|
101
|
-
<b>{field.name || field.fid}</b>
|
|
102
|
-
<div>
|
|
103
|
-
<select
|
|
104
|
-
className={
|
|
105
|
-
"px-2 py font-normal mt-2 rounded-full text-xs text-white " +
|
|
106
|
-
(field.analyticType === "dimension" ? "bg-blue-500" : "bg-teal-500")
|
|
107
|
-
}
|
|
108
|
-
// className="border-b border-gray-200 bg-gray-50 pl-0 mt-2 font-light"
|
|
109
|
-
value={field.analyticType}
|
|
110
|
-
onChange={(e) => {
|
|
111
|
-
commonStore.updateTempFieldAnalyticType(
|
|
112
|
-
field.fid,
|
|
113
|
-
e.target.value as IMutField["analyticType"]
|
|
114
|
-
);
|
|
115
|
-
}}
|
|
116
|
-
>
|
|
117
|
-
{analyticTypeList.map((type) => (
|
|
118
|
-
<option key={type.value} value={type.value}>
|
|
119
|
-
{type.label}
|
|
120
|
-
</option>
|
|
121
|
-
))}
|
|
122
|
-
</select>
|
|
123
|
-
</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}
|
|
131
|
-
</div>
|
|
132
|
-
</div>
|
|
133
|
-
</th>
|
|
134
|
-
))}
|
|
135
|
-
</tr>
|
|
136
|
-
</thead>
|
|
137
|
-
<tbody className="divide-y divide-gray-200 bg-white">
|
|
138
|
-
{tmpDataSource.slice(0, size).map((record, index) => (
|
|
139
|
-
<tr className={"divide-x divide-gray-200 " + (index % 2 ? "bg-gray-50" : "")} key={index}>
|
|
140
|
-
{tmpDSRawFields.map((field) => (
|
|
141
|
-
<td
|
|
142
|
-
key={field.fid + index}
|
|
143
|
-
className={
|
|
144
|
-
getHeaderType(field) +
|
|
145
|
-
" whitespace-nowrap py-2 pl-4 pr-3 text-xs text-gray-500 sm:pl-6"
|
|
146
|
-
}
|
|
147
|
-
>
|
|
148
|
-
{record[field.fid]}
|
|
149
|
-
</td>
|
|
150
|
-
))}
|
|
151
|
-
</tr>
|
|
152
|
-
))}
|
|
153
|
-
</tbody>
|
|
154
|
-
</table>
|
|
155
|
-
</Container>
|
|
17
|
+
<DataTable
|
|
18
|
+
size={size}
|
|
19
|
+
metas={toJS(tmpDSRawFields)}
|
|
20
|
+
data={tmpDataSource}
|
|
21
|
+
onMetaChange={(fid, fIndex, diffMeta) => {
|
|
22
|
+
commonStore.updateTempDatasetMetas(fid, diffMeta);
|
|
23
|
+
}}
|
|
24
|
+
/>
|
|
156
25
|
);
|
|
157
26
|
};
|
|
158
27
|
|
package/src/dataSource/utils.ts
CHANGED
|
@@ -1,47 +1,42 @@
|
|
|
1
|
-
import { IRow, IMutField } from
|
|
2
|
-
import {
|
|
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)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
if (dataSource.length === 0) {
|
|
10
|
+
return {
|
|
11
|
+
dataSource: [],
|
|
12
|
+
fields: [],
|
|
13
|
+
};
|
|
14
|
+
}
|
|
13
15
|
const keys = Object.keys(dataSource[0]);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
const metas = inferMeta({
|
|
17
|
+
dataSource,
|
|
18
|
+
fields: keys.map((k) => ({
|
|
19
|
+
fid: k,
|
|
18
20
|
key: k,
|
|
19
|
-
analyticType:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
30
|
-
if (field.
|
|
31
|
-
newRecord[field.
|
|
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.
|
|
33
|
+
newRecord[field.fid] = record[field.fid];
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
-
|
|
36
|
+
finalData.push(newRecord);
|
|
37
37
|
}
|
|
38
38
|
return {
|
|
39
|
-
dataSource:
|
|
40
|
-
fields:
|
|
41
|
-
|
|
42
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
);
|
package/src/index.tsx
CHANGED
|
@@ -3,7 +3,7 @@ import { StyleSheetManager } from 'styled-components';
|
|
|
3
3
|
import root from 'react-shadow';
|
|
4
4
|
import { DOM } from '@kanaries/react-beautiful-dnd';
|
|
5
5
|
import { observer } from 'mobx-react-lite';
|
|
6
|
-
import App, {
|
|
6
|
+
import App, { IGWProps } from './App';
|
|
7
7
|
import { StoreWrapper } from './store/index';
|
|
8
8
|
import { FieldsContextWrapper } from './fields/fieldsContext';
|
|
9
9
|
|
|
@@ -14,10 +14,9 @@ import style from './index.css?inline';
|
|
|
14
14
|
|
|
15
15
|
export const ShadowDomContext = createContext<{ root: ShadowRoot | null }>({ root: null });
|
|
16
16
|
|
|
17
|
-
export const GraphicWalker: React.FC<
|
|
17
|
+
export const GraphicWalker: React.FC<IGWProps> = observer(props => {
|
|
18
18
|
const [shadowRoot, setShadowRoot] = useState<ShadowRoot | null>(null);
|
|
19
19
|
const rootRef = useRef<HTMLDivElement>(null);
|
|
20
|
-
const fixContainer = props.fixContainer ?? false;
|
|
21
20
|
|
|
22
21
|
useEffect(() => {
|
|
23
22
|
if (rootRef.current) {
|
|
@@ -38,7 +37,7 @@ export const GraphicWalker: React.FC<EditorProps> = observer(props => {
|
|
|
38
37
|
<style>{style}</style>
|
|
39
38
|
{shadowRoot && (
|
|
40
39
|
<StyleSheetManager target={shadowRoot}>
|
|
41
|
-
<StoreWrapper keepAlive={props.keepAlive}
|
|
40
|
+
<StoreWrapper keepAlive={props.keepAlive}>
|
|
42
41
|
<FieldsContextWrapper>
|
|
43
42
|
<ShadowDomContext.Provider value={{ root: shadowRoot }}>
|
|
44
43
|
<App {...props} />
|
package/src/interfaces.ts
CHANGED
|
@@ -27,6 +27,15 @@ export interface IMutField {
|
|
|
27
27
|
analyticType: IAnalyticType;
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
+
export interface IUncertainMutField {
|
|
31
|
+
fid: string;
|
|
32
|
+
key?: string;
|
|
33
|
+
name?: string;
|
|
34
|
+
disable?: boolean;
|
|
35
|
+
semanticType: ISemanticType | '?';
|
|
36
|
+
analyticType: IAnalyticType | '?';
|
|
37
|
+
}
|
|
38
|
+
|
|
30
39
|
export interface IField {
|
|
31
40
|
/**
|
|
32
41
|
* fid: key in data record
|
|
@@ -169,4 +178,9 @@ export interface IVisSpec {
|
|
|
169
178
|
readonly name?: [string, Record<string, any>?];
|
|
170
179
|
readonly encodings: DeepReadonly<DraggableFieldState>;
|
|
171
180
|
readonly config: DeepReadonly<IVisualConfig>;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export enum ISegmentKey {
|
|
184
|
+
vis = 'vis',
|
|
185
|
+
data = 'data'
|
|
172
186
|
}
|