@kanaries/graphic-walker 0.2.13 → 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/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/toolbar/toolbar-item.d.ts +1 -0
- package/dist/dataSource/dataSelection/config.d.ts +2 -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 +20874 -19172
- package/dist/graphic-walker.es.js.map +1 -1
- package/dist/graphic-walker.umd.js +245 -170
- package/dist/graphic-walker.umd.js.map +1 -1
- package/dist/insightBoard/index.d.ts +1 -1
- package/dist/interfaces.d.ts +1 -0
- package/dist/renderer/index.d.ts +3 -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 +8 -5
- 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 +42 -52
- package/src/components/dataTable/pagination.tsx +4 -4
- 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 +4 -4
- package/src/components/tabs/editableTab.tsx +5 -5
- 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/index.tsx +81 -61
- 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 +1 -0
- package/src/lib/inferMeta.ts +1 -1
- package/src/locales/en-US.json +7 -4
- package/src/locales/zh-CN.json +5 -2
- package/src/main.tsx +1 -1
- package/src/renderer/index.tsx +2 -1
- 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
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",
|
|
@@ -105,7 +105,9 @@
|
|
|
105
105
|
"file": {
|
|
106
106
|
"open": "Open...",
|
|
107
107
|
"submit": "Submit",
|
|
108
|
-
"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."
|
|
109
111
|
},
|
|
110
112
|
"public": {
|
|
111
113
|
"submit": "Submit"
|
|
@@ -184,6 +186,7 @@
|
|
|
184
186
|
},
|
|
185
187
|
"actions": {
|
|
186
188
|
"prev": "Previous",
|
|
187
|
-
"next": "Next"
|
|
189
|
+
"next": "Next",
|
|
190
|
+
"drop_field": "Drop Field Here"
|
|
188
191
|
}
|
|
189
192
|
}
|
package/src/locales/zh-CN.json
CHANGED
|
@@ -105,7 +105,9 @@
|
|
|
105
105
|
"file": {
|
|
106
106
|
"open": "打开文件...",
|
|
107
107
|
"submit": "确认",
|
|
108
|
-
"dataset_name": "数据集名称"
|
|
108
|
+
"dataset_name": "数据集名称",
|
|
109
|
+
"choose_file": "选择文件",
|
|
110
|
+
"get_start_desc": "创建一个数据集以开始分析"
|
|
109
111
|
},
|
|
110
112
|
"public": {
|
|
111
113
|
"submit": "确认"
|
|
@@ -184,6 +186,7 @@
|
|
|
184
186
|
},
|
|
185
187
|
"actions": {
|
|
186
188
|
"prev": "向前",
|
|
187
|
-
"next": "向后"
|
|
189
|
+
"next": "向后",
|
|
190
|
+
"drop_field": "拖拽字段至此"
|
|
188
191
|
}
|
|
189
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;
|
|
@@ -105,6 +105,7 @@ const ReactiveRenderer = forwardRef<IReactVegaHandler, {}>(function ReactiveRend
|
|
|
105
105
|
brushEncoding={exploration.mode === 'brush' ? exploration.brushDirection : 'none'}
|
|
106
106
|
selectEncoding={exploration.mode === 'point' ? 'default' : 'none'}
|
|
107
107
|
onGeomClick={handleGeomClick}
|
|
108
|
+
themeKey={themeKey}
|
|
108
109
|
/>
|
|
109
110
|
</Resizable>
|
|
110
111
|
});
|
|
@@ -53,6 +53,7 @@ function initEncoding(): DraggableFieldState {
|
|
|
53
53
|
shape: [],
|
|
54
54
|
radius: [],
|
|
55
55
|
theta: [],
|
|
56
|
+
details: [],
|
|
56
57
|
filters: [],
|
|
57
58
|
};
|
|
58
59
|
}
|
|
@@ -444,6 +445,21 @@ export class VizSpecStore {
|
|
|
444
445
|
fields.splice(sourceIndex, 1);
|
|
445
446
|
});
|
|
446
447
|
}
|
|
448
|
+
public replaceField(sourceKey: keyof DraggableFieldState, sourceIndex: number, fid: string) {
|
|
449
|
+
if (MetaFieldKeys.includes(sourceKey)) return;
|
|
450
|
+
const enteringField = [
|
|
451
|
+
...this.draggableFieldState.dimensions,
|
|
452
|
+
...this.draggableFieldState.measures
|
|
453
|
+
].find(which => which.fid === fid);
|
|
454
|
+
if (!enteringField) {
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
this.useMutable(({ encodings }) => {
|
|
459
|
+
const fields = encodings[sourceKey];
|
|
460
|
+
fields.splice(sourceIndex, 1, toJS(enteringField));
|
|
461
|
+
});
|
|
462
|
+
}
|
|
447
463
|
private appendFilter(index: number, data: IViewField) {
|
|
448
464
|
this.useMutable(({ encodings }) => {
|
|
449
465
|
encodings.filters.splice(index, 0, {
|
package/src/utils/index.ts
CHANGED
|
@@ -258,3 +258,22 @@ export function extendCountField(
|
|
|
258
258
|
fields: nextFields,
|
|
259
259
|
};
|
|
260
260
|
}
|
|
261
|
+
|
|
262
|
+
export function getRange (nums: number[]): [number, number] {
|
|
263
|
+
let _min = Infinity;
|
|
264
|
+
let _max = -Infinity;
|
|
265
|
+
for (let i = 0; i < nums.length; i++) {
|
|
266
|
+
_min = Math.min(_min, nums[i]);
|
|
267
|
+
_max = Math.max(_max, nums[i]);
|
|
268
|
+
}
|
|
269
|
+
return [_min, _max];
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export function makeNumbersBeautiful (nums: number[]): number[] {
|
|
273
|
+
const [min, max] = getRange(nums);
|
|
274
|
+
const range = max - min;
|
|
275
|
+
const step = Math.pow(10, Math.floor(Math.log10(range)));
|
|
276
|
+
return nums.map((num) => {
|
|
277
|
+
return Math.round(num / step) * step;
|
|
278
|
+
})
|
|
279
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
|
|
3
|
+
export function currentMediaTheme(): "dark" | "light" {
|
|
4
|
+
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
|
5
|
+
return "dark";
|
|
6
|
+
} else {
|
|
7
|
+
return "light";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function useCurrentMediaTheme(): "dark" | "light" {
|
|
12
|
+
const [theme, setTheme] = useState<"dark" | "light">(currentMediaTheme());
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const mediaQuery = window.matchMedia?.("(prefers-color-scheme: dark)") as MediaQueryList | undefined;
|
|
16
|
+
const listener = (e: MediaQueryListEvent) => {
|
|
17
|
+
setTheme(e.matches ? "dark" : "light");
|
|
18
|
+
};
|
|
19
|
+
mediaQuery?.addEventListener("change", listener);
|
|
20
|
+
return () => {
|
|
21
|
+
mediaQuery?.removeEventListener("change", listener);
|
|
22
|
+
};
|
|
23
|
+
}, []);
|
|
24
|
+
|
|
25
|
+
return theme;
|
|
26
|
+
}
|
|
@@ -140,12 +140,13 @@ export function makeBinField(dataSource: IRow[], fid: string, binFid: string, bi
|
|
|
140
140
|
if (val < _min) _min = val;
|
|
141
141
|
}
|
|
142
142
|
const step = (_max - _min) / binSize;
|
|
143
|
+
const beaStep = Math.max(-Math.round(Math.log10(_max - _min)) + 2, 0)
|
|
143
144
|
return dataSource.map((r) => {
|
|
144
145
|
let bIndex = Math.floor((r[fid] - _min) / step);
|
|
145
146
|
if (bIndex === binSize) bIndex = binSize - 1;
|
|
146
147
|
return {
|
|
147
148
|
...r,
|
|
148
|
-
[binFid]: bIndex * step + _min,
|
|
149
|
+
[binFid]: Number(((bIndex * step + _min)).toFixed(beaStep)),
|
|
149
150
|
};
|
|
150
151
|
});
|
|
151
152
|
}
|