@kanaries/graphic-walker 0.2.12 → 0.2.14
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 +4 -2
- package/dist/assets/explainer.worker-8428eb12.js.map +1 -1
- package/dist/components/button/base.d.ts +1 -0
- package/dist/components/button/defaultMini.d.ts +4 -0
- package/dist/components/button/primaryMini.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/dropdownContext/index.d.ts +13 -0
- package/dist/components/dropdownSelect/index.d.ts +17 -0
- package/dist/components/modal.d.ts +1 -0
- package/dist/components/tabs/defaultTab.d.ts +14 -0
- package/dist/components/tabs/{pureTab.d.ts → editableTab.d.ts} +2 -2
- package/dist/components/toolbar/toolbar-item.d.ts +1 -0
- package/dist/dataSource/dataSelection/config.d.ts +2 -0
- package/dist/dataSource/datasetConfig/index.d.ts +3 -0
- package/dist/dataSource/index.d.ts +1 -1
- package/dist/fields/datasetFields/dimFields.d.ts +2 -2
- package/dist/fields/datasetFields/meaFields.d.ts +2 -2
- package/dist/fields/encodeFields/singleEncodeDropDown.d.ts +17 -0
- package/dist/fields/encodeFields/singleEncodeEditor.d.ts +11 -0
- package/dist/fields/obComponents/obPill.d.ts +3 -3
- package/dist/graphic-walker.es.js +21038 -19133
- package/dist/graphic-walker.es.js.map +1 -1
- package/dist/graphic-walker.umd.js +247 -170
- package/dist/graphic-walker.umd.js.map +1 -1
- package/dist/insightBoard/index.d.ts +1 -1
- package/dist/interfaces.d.ts +5 -0
- package/dist/renderer/index.d.ts +3 -1
- package/dist/segments/segmentNav.d.ts +3 -0
- package/dist/store/commonStore.d.ts +7 -1
- package/dist/store/visualSpecStore.d.ts +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/media.d.ts +2 -0
- package/dist/vis/react-vega.d.ts +2 -0
- package/dist/vis/theme.d.ts +130 -0
- package/package.json +2 -1
- package/src/App.tsx +74 -46
- package/src/components/button/base.ts +1 -0
- package/src/components/button/default.tsx +6 -2
- package/src/components/button/defaultMini.tsx +17 -0
- package/src/components/button/primary.tsx +6 -2
- package/src/components/button/primaryMini.tsx +21 -0
- package/src/components/callout.tsx +4 -1
- package/src/components/clickMenu.tsx +4 -2
- package/src/components/container.tsx +9 -0
- package/src/components/dataTable/index.tsx +177 -0
- package/src/components/dataTable/pagination.tsx +44 -0
- package/src/components/dataTypeIcon.tsx +1 -1
- package/src/components/dropdownContext/index.tsx +64 -0
- package/src/components/dropdownSelect/index.tsx +92 -0
- package/src/components/modal.tsx +26 -14
- package/src/components/tabs/defaultTab.tsx +43 -0
- package/src/components/tabs/{pureTab.tsx → editableTab.tsx} +7 -7
- package/src/components/toolbar/components.tsx +18 -1
- package/src/components/toolbar/index.tsx +5 -0
- package/src/components/toolbar/toolbar-button.tsx +6 -1
- package/src/components/toolbar/toolbar-item.tsx +4 -0
- package/src/components/toolbar/toolbar-select-button.tsx +21 -4
- package/src/components/toolbar/toolbar-toggle-button.tsx +6 -1
- package/src/components/tooltip.tsx +4 -1
- package/src/dataSource/dataSelection/config.ts +28 -0
- package/src/dataSource/dataSelection/csvData.tsx +77 -32
- package/src/dataSource/dataSelection/gwFile.tsx +0 -8
- package/src/dataSource/dataSelection/index.tsx +1 -2
- package/src/dataSource/dataSelection/publicData.tsx +2 -3
- package/src/dataSource/datasetConfig/index.tsx +21 -0
- package/src/dataSource/index.tsx +81 -61
- package/src/dataSource/table.tsx +11 -155
- package/src/fields/aestheticFields.tsx +3 -1
- package/src/fields/components.tsx +21 -2
- package/src/fields/datasetFields/dimFields.tsx +43 -35
- package/src/fields/datasetFields/index.tsx +2 -2
- package/src/fields/datasetFields/meaFields.tsx +73 -47
- package/src/fields/encodeFields/singleEncodeDropDown.tsx +92 -0
- package/src/fields/encodeFields/singleEncodeEditor.tsx +78 -0
- package/src/fields/filterField/filterEditDialog.tsx +2 -1
- package/src/fields/filterField/filterPill.tsx +1 -1
- package/src/fields/filterField/slider.tsx +1 -1
- package/src/fields/filterField/tabs.tsx +11 -21
- package/src/fields/obComponents/obPill.tsx +65 -35
- package/src/index.css +13 -0
- package/src/insightBoard/index.tsx +24 -23
- package/src/insightBoard/radioGroupButtons.tsx +7 -0
- package/src/interfaces.ts +6 -0
- package/src/lib/inferMeta.ts +1 -1
- package/src/locales/en-US.json +14 -3
- package/src/locales/zh-CN.json +12 -1
- package/src/main.tsx +1 -1
- package/src/renderer/index.tsx +3 -1
- package/src/segments/segmentNav.tsx +58 -0
- package/src/segments/visNav.tsx +2 -2
- package/src/store/commonStore.ts +28 -2
- package/src/store/visualSpecStore.ts +16 -0
- package/src/utils/index.ts +19 -0
- package/src/utils/media.ts +26 -0
- package/src/utils/normalization.ts +2 -1
- package/src/vis/react-vega.tsx +20 -4
- package/src/vis/theme.ts +126 -0
- package/src/visualSettings/index.tsx +24 -8
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import React, { useMemo } from "react";
|
|
2
|
+
import { IDraggableStateKey } from "../../interfaces";
|
|
3
|
+
import { observer } from "mobx-react-lite";
|
|
4
|
+
import { useGlobalStore } from "../../store";
|
|
5
|
+
import DropdownSelect from "./singleEncodeDropDown";
|
|
6
|
+
import { DroppableProvided } from "react-beautiful-dnd";
|
|
7
|
+
import { ChevronUpDownIcon, TrashIcon } from "@heroicons/react/24/outline";
|
|
8
|
+
import { useTranslation } from "react-i18next";
|
|
9
|
+
import { COUNT_FIELD_ID } from "../../constants";
|
|
10
|
+
import DropdownContext from "../../components/dropdownContext";
|
|
11
|
+
import { AGGREGATOR_LIST } from "../fieldsContext";
|
|
12
|
+
import { Draggable, DroppableStateSnapshot } from "@kanaries/react-beautiful-dnd";
|
|
13
|
+
|
|
14
|
+
interface SingleEncodeEditorProps {
|
|
15
|
+
dkey: IDraggableStateKey;
|
|
16
|
+
provided: DroppableProvided;
|
|
17
|
+
snapshot: DroppableStateSnapshot;
|
|
18
|
+
}
|
|
19
|
+
const SingleEncodeEditor: React.FC<SingleEncodeEditorProps> = (props) => {
|
|
20
|
+
const { dkey, provided, snapshot } = props;
|
|
21
|
+
const { vizStore } = useGlobalStore();
|
|
22
|
+
const { draggableFieldState, visualConfig } = vizStore;
|
|
23
|
+
const channelItem = draggableFieldState[dkey.id][0];
|
|
24
|
+
const { t } = useTranslation();
|
|
25
|
+
|
|
26
|
+
const aggregationOptions = useMemo(() => {
|
|
27
|
+
return AGGREGATOR_LIST.map((op) => ({
|
|
28
|
+
value: op,
|
|
29
|
+
label: t(`constant.aggregator.${op}`),
|
|
30
|
+
}));
|
|
31
|
+
}, []);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div className="p-1 select-none relative" {...provided.droppableProps} ref={provided.innerRef}>
|
|
35
|
+
<div className={`p-1.5 bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 flex item-center justify-center grow text-gray-500 dark:text-gray-400 ${snapshot.draggingFromThisWith || snapshot.isDraggingOver || !channelItem ? 'opacity-100' : 'opacity-0'} relative z-0`}>
|
|
36
|
+
{t('actions.drop_field')}
|
|
37
|
+
</div>
|
|
38
|
+
{channelItem && (
|
|
39
|
+
<Draggable key={channelItem.dragId} draggableId={channelItem.dragId} index={0}>
|
|
40
|
+
{(provided, snapshot) => {
|
|
41
|
+
return (
|
|
42
|
+
<div ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps} className="flex items-stretch absolute z-10 top-0 left-0 right-0 bottom-0 m-1">
|
|
43
|
+
<div
|
|
44
|
+
onClick={() => {
|
|
45
|
+
vizStore.removeField(dkey.id, 0);
|
|
46
|
+
}}
|
|
47
|
+
className="grow-0 shrink-0 px-1.5 flex items-center justify-center bg-red-50 dark:bg-red-900 border border-red-200 dark:border-red-700 cursor-pointer"
|
|
48
|
+
>
|
|
49
|
+
<TrashIcon className="w-4" />
|
|
50
|
+
</div>
|
|
51
|
+
<div className="flex-1 flex items-center border border-gray-200 dark:border-gray-700 border-l-0 px-2 space-x-2">
|
|
52
|
+
<span className="flex-1 truncate">
|
|
53
|
+
{channelItem.name}
|
|
54
|
+
</span>
|
|
55
|
+
{channelItem.analyticType === "measure" && channelItem.fid !== COUNT_FIELD_ID && visualConfig.defaultAggregated && (
|
|
56
|
+
<DropdownContext
|
|
57
|
+
options={aggregationOptions}
|
|
58
|
+
onSelect={(value) => {
|
|
59
|
+
vizStore.setFieldAggregator(dkey.id, 0, value);
|
|
60
|
+
}}
|
|
61
|
+
>
|
|
62
|
+
<span className="bg-transparent text-gray-700 dark:text-gray-200 float-right focus:outline-none focus:border-gray-500 dark:focus:border-gray-400 flex items-center ml-2">
|
|
63
|
+
{channelItem.aggName || ""}
|
|
64
|
+
<ChevronUpDownIcon className="w-3" />
|
|
65
|
+
</span>
|
|
66
|
+
</DropdownContext>
|
|
67
|
+
)}
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
}}
|
|
72
|
+
</Draggable>
|
|
73
|
+
)}
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export default observer(SingleEncodeEditor);
|
|
@@ -109,13 +109,14 @@ const FilterEditDialog: React.FC = observer(() => {
|
|
|
109
109
|
|
|
110
110
|
return uncontrolledField ? (
|
|
111
111
|
<Modal
|
|
112
|
+
show={Boolean(uncontrolledField)}
|
|
112
113
|
title={t('editing')}
|
|
113
114
|
onClose={() => vizStore.closeFilterEditing()}
|
|
114
115
|
>
|
|
115
116
|
<header className="text-lg font-semibold py-2 outline-none">
|
|
116
117
|
{t('form.name')}
|
|
117
118
|
</header>
|
|
118
|
-
<input className="border py-1 px-4" readOnly value={uncontrolledField.name}/>
|
|
119
|
+
<input className="border py-1 px-4 bg-white text-gray-800" readOnly value={uncontrolledField.name}/>
|
|
119
120
|
<header className="text-lg font-semibold py-2 outline-none">
|
|
120
121
|
{t('form.rule')}
|
|
121
122
|
</header>
|
|
@@ -73,7 +73,7 @@ const FilterPill: React.FC<FilterPillProps> = observer(props => {
|
|
|
73
73
|
{field.name}
|
|
74
74
|
</header>
|
|
75
75
|
<div
|
|
76
|
-
className="bg-white text-gray-500 hover:bg-gray-100 flex flex-row output"
|
|
76
|
+
className="bg-white dark:bg-zinc-900 text-gray-500 hover:bg-gray-100 flex flex-row output"
|
|
77
77
|
onClick={() => vizStore.setFilterEditing(fIndex)}
|
|
78
78
|
style={{ cursor: 'pointer' }}
|
|
79
79
|
title={t('to_edit')}
|
|
@@ -197,7 +197,7 @@ const Slider: React.FC<SliderProps> = React.memo(function Slider ({
|
|
|
197
197
|
tabIndex={-1}
|
|
198
198
|
onMouseDown={ev => {
|
|
199
199
|
if (ev.buttons === 1) {
|
|
200
|
-
mouseOffsetRef.current = ev.nativeEvent.offsetX;
|
|
200
|
+
mouseOffsetRef.current = ev.nativeEvent.offsetX - (ev.target as HTMLDivElement).getBoundingClientRect().width;
|
|
201
201
|
setDragging('left');
|
|
202
202
|
}
|
|
203
203
|
}}
|
|
@@ -5,6 +5,7 @@ import styled from 'styled-components';
|
|
|
5
5
|
|
|
6
6
|
import type { IFilterField, IFilterRule } from '../../interfaces';
|
|
7
7
|
import { useGlobalStore } from '../../store';
|
|
8
|
+
import PureTabs from '../../components/tabs/defaultTab';
|
|
8
9
|
import Slider from './slider';
|
|
9
10
|
|
|
10
11
|
|
|
@@ -368,27 +369,16 @@ const Tabs: React.FC<TabsProps> = observer(({ field, onChange, tabs }) => {
|
|
|
368
369
|
|
|
369
370
|
return (
|
|
370
371
|
<TabsContainer>
|
|
371
|
-
<
|
|
372
|
-
{
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
onClick={() => {
|
|
382
|
-
if (which !== tab) {
|
|
383
|
-
setWhich(tab);
|
|
384
|
-
}
|
|
385
|
-
}}
|
|
386
|
-
>
|
|
387
|
-
{t(tab.replaceAll(/ /g, '_'))}
|
|
388
|
-
</TabHeader>
|
|
389
|
-
))
|
|
390
|
-
}
|
|
391
|
-
</TabList>
|
|
372
|
+
<PureTabs
|
|
373
|
+
selectedKey={which}
|
|
374
|
+
tabs={tabs.map(tab => ({
|
|
375
|
+
key: tab,
|
|
376
|
+
label: t(tab.replaceAll(/ /g, '_')),
|
|
377
|
+
}))}
|
|
378
|
+
onSelected={sk => {
|
|
379
|
+
setWhich(sk as typeof which);
|
|
380
|
+
}}
|
|
381
|
+
/>
|
|
392
382
|
<TabPanel>
|
|
393
383
|
{
|
|
394
384
|
tabs.map((tab, i) => {
|
|
@@ -1,48 +1,78 @@
|
|
|
1
|
-
import { BarsArrowDownIcon, BarsArrowUpIcon } from
|
|
2
|
-
import { observer } from
|
|
3
|
-
import React from
|
|
4
|
-
import { useTranslation } from
|
|
5
|
-
import { DraggableProvided } from
|
|
6
|
-
import { COUNT_FIELD_ID } from
|
|
7
|
-
import { IDraggableStateKey } from
|
|
8
|
-
import { useGlobalStore } from
|
|
9
|
-
import { Pill } from
|
|
10
|
-
import { AGGREGATOR_LIST } from
|
|
11
|
-
|
|
1
|
+
import { BarsArrowDownIcon, BarsArrowUpIcon, ChevronUpDownIcon } from "@heroicons/react/24/outline";
|
|
2
|
+
import { observer } from "mobx-react-lite";
|
|
3
|
+
import React, { useMemo } from "react";
|
|
4
|
+
import { useTranslation } from "react-i18next";
|
|
5
|
+
import { DraggableProvided } from "@kanaries/react-beautiful-dnd";
|
|
6
|
+
import { COUNT_FIELD_ID } from "../../constants";
|
|
7
|
+
import { IDraggableStateKey } from "../../interfaces";
|
|
8
|
+
import { useGlobalStore } from "../../store";
|
|
9
|
+
import { Pill } from "../components";
|
|
10
|
+
import { AGGREGATOR_LIST } from "../fieldsContext";
|
|
11
|
+
import DropdownContext from "../../components/dropdownContext";
|
|
12
12
|
|
|
13
13
|
interface PillProps {
|
|
14
14
|
provided: DraggableProvided;
|
|
15
15
|
fIndex: number;
|
|
16
16
|
dkey: IDraggableStateKey;
|
|
17
17
|
}
|
|
18
|
-
const OBPill: React.FC<PillProps> = props => {
|
|
18
|
+
const OBPill: React.FC<PillProps> = (props) => {
|
|
19
19
|
const { provided, dkey, fIndex } = props;
|
|
20
20
|
const { vizStore } = useGlobalStore();
|
|
21
21
|
const { visualConfig } = vizStore;
|
|
22
22
|
const field = vizStore.draggableFieldState[dkey.id][fIndex];
|
|
23
|
-
const { t } = useTranslation(
|
|
23
|
+
const { t } = useTranslation("translation", { keyPrefix: "constant.aggregator" });
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
25
|
+
const aggregationOptions = useMemo(() => {
|
|
26
|
+
return AGGREGATOR_LIST.map((op) => ({
|
|
27
|
+
value: op,
|
|
28
|
+
label: t(op),
|
|
29
|
+
}));
|
|
30
|
+
}, []);
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Pill
|
|
34
|
+
ref={provided.innerRef}
|
|
35
|
+
colType={field.analyticType === "dimension" ? "discrete" : "continuous"}
|
|
36
|
+
{...provided.draggableProps}
|
|
37
|
+
{...provided.dragHandleProps}
|
|
38
|
+
>
|
|
39
|
+
<span className="flex-1 truncate">{field.name}</span>
|
|
40
|
+
{field.analyticType === "measure" && field.fid !== COUNT_FIELD_ID && visualConfig.defaultAggregated && (
|
|
41
|
+
<DropdownContext
|
|
42
|
+
options={aggregationOptions}
|
|
43
|
+
onSelect={(value) => {
|
|
44
|
+
vizStore.setFieldAggregator(dkey.id, fIndex, value);
|
|
45
|
+
}}
|
|
46
|
+
>
|
|
47
|
+
<span className="bg-transparent text-gray-700 float-right focus:outline-none focus:border-gray-500 dark:focus:border-gray-400 flex items-center ml-2">
|
|
48
|
+
{field.aggName || ""}
|
|
49
|
+
<ChevronUpDownIcon className="w-3" />
|
|
50
|
+
</span>
|
|
51
|
+
</DropdownContext>
|
|
52
|
+
)}
|
|
53
|
+
{/* {field.analyticType === "measure" && field.fid !== COUNT_FIELD_ID && visualConfig.defaultAggregated && (
|
|
54
|
+
<select
|
|
55
|
+
className="bg-transparent text-gray-700 float-right focus:outline-none focus:border-gray-500"
|
|
56
|
+
value={field.aggName || ""}
|
|
57
|
+
onChange={(e) => {
|
|
58
|
+
vizStore.setFieldAggregator(dkey.id, fIndex, e.target.value);
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
{AGGREGATOR_LIST.map((op) => (
|
|
62
|
+
<option className="inline" value={op} key={op}>
|
|
63
|
+
{t(op)}
|
|
64
|
+
</option>
|
|
65
|
+
))}
|
|
66
|
+
</select>
|
|
67
|
+
)} */}
|
|
68
|
+
{field.analyticType === "dimension" && field.sort === "ascending" && (
|
|
69
|
+
<BarsArrowUpIcon className="float-right w-3" role="status" aria-label="Sorted in ascending order" />
|
|
70
|
+
)}
|
|
71
|
+
{field.analyticType === "dimension" && field.sort === "descending" && (
|
|
72
|
+
<BarsArrowDownIcon className="float-right w-3" role="status" aria-label="Sorted in descending order" />
|
|
73
|
+
)}
|
|
74
|
+
</Pill>
|
|
75
|
+
);
|
|
76
|
+
};
|
|
47
77
|
|
|
48
78
|
export default observer(OBPill);
|
package/src/index.css
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
|
+
html{
|
|
2
|
+
margin: 0px;
|
|
3
|
+
padding: 0px;
|
|
4
|
+
background-color: #fff;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
@media (prefers-color-scheme: dark) {
|
|
8
|
+
html {
|
|
9
|
+
background-color: rgb(24 24 27);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
1
12
|
body {
|
|
2
13
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
|
3
14
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
|
4
15
|
sans-serif;
|
|
5
16
|
-webkit-font-smoothing: antialiased;
|
|
6
17
|
-moz-osx-font-smoothing: grayscale;
|
|
18
|
+
margin: 0px;
|
|
19
|
+
padding: 0px;
|
|
7
20
|
}
|
|
8
21
|
|
|
9
22
|
code {
|
|
@@ -1,30 +1,31 @@
|
|
|
1
|
-
import { toJS } from
|
|
2
|
-
import { observer } from
|
|
3
|
-
import React, { useCallback } from
|
|
4
|
-
import Modal from
|
|
5
|
-
import { useGlobalStore } from
|
|
6
|
-
import InsightMainBoard from
|
|
1
|
+
import { toJS } from "mobx";
|
|
2
|
+
import { observer } from "mobx-react-lite";
|
|
3
|
+
import React, { useCallback } from "react";
|
|
4
|
+
import Modal from "../components/modal";
|
|
5
|
+
import { useGlobalStore } from "../store";
|
|
6
|
+
import InsightMainBoard from "./mainBoard";
|
|
7
7
|
|
|
8
|
-
const InsightBoard: React.FC = props => {
|
|
8
|
+
const InsightBoard: React.FC = (props) => {
|
|
9
9
|
const { commonStore, vizStore } = useGlobalStore();
|
|
10
10
|
const { showInsightBoard, currentDataset, filters } = commonStore;
|
|
11
11
|
const { viewDimensions, viewMeasures, draggableFieldState } = vizStore;
|
|
12
12
|
const onCloseModal = useCallback(() => {
|
|
13
13
|
commonStore.setShowInsightBoard(false);
|
|
14
|
-
}, [])
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
14
|
+
}, []);
|
|
15
|
+
if (!showInsightBoard) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
return (
|
|
19
|
+
<Modal onClose={onCloseModal} show={showInsightBoard}>
|
|
20
|
+
<InsightMainBoard
|
|
21
|
+
dataSource={currentDataset.dataSource}
|
|
22
|
+
fields={toJS(draggableFieldState.fields)}
|
|
23
|
+
viewDs={viewDimensions}
|
|
24
|
+
viewMs={viewMeasures}
|
|
25
|
+
filters={toJS(filters)}
|
|
26
|
+
/>
|
|
27
|
+
</Modal>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
29
30
|
|
|
30
|
-
export default observer(InsightBoard);
|
|
31
|
+
export default observer(InsightBoard);
|
|
@@ -9,9 +9,16 @@ const RGBContainer = styled.div`
|
|
|
9
9
|
border: 1px solid #f0f0f0;
|
|
10
10
|
background-color: #f0f0f0;
|
|
11
11
|
cursor: pointer;
|
|
12
|
+
@media (prefers-color-scheme: dark) {
|
|
13
|
+
background-color: #000;
|
|
14
|
+
border: 1px solid #4b5563;
|
|
15
|
+
}
|
|
12
16
|
}
|
|
13
17
|
.choosen.option {
|
|
14
18
|
background-color: #fff;
|
|
19
|
+
@media (prefers-color-scheme: dark) {
|
|
20
|
+
background-color: #000;
|
|
21
|
+
}
|
|
15
22
|
}
|
|
16
23
|
`;
|
|
17
24
|
|
package/src/interfaces.ts
CHANGED
|
@@ -119,6 +119,7 @@ export interface DraggableFieldState {
|
|
|
119
119
|
shape: IViewField[];
|
|
120
120
|
theta: IViewField[];
|
|
121
121
|
radius: IViewField[];
|
|
122
|
+
details: IViewField[];
|
|
122
123
|
filters: IFilterField[];
|
|
123
124
|
}
|
|
124
125
|
|
|
@@ -178,4 +179,9 @@ export interface IVisSpec {
|
|
|
178
179
|
readonly name?: [string, Record<string, any>?];
|
|
179
180
|
readonly encodings: DeepReadonly<DraggableFieldState>;
|
|
180
181
|
readonly config: DeepReadonly<IVisualConfig>;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export enum ISegmentKey {
|
|
185
|
+
vis = 'vis',
|
|
186
|
+
data = 'data'
|
|
181
187
|
}
|
package/src/lib/inferMeta.ts
CHANGED
|
@@ -53,7 +53,7 @@ function inferAnalyticTypeFromSemanticType(semanticType: ISemanticType): IAnalyt
|
|
|
53
53
|
export function inferSemanticType(data: IRow[], fid: string): ISemanticType {
|
|
54
54
|
let st = UnivariateSummary.getFieldType(data, fid);
|
|
55
55
|
if (st === 'nominal') {
|
|
56
|
-
if (isDateTimeArray(data.map((row) => row[fid]))) st = 'temporal';
|
|
56
|
+
if (isDateTimeArray(data.map((row) => `${row[fid]}`))) st = 'temporal';
|
|
57
57
|
} else if (st === 'ordinal') {
|
|
58
58
|
const valueSet: Set<number> = new Set();
|
|
59
59
|
let _max = -Infinity;
|
package/src/locales/en-US.json
CHANGED
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
},
|
|
51
51
|
"draggable_key": {
|
|
52
52
|
"fields": "Fields",
|
|
53
|
-
"columns": "
|
|
54
|
-
"rows": "
|
|
53
|
+
"columns": "X-Axis",
|
|
54
|
+
"rows": "Y-Axis",
|
|
55
55
|
"color": "Color",
|
|
56
56
|
"opacity": "Opacity",
|
|
57
57
|
"size": "Size",
|
|
@@ -81,6 +81,10 @@
|
|
|
81
81
|
"App": {
|
|
82
82
|
"labels": {
|
|
83
83
|
"data_interpretation": "Interpret Data"
|
|
84
|
+
},
|
|
85
|
+
"segments": {
|
|
86
|
+
"vis": "Visualization",
|
|
87
|
+
"data": "Data"
|
|
84
88
|
}
|
|
85
89
|
},
|
|
86
90
|
"DataSource": {
|
|
@@ -101,7 +105,9 @@
|
|
|
101
105
|
"file": {
|
|
102
106
|
"open": "Open...",
|
|
103
107
|
"submit": "Submit",
|
|
104
|
-
"dataset_name": "Dataset Name"
|
|
108
|
+
"dataset_name": "Dataset Name",
|
|
109
|
+
"choose_file": "Choose a Data File",
|
|
110
|
+
"get_start_desc": "Get started by creating a dataset."
|
|
105
111
|
},
|
|
106
112
|
"public": {
|
|
107
113
|
"submit": "Submit"
|
|
@@ -177,5 +183,10 @@
|
|
|
177
183
|
"children_major_factor": "major factor",
|
|
178
184
|
"children_outlier": "outlier"
|
|
179
185
|
}
|
|
186
|
+
},
|
|
187
|
+
"actions": {
|
|
188
|
+
"prev": "Previous",
|
|
189
|
+
"next": "Next",
|
|
190
|
+
"drop_field": "Drop Field Here"
|
|
180
191
|
}
|
|
181
192
|
}
|
package/src/locales/zh-CN.json
CHANGED
|
@@ -81,6 +81,10 @@
|
|
|
81
81
|
"App": {
|
|
82
82
|
"labels": {
|
|
83
83
|
"data_interpretation": "数据解读"
|
|
84
|
+
},
|
|
85
|
+
"segments": {
|
|
86
|
+
"vis": "可视化",
|
|
87
|
+
"data": "数据"
|
|
84
88
|
}
|
|
85
89
|
},
|
|
86
90
|
"DataSource": {
|
|
@@ -101,7 +105,9 @@
|
|
|
101
105
|
"file": {
|
|
102
106
|
"open": "打开文件...",
|
|
103
107
|
"submit": "确认",
|
|
104
|
-
"dataset_name": "数据集名称"
|
|
108
|
+
"dataset_name": "数据集名称",
|
|
109
|
+
"choose_file": "选择文件",
|
|
110
|
+
"get_start_desc": "创建一个数据集以开始分析"
|
|
105
111
|
},
|
|
106
112
|
"public": {
|
|
107
113
|
"submit": "确认"
|
|
@@ -177,5 +183,10 @@
|
|
|
177
183
|
"children_major_factor": "子节点主要因素",
|
|
178
184
|
"children_outlier": "子节点异常"
|
|
179
185
|
}
|
|
186
|
+
},
|
|
187
|
+
"actions": {
|
|
188
|
+
"prev": "向前",
|
|
189
|
+
"next": "向后",
|
|
190
|
+
"drop_field": "拖拽字段至此"
|
|
180
191
|
}
|
|
181
192
|
}
|
package/src/main.tsx
CHANGED
package/src/renderer/index.tsx
CHANGED
|
@@ -7,7 +7,7 @@ import { useGlobalStore } from '../store';
|
|
|
7
7
|
import ReactVega, { IReactVegaHandler } from '../vis/react-vega';
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
const ReactiveRenderer = forwardRef<IReactVegaHandler, {}>(function ReactiveRenderer (
|
|
10
|
+
const ReactiveRenderer = forwardRef<IReactVegaHandler, { themeKey?: 'vega' | 'g2' }>(function ReactiveRenderer ({ themeKey }, ref) {
|
|
11
11
|
const { vizStore, commonStore } = useGlobalStore();
|
|
12
12
|
const { draggableFieldState, visualConfig } = vizStore;
|
|
13
13
|
const { geoms, interactiveScale, defaultAggregated, stack, showActions, size, exploration } = visualConfig;
|
|
@@ -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);
|
|
@@ -104,6 +105,7 @@ const ReactiveRenderer = forwardRef<IReactVegaHandler, {}>(function ReactiveRend
|
|
|
104
105
|
brushEncoding={exploration.mode === 'brush' ? exploration.brushDirection : 'none'}
|
|
105
106
|
selectEncoding={exploration.mode === 'point' ? 'default' : 'none'}
|
|
106
107
|
onGeomClick={handleGeomClick}
|
|
108
|
+
themeKey={themeKey}
|
|
107
109
|
/>
|
|
108
110
|
</Resizable>
|
|
109
111
|
});
|
|
@@ -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);
|
package/src/segments/visNav.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useCallback } from "react";
|
|
2
2
|
import { observer } from "mobx-react-lite";
|
|
3
|
-
import
|
|
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
|
-
<
|
|
39
|
+
<EditableTabs
|
|
40
40
|
selectedKey={visList[visIndex].visId}
|
|
41
41
|
tabs={tabs}
|
|
42
42
|
onEditLabel={editLabelHandler}
|
package/src/store/commonStore.ts
CHANGED
|
@@ -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,8 +13,9 @@ 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
|
-
|
|
16
|
+
public showDataConfig: boolean = false;
|
|
17
17
|
public filters: Filters = {};
|
|
18
|
+
public segmentKey: ISegmentKey = ISegmentKey.vis;
|
|
18
19
|
constructor () {
|
|
19
20
|
this.datasets = [];
|
|
20
21
|
this.dataSources = [];
|
|
@@ -44,9 +45,15 @@ export class CommonStore {
|
|
|
44
45
|
dataSource: []
|
|
45
46
|
}
|
|
46
47
|
}
|
|
48
|
+
public setSegmentKey (sk: ISegmentKey) {
|
|
49
|
+
this.segmentKey = sk;
|
|
50
|
+
}
|
|
47
51
|
public setShowDSPanel (show: boolean) {
|
|
48
52
|
this.showDSPanel = show;
|
|
49
53
|
}
|
|
54
|
+
public setShowDataConfig (show: boolean) {
|
|
55
|
+
this.showDataConfig = show;
|
|
56
|
+
}
|
|
50
57
|
public setShowInsightBoard (show: boolean) {
|
|
51
58
|
this.showInsightBoard = show;
|
|
52
59
|
}
|
|
@@ -66,6 +73,25 @@ export class CommonStore {
|
|
|
66
73
|
this.tmpDSRawFields = fields;
|
|
67
74
|
}
|
|
68
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
|
+
|
|
69
95
|
public updateTempFieldAnalyticType (fieldKey: string, analyticType: IMutField['analyticType']) {
|
|
70
96
|
const field = this.tmpDSRawFields.find(f => f.fid === fieldKey);
|
|
71
97
|
if (field) {
|