@kanaries/graphic-walker 0.2.13 → 0.2.15
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 +1 -0
- package/dist/components/button/defaultMini.d.ts +4 -0
- package/dist/components/button/primaryMini.d.ts +4 -0
- package/dist/components/callout.d.ts +2 -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/components.d.ts +4 -1
- package/dist/components/toolbar/index.d.ts +2 -0
- package/dist/components/toolbar/toolbar-item.d.ts +4 -0
- package/dist/components/tooltip.d.ts +2 -0
- package/dist/dataSource/dataSelection/config.d.ts +2 -0
- package/dist/dataSource/index.d.ts +1 -1
- package/dist/fields/components.d.ts +0 -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/filterField/filterEditDialog.d.ts +1 -1
- package/dist/fields/obComponents/obPill.d.ts +3 -3
- package/dist/graphic-walker.es.js +21205 -19397
- package/dist/graphic-walker.es.js.map +1 -1
- package/dist/graphic-walker.umd.js +181 -236
- package/dist/graphic-walker.umd.js.map +1 -1
- package/dist/insightBoard/index.d.ts +1 -1
- package/dist/interfaces.d.ts +3 -0
- package/dist/renderer/index.d.ts +7 -3
- package/dist/store/visualSpecStore.d.ts +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/media.d.ts +3 -0
- package/dist/vis/react-vega.d.ts +5 -1
- package/dist/vis/theme.d.ts +146 -0
- package/dist/visualSettings/index.d.ts +2 -1
- package/package.json +2 -1
- package/src/App.tsx +24 -16
- 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 +9 -4
- package/src/components/clickMenu.tsx +1 -5
- 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 +20 -22
- package/src/components/sizeSetting.tsx +2 -2
- package/src/components/tabs/defaultTab.tsx +4 -4
- package/src/components/tabs/editableTab.tsx +5 -5
- package/src/components/toolbar/components.tsx +10 -8
- package/src/components/toolbar/index.tsx +16 -4
- package/src/components/toolbar/toolbar-button.tsx +8 -2
- package/src/components/toolbar/toolbar-item.tsx +18 -9
- package/src/components/toolbar/toolbar-select-button.tsx +21 -8
- package/src/components/toolbar/toolbar-toggle-button.tsx +8 -2
- package/src/components/tooltip.tsx +10 -3
- 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 +80 -61
- package/src/fields/aestheticFields.tsx +3 -1
- package/src/fields/components.tsx +20 -38
- package/src/fields/datasetFields/dimFields.tsx +43 -35
- package/src/fields/datasetFields/index.tsx +3 -4
- 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 +63 -98
- package/src/fields/filterField/filterPill.tsx +1 -1
- package/src/fields/filterField/slider.tsx +2 -2
- 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/mainBoard.tsx +9 -2
- package/src/insightBoard/radioGroupButtons.tsx +7 -0
- package/src/interfaces.ts +5 -1
- package/src/lib/inferMeta.ts +1 -1
- package/src/locales/en-US.json +11 -5
- package/src/locales/i18n.ts +7 -0
- package/src/locales/ja-JP.json +195 -0
- package/src/locales/zh-CN.json +9 -3
- package/src/main.tsx +1 -1
- package/src/renderer/index.tsx +96 -70
- package/src/store/visualSpecStore.ts +16 -0
- package/src/utils/index.ts +19 -0
- package/src/utils/media.ts +31 -0
- package/src/utils/normalization.ts +2 -1
- package/src/vis/react-vega.tsx +36 -5
- package/src/vis/theme.ts +124 -0
- package/src/visualSettings/index.tsx +29 -33
- package/dist/components/container.d.ts +0 -2
- package/src/components/container.tsx +0 -16
package/src/dataSource/index.tsx
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import React, { useRef } from
|
|
2
|
-
import { observer } from
|
|
3
|
-
import { CheckCircleIcon, ArrowPathIcon } from
|
|
4
|
-
import { useTranslation } from
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
1
|
+
import React, { useRef } from "react";
|
|
2
|
+
import { observer } from "mobx-react-lite";
|
|
3
|
+
import { CheckCircleIcon, ArrowPathIcon } from "@heroicons/react/24/outline";
|
|
4
|
+
import { useTranslation } from "react-i18next";
|
|
5
|
+
import Modal from "../components/modal";
|
|
6
|
+
import { useGlobalStore } from "../store";
|
|
7
|
+
import { download } from "../utils/save";
|
|
8
|
+
import GwFile from "./dataSelection/gwFile";
|
|
9
|
+
import DataSelection from "./dataSelection";
|
|
10
|
+
import DefaultButton from "../components/button/default";
|
|
11
|
+
import DropdownSelect from "../components/dropdownSelect";
|
|
12
|
+
import PrimaryButton from "../components/button/primary";
|
|
12
13
|
|
|
13
14
|
interface DSSegmentProps {
|
|
14
15
|
preWorkDone: boolean;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
const DataSourceSegment: React.FC<DSSegmentProps> = props => {
|
|
18
|
+
const DataSourceSegment: React.FC<DSSegmentProps> = (props) => {
|
|
18
19
|
const { preWorkDone } = props;
|
|
19
20
|
const { commonStore, vizStore } = useGlobalStore();
|
|
20
21
|
const gwFileRef = useRef<HTMLInputElement>(null);
|
|
@@ -22,55 +23,73 @@ const DataSourceSegment: React.FC<DSSegmentProps> = props => {
|
|
|
22
23
|
|
|
23
24
|
const { currentDataset, datasets, showDSPanel } = commonStore;
|
|
24
25
|
|
|
25
|
-
return
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
{
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
26
|
+
return (
|
|
27
|
+
<div className="flex items-center m-4 p-4 border border-gray-200 dark:border-gray-700">
|
|
28
|
+
<GwFile fileRef={gwFileRef} />
|
|
29
|
+
{!preWorkDone && (
|
|
30
|
+
<div className="animate-spin inline-block mr-2 ml-2 w-4 h-4 rounded-full border-t-2 border-l-2 border-blue-500"></div>
|
|
31
|
+
)}
|
|
32
|
+
{/* <label className="text-xs mr-1 whitespace-nowrap self-center h-4">
|
|
33
|
+
{t("DataSource.labels.cur_dataset")}
|
|
34
|
+
</label> */}
|
|
35
|
+
<div className="mr-2">
|
|
36
|
+
<DropdownSelect
|
|
37
|
+
options={datasets.map((d) => ({ label: d.name, value: d.id }))}
|
|
38
|
+
selectedKey={currentDataset.id}
|
|
39
|
+
onSelect={(dsKey) => {
|
|
40
|
+
commonStore.useDS(dsKey);
|
|
41
|
+
}}
|
|
42
|
+
placeholder={t("DataSource.labels.cur_dataset")}
|
|
43
|
+
/>
|
|
44
|
+
</div>
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
46
|
+
<PrimaryButton
|
|
47
|
+
className="mr-2"
|
|
48
|
+
text={t("DataSource.buttons.create_dataset")}
|
|
49
|
+
onClick={() => {
|
|
50
|
+
commonStore.startDSBuildingTask();
|
|
51
|
+
}}
|
|
52
|
+
/>
|
|
53
|
+
<DefaultButton
|
|
54
|
+
className="mr-2"
|
|
55
|
+
text={t("DataSource.buttons.export_as_file")}
|
|
56
|
+
onClick={() => {
|
|
57
|
+
const res = vizStore.exportAsRaw();
|
|
58
|
+
download(res, "graphic-walker-notebook.json", "text/plain");
|
|
59
|
+
}}
|
|
60
|
+
/>
|
|
61
|
+
<DefaultButton
|
|
62
|
+
className="mr-2"
|
|
63
|
+
text={t("DataSource.buttons.import_file")}
|
|
64
|
+
onClick={() => {
|
|
65
|
+
if (gwFileRef.current) {
|
|
66
|
+
gwFileRef.current.click();
|
|
67
|
+
}
|
|
68
|
+
}}
|
|
69
|
+
/>
|
|
63
70
|
<Modal
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
71
|
+
title={t("DataSource.dialog.create_data_source")}
|
|
72
|
+
onClose={() => {
|
|
73
|
+
commonStore.setShowDSPanel(false);
|
|
74
|
+
}}
|
|
75
|
+
show={showDSPanel}
|
|
76
|
+
>
|
|
77
|
+
<DataSelection />
|
|
78
|
+
</Modal>
|
|
79
|
+
{/* {showDSPanel && (
|
|
80
|
+
<Modal
|
|
81
|
+
title={t("DataSource.dialog.create_data_source")}
|
|
82
|
+
onClose={() => {
|
|
83
|
+
commonStore.setShowDSPanel(false);
|
|
84
|
+
}}
|
|
85
|
+
>
|
|
86
|
+
<DataSelection />
|
|
87
|
+
</Modal>
|
|
88
|
+
)} */}
|
|
89
|
+
{preWorkDone && <CheckCircleIcon className="text-green-500 w-5 inline-block ml-2" />}
|
|
90
|
+
{!preWorkDone && <ArrowPathIcon className="text-yellow-500 w-5 inline-block ml-2" />}
|
|
91
|
+
</div>
|
|
92
|
+
);
|
|
93
|
+
};
|
|
75
94
|
|
|
76
|
-
export default observer(DataSourceSegment);
|
|
95
|
+
export default observer(DataSourceSegment);
|
|
@@ -3,6 +3,7 @@ import { Droppable } from "@kanaries/react-beautiful-dnd";
|
|
|
3
3
|
import { DRAGGABLE_STATE_KEYS } from './fieldsContext';
|
|
4
4
|
import { AestheticFieldContainer } from './components'
|
|
5
5
|
import OBFieldContainer from './obComponents/obFContainer';
|
|
6
|
+
import SingleEncodeEditor from './encodeFields/singleEncodeEditor';
|
|
6
7
|
|
|
7
8
|
const aestheticFields = DRAGGABLE_STATE_KEYS.filter(f => ['color', 'opacity', 'size', 'shape'].includes(f.id));
|
|
8
9
|
|
|
@@ -12,7 +13,8 @@ const AestheticFields: React.FC = props => {
|
|
|
12
13
|
aestheticFields.map(dkey => <AestheticFieldContainer name={dkey.id} key={dkey.id}>
|
|
13
14
|
<Droppable droppableId={dkey.id} direction="horizontal">
|
|
14
15
|
{(provided, snapshot) => (
|
|
15
|
-
<OBFieldContainer dkey={dkey} provided={provided} />
|
|
16
|
+
// <OBFieldContainer dkey={dkey} provided={provided} />
|
|
17
|
+
<SingleEncodeEditor dkey={dkey} provided={provided} snapshot={snapshot} />
|
|
16
18
|
)}
|
|
17
19
|
</Droppable>
|
|
18
20
|
</AestheticFieldContainer>)
|
|
@@ -3,31 +3,13 @@ import styled from "styled-components";
|
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import { COLORS } from "../config";
|
|
5
5
|
|
|
6
|
-
export const AestheticSegment = styled.div`
|
|
7
|
-
border: 1px solid #e5e7eb;
|
|
8
|
-
font-size: 12px;
|
|
9
|
-
margin: 0.2em;
|
|
10
|
-
|
|
11
|
-
.aes-header{
|
|
12
|
-
border-bottom: 1px solid #e5e7eb;
|
|
13
|
-
padding: 0.6em;
|
|
14
|
-
h4 {
|
|
15
|
-
font-weight: 400;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
.aes-container{
|
|
19
|
-
overflow-x: auto;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
`
|
|
23
|
-
|
|
24
6
|
export const FieldListContainer: React.FC<{ name: string }> = (props) => {
|
|
25
7
|
const { t } = useTranslation('translation', { keyPrefix: 'constant.draggable_key' });
|
|
26
8
|
|
|
27
9
|
return (
|
|
28
|
-
<FieldListSegment>
|
|
29
|
-
<div className="fl-header">
|
|
30
|
-
<h4>{t(props.name)}</h4>
|
|
10
|
+
<FieldListSegment className="m-0.5 border border-gray-200 dark:border-gray-700">
|
|
11
|
+
<div className="fl-header border-r border-gray-200 dark:border-gray-800 cursor-default select-none">
|
|
12
|
+
<h4 className="font-normal">{t(props.name)}</h4>
|
|
31
13
|
</div>
|
|
32
14
|
<div className="fl-container">{props.children}</div>
|
|
33
15
|
</FieldListSegment>
|
|
@@ -38,12 +20,12 @@ export const AestheticFieldContainer: React.FC<{ name: string }> = props => {
|
|
|
38
20
|
const { t } = useTranslation('translation', { keyPrefix: 'constant.draggable_key' });
|
|
39
21
|
|
|
40
22
|
return (
|
|
41
|
-
<
|
|
42
|
-
<div className="
|
|
43
|
-
<h4>{t(props.name)}</h4>
|
|
23
|
+
<div className="m-0.5 text-xs border border-gray-200 dark:border-gray-700">
|
|
24
|
+
<div className="border-b border-gray-200 dark:border-gray-800 p-2 cursor-default select-none">
|
|
25
|
+
<h4 className="font-normal">{t(props.name)}</h4>
|
|
44
26
|
</div>
|
|
45
|
-
<div
|
|
46
|
-
</
|
|
27
|
+
<div>{props.children}</div>
|
|
28
|
+
</div>
|
|
47
29
|
);
|
|
48
30
|
}
|
|
49
31
|
|
|
@@ -51,12 +33,12 @@ export const FilterFieldContainer: React.FC = props => {
|
|
|
51
33
|
const { t } = useTranslation('translation', { keyPrefix: 'constant.draggable_key' });
|
|
52
34
|
|
|
53
35
|
return (
|
|
54
|
-
<
|
|
55
|
-
<div className="
|
|
56
|
-
<h4>{t('filters')}</h4>
|
|
36
|
+
<div className="m-0.5 text-xs border border-gray-200 dark:border-gray-700">
|
|
37
|
+
<div className="border-b border-gray-200 dark:border-gray-800 p-2 cursor-default select-none">
|
|
38
|
+
<h4 className="font-normal">{t('filters')}</h4>
|
|
57
39
|
</div>
|
|
58
|
-
<div
|
|
59
|
-
</
|
|
40
|
+
<div>{props.children}</div>
|
|
41
|
+
</div>
|
|
60
42
|
);
|
|
61
43
|
}
|
|
62
44
|
|
|
@@ -84,13 +66,11 @@ export const FilterFieldsContainer = styled.div({
|
|
|
84
66
|
|
|
85
67
|
export const FieldListSegment = styled.div`
|
|
86
68
|
display: flex;
|
|
87
|
-
border: 1px solid #e5e7eb;
|
|
88
69
|
margin: 0.2em;
|
|
89
70
|
font-size: 12px;
|
|
90
71
|
div.fl-header {
|
|
91
72
|
/* flex-basis: 100px; */
|
|
92
73
|
width: 100px;
|
|
93
|
-
border-right: 1px solid #e5e7eb;
|
|
94
74
|
flex-shrink: 0;
|
|
95
75
|
h4 {
|
|
96
76
|
margin: 0.6em;
|
|
@@ -99,20 +79,22 @@ export const FieldListSegment = styled.div`
|
|
|
99
79
|
}
|
|
100
80
|
div.fl-container {
|
|
101
81
|
flex-grow: 10;
|
|
102
|
-
/* display: flex;
|
|
103
|
-
flex-wrap: wrap; */
|
|
104
|
-
/* overflow-x: auto;
|
|
105
|
-
overflow-y: hidden; */
|
|
106
82
|
}
|
|
107
83
|
`;
|
|
108
84
|
|
|
109
85
|
export const FilterFieldSegment = styled.div`
|
|
110
86
|
border: 1px solid #e5e7eb;
|
|
87
|
+
@media (prefers-color-scheme: dark) {
|
|
88
|
+
border: 1px solid #2d3748;
|
|
89
|
+
}
|
|
111
90
|
font-size: 12px;
|
|
112
91
|
margin: 0.2em;
|
|
113
92
|
|
|
114
93
|
.flt-header {
|
|
115
94
|
border-bottom: 1px solid #e5e7eb;
|
|
95
|
+
@media (prefers-color-scheme: dark) {
|
|
96
|
+
border-bottom: 1px solid #2d3748;
|
|
97
|
+
}
|
|
116
98
|
padding: 0.6em;
|
|
117
99
|
|
|
118
100
|
> h4 {
|
|
@@ -144,7 +126,7 @@ export const Pill = styled.div<{colType: 'discrete' | 'continuous'}>`
|
|
|
144
126
|
font-size: 12px;
|
|
145
127
|
height: 20px;
|
|
146
128
|
min-width: 150px;
|
|
147
|
-
overflow-y: hidden;
|
|
129
|
+
/* overflow-y: hidden; */
|
|
148
130
|
padding: 0 10px;
|
|
149
131
|
user-select: none;
|
|
150
132
|
text-overflow: ellipsis;
|
|
@@ -1,46 +1,54 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import { Draggable, DroppableProvided } from
|
|
3
|
-
import { observer } from
|
|
4
|
-
import { useGlobalStore } from
|
|
5
|
-
import DataTypeIcon from
|
|
6
|
-
import { FieldPill } from
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Draggable, DroppableProvided } from "@kanaries/react-beautiful-dnd";
|
|
3
|
+
import { observer } from "mobx-react-lite";
|
|
4
|
+
import { useGlobalStore } from "../../store";
|
|
5
|
+
import DataTypeIcon from "../../components/dataTypeIcon";
|
|
6
|
+
import { FieldPill } from "./fieldPill";
|
|
7
7
|
|
|
8
8
|
interface Props {
|
|
9
9
|
provided: DroppableProvided;
|
|
10
10
|
}
|
|
11
|
-
const DimFields: React.FC<Props> = props => {
|
|
11
|
+
const DimFields: React.FC<Props> = (props) => {
|
|
12
12
|
const { provided } = props;
|
|
13
13
|
const { vizStore } = useGlobalStore();
|
|
14
14
|
const dimensions = vizStore.draggableFieldState.dimensions;
|
|
15
|
-
return
|
|
16
|
-
{
|
|
17
|
-
|
|
18
|
-
{
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
</FieldPill>
|
|
31
|
-
{
|
|
32
|
-
<FieldPill className={`pt-0.5 pb-0.5 pl-2 pr-2 mx-0 m-1 text-xs hover:bg-blue-100 rounded-full border-blue-400 border truncate ${snapshot.isDragging ? '' : 'hidden'}`}
|
|
33
|
-
isDragging={snapshot.isDragging}
|
|
15
|
+
return (
|
|
16
|
+
<div {...provided.droppableProps} ref={provided.innerRef}>
|
|
17
|
+
{dimensions.map((f, index) => (
|
|
18
|
+
<Draggable key={f.dragId} draggableId={f.dragId} index={index}>
|
|
19
|
+
{(provided, snapshot) => {
|
|
20
|
+
return (
|
|
21
|
+
<>
|
|
22
|
+
<FieldPill
|
|
23
|
+
className={`dark:text-white pt-0.5 pb-0.5 pl-2 pr-2 mx-0 m-1 text-xs hover:bg-blue-100 dark:hover:bg-blue-800 rounded-full truncate border border-transparent ${
|
|
24
|
+
snapshot.isDragging ? "bg-blue-100 dark:bg-blue-800" : ""
|
|
25
|
+
}`}
|
|
26
|
+
ref={provided.innerRef}
|
|
27
|
+
isDragging={snapshot.isDragging}
|
|
28
|
+
{...provided.draggableProps}
|
|
29
|
+
{...provided.dragHandleProps}
|
|
34
30
|
>
|
|
35
|
-
<DataTypeIcon dataType={f.semanticType} analyticType={f.analyticType} /> {f.name}
|
|
31
|
+
<DataTypeIcon dataType={f.semanticType} analyticType={f.analyticType} /> {f.name}
|
|
32
|
+
|
|
36
33
|
</FieldPill>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
34
|
+
{
|
|
35
|
+
<FieldPill
|
|
36
|
+
className={`dark:text-white pt-0.5 pb-0.5 pl-2 pr-2 mx-0 m-1 text-xs hover:bg-blue-100 dark:hover:bg-blue-800 rounded-full border-blue-400 border truncate ${
|
|
37
|
+
snapshot.isDragging ? "bg-blue-100 dark:bg-blue-800" : "hidden"
|
|
38
|
+
}`}
|
|
39
|
+
isDragging={snapshot.isDragging}
|
|
40
|
+
>
|
|
41
|
+
<DataTypeIcon dataType={f.semanticType} analyticType={f.analyticType} />{" "}
|
|
42
|
+
{f.name}
|
|
43
|
+
</FieldPill>
|
|
44
|
+
}
|
|
45
|
+
</>
|
|
46
|
+
);
|
|
47
|
+
}}
|
|
48
|
+
</Draggable>
|
|
49
|
+
))}
|
|
50
|
+
</div>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
45
53
|
|
|
46
54
|
export default observer(DimFields);
|
|
@@ -2,11 +2,10 @@ import React from "react";
|
|
|
2
2
|
import { Droppable } from "@kanaries/react-beautiful-dnd";
|
|
3
3
|
import { useTranslation } from "react-i18next";
|
|
4
4
|
import styled from 'styled-components';
|
|
5
|
-
import { NestContainer } from "../../components/container";
|
|
6
5
|
import DimFields from "./dimFields";
|
|
7
6
|
import MeaFields from "./meaFields";
|
|
8
7
|
|
|
9
|
-
const DSContainer = styled
|
|
8
|
+
const DSContainer = styled.div`
|
|
10
9
|
@media (min-width: 768px) {
|
|
11
10
|
height: 680px;
|
|
12
11
|
}
|
|
@@ -16,14 +15,14 @@ const DatasetFields: React.FC = (props) => {
|
|
|
16
15
|
const { t } = useTranslation("translation", { keyPrefix: "main.tabpanel.DatasetFields" });
|
|
17
16
|
|
|
18
17
|
return (
|
|
19
|
-
<DSContainer className="border-gray-200 flex md:flex-col" style={{ paddingBlock: 0, paddingInline: '0.6em' }}>
|
|
18
|
+
<DSContainer className="p-1 m-0.5 border border-gray-200 dark:border-gray-700 flex md:flex-col" style={{ paddingBlock: 0, paddingInline: '0.6em' }}>
|
|
20
19
|
<h4 className="text-xs mb-2 flex-grow-0 cursor-default select-none mt-2">{t("field_list")}</h4>
|
|
21
20
|
<div className="pd-1 overflow-y-auto" style={{ maxHeight: "380px", minHeight: '100px' }}>
|
|
22
21
|
<Droppable droppableId="dimensions" direction="vertical">
|
|
23
22
|
{(provided, snapshot) => <DimFields provided={provided} />}
|
|
24
23
|
</Droppable>
|
|
25
24
|
</div>
|
|
26
|
-
<div className="border-t flex-grow pd-1 overflow-y-auto">
|
|
25
|
+
<div className="border-t dark:border-gray-700 flex-grow pd-1 overflow-y-auto">
|
|
27
26
|
<Droppable droppableId="measures" direction="vertical">
|
|
28
27
|
{(provided, snapshot) => <MeaFields provided={provided} />}
|
|
29
28
|
</Droppable>
|
|
@@ -1,58 +1,84 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import { Draggable, DroppableProvided } from
|
|
3
|
-
import { observer } from
|
|
4
|
-
import { useGlobalStore } from
|
|
5
|
-
import DataTypeIcon from
|
|
6
|
-
import { FieldPill } from
|
|
1
|
+
import React, { useCallback, useMemo } from "react";
|
|
2
|
+
import { Draggable, DroppableProvided } from "@kanaries/react-beautiful-dnd";
|
|
3
|
+
import { observer } from "mobx-react-lite";
|
|
4
|
+
import { useGlobalStore } from "../../store";
|
|
5
|
+
import DataTypeIcon from "../../components/dataTypeIcon";
|
|
6
|
+
import { FieldPill } from "./fieldPill";
|
|
7
|
+
import DropdownContext, { IDropdownContextOption } from "../../components/dropdownContext";
|
|
7
8
|
|
|
8
9
|
interface Props {
|
|
9
10
|
provided: DroppableProvided;
|
|
10
11
|
}
|
|
11
|
-
const MeaFields: React.FC<Props> = props => {
|
|
12
|
+
const MeaFields: React.FC<Props> = (props) => {
|
|
12
13
|
const { provided } = props;
|
|
13
14
|
const { vizStore } = useGlobalStore();
|
|
14
15
|
const measures = vizStore.draggableFieldState.measures;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
16
|
+
|
|
17
|
+
const MEA_ACTION_OPTIONS = useMemo<IDropdownContextOption[]>(() => {
|
|
18
|
+
return [
|
|
19
|
+
{
|
|
20
|
+
value: "bin",
|
|
21
|
+
label: "Bin",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
value: "log10",
|
|
25
|
+
label: "Log10",
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
}, []);
|
|
29
|
+
|
|
30
|
+
const fieldActionHandler = useCallback((selectedValue: any, opIndex: number, meaIndex: number) => {
|
|
31
|
+
if (selectedValue === "bin") {
|
|
32
|
+
vizStore.createBinField("measures", meaIndex);
|
|
33
|
+
} else if (selectedValue === "log10") {
|
|
34
|
+
vizStore.createLogField("measures", meaIndex);
|
|
35
|
+
}
|
|
36
|
+
}, []);
|
|
37
|
+
return (
|
|
38
|
+
<div {...provided.droppableProps} ref={provided.innerRef}>
|
|
39
|
+
{measures.map((f, index) => (
|
|
40
|
+
<Draggable key={f.dragId} draggableId={f.dragId} index={index}>
|
|
41
|
+
{(provided, snapshot) => {
|
|
42
|
+
return (
|
|
43
|
+
<div className="block">
|
|
44
|
+
<DropdownContext
|
|
45
|
+
disable={snapshot.isDragging}
|
|
46
|
+
options={MEA_ACTION_OPTIONS}
|
|
47
|
+
onSelect={(v, opIndex) => {
|
|
48
|
+
fieldActionHandler(v, opIndex, index);
|
|
49
|
+
}}
|
|
50
|
+
>
|
|
51
|
+
<FieldPill
|
|
52
|
+
className={`dark:text-white pt-0.5 pb-0.5 pl-2 pr-2 mx-0 m-1 text-xs hover:bg-purple-100 dark:hover:bg-purple-800 rounded-full truncate border border-transparent ${
|
|
53
|
+
snapshot.isDragging ? "bg-purple-100 dark:bg-purple-800" : ""
|
|
54
|
+
}`}
|
|
55
|
+
isDragging={snapshot.isDragging}
|
|
56
|
+
ref={provided.innerRef}
|
|
57
|
+
{...provided.draggableProps}
|
|
58
|
+
{...provided.dragHandleProps}
|
|
59
|
+
>
|
|
60
|
+
<DataTypeIcon dataType={f.semanticType} analyticType={f.analyticType} />{" "}
|
|
61
|
+
{f.name}
|
|
62
|
+
</FieldPill>
|
|
63
|
+
</DropdownContext>
|
|
29
64
|
{
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
<option value="log10">log10</option>
|
|
40
|
-
</select>
|
|
65
|
+
<FieldPill
|
|
66
|
+
className={`dark:text-white pt-0.5 pb-0.5 pl-2 pr-2 mx-0 m-1 text-xs hover:bg-purple-100 dark:hover:bg-purple-800 rounded-full border-purple-400 border truncate ${
|
|
67
|
+
snapshot.isDragging ? "bg-purple-100 dark:bg-purple-800" : "hidden"
|
|
68
|
+
}`}
|
|
69
|
+
isDragging={snapshot.isDragging}
|
|
70
|
+
>
|
|
71
|
+
<DataTypeIcon dataType={f.semanticType} analyticType={f.analyticType} />{" "}
|
|
72
|
+
{f.name}
|
|
73
|
+
</FieldPill>
|
|
41
74
|
}
|
|
42
|
-
</
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
</>
|
|
51
|
-
);
|
|
52
|
-
}}
|
|
53
|
-
</Draggable>
|
|
54
|
-
))}
|
|
55
|
-
</div>
|
|
56
|
-
}
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
}}
|
|
78
|
+
</Draggable>
|
|
79
|
+
))}
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
57
83
|
|
|
58
84
|
export default observer(MeaFields);
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import React, { Fragment } from "react";
|
|
2
|
+
import { Listbox, Menu, Transition } from "@headlessui/react";
|
|
3
|
+
import { CheckIcon, ChevronDownIcon, ChevronUpDownIcon } from "@heroicons/react/24/outline";
|
|
4
|
+
|
|
5
|
+
function classNames(...classes: string[]) {
|
|
6
|
+
return classes.filter(Boolean).join(" ");
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface IDropdownSelectOption {
|
|
10
|
+
label: string;
|
|
11
|
+
value: string;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
}
|
|
14
|
+
interface IDropdownSelectProps {
|
|
15
|
+
options?: IDropdownSelectOption[];
|
|
16
|
+
disable?: boolean;
|
|
17
|
+
selectedKey: string;
|
|
18
|
+
onSelect?: (value: string) => void;
|
|
19
|
+
placeholder?: string;
|
|
20
|
+
className?: string;
|
|
21
|
+
buttonClassName?: string;
|
|
22
|
+
}
|
|
23
|
+
const DropdownSelect: React.FC<IDropdownSelectProps> = (props) => {
|
|
24
|
+
const { options = [], disable, selectedKey, onSelect, placeholder = "Select an option", className, buttonClassName } = props;
|
|
25
|
+
|
|
26
|
+
const selectedItem = options.find((op) => op.value === selectedKey);
|
|
27
|
+
|
|
28
|
+
if (disable) {
|
|
29
|
+
return <Fragment>{props.children}</Fragment>;
|
|
30
|
+
}
|
|
31
|
+
let rootClassName = "flex truncate";
|
|
32
|
+
let btnComputedClassName = "grow shrink relative cursor-default text-xs rounded-lg bg-white dark:bg-zinc-900 px-2.5 py-1.5 pr-10 text-left border border-gray-200 focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 truncate"
|
|
33
|
+
if (buttonClassName) {
|
|
34
|
+
btnComputedClassName = btnComputedClassName + " " + buttonClassName;
|
|
35
|
+
}
|
|
36
|
+
if (className) {
|
|
37
|
+
rootClassName = rootClassName + " " + className;
|
|
38
|
+
}
|
|
39
|
+
return (
|
|
40
|
+
<Listbox
|
|
41
|
+
value={selectedKey}
|
|
42
|
+
onChange={(newKey) => {
|
|
43
|
+
onSelect && onSelect(newKey);
|
|
44
|
+
}}
|
|
45
|
+
>
|
|
46
|
+
<div className={rootClassName}>
|
|
47
|
+
<Listbox.Button className={btnComputedClassName}>
|
|
48
|
+
<span className="block truncate">{selectedItem?.label || ""}</span>
|
|
49
|
+
{ selectedItem === undefined && <span className="block truncate text-gray-400">{placeholder}</span>}
|
|
50
|
+
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
|
|
51
|
+
<ChevronUpDownIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
|
|
52
|
+
</span>
|
|
53
|
+
</Listbox.Button>
|
|
54
|
+
<Transition
|
|
55
|
+
as={Fragment}
|
|
56
|
+
leave="transition ease-in duration-100"
|
|
57
|
+
leaveFrom="opacity-100"
|
|
58
|
+
leaveTo="opacity-0"
|
|
59
|
+
>
|
|
60
|
+
<Listbox.Options className="absolute z-50 mt-8 max-h-60 w-full overflow-auto rounded-md bg-white dark:bg-zinc-900 py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
|
|
61
|
+
{options.map((op, opIndex) => (
|
|
62
|
+
<Listbox.Option
|
|
63
|
+
key={op.value}
|
|
64
|
+
className={({ active }) =>
|
|
65
|
+
`relative cursor-default select-none py-2 pl-10 pr-4 ${
|
|
66
|
+
active ? "bg-amber-100 text-amber-900 dark:bg-amber-800 dark:text-amber-50" : "text-gray-900 dark:text-amber-50"
|
|
67
|
+
}`
|
|
68
|
+
}
|
|
69
|
+
value={op.value}
|
|
70
|
+
>
|
|
71
|
+
{({ selected }) => (
|
|
72
|
+
<>
|
|
73
|
+
<span className={`block truncate ${selected ? "font-medium" : "font-normal"}`}>
|
|
74
|
+
{op.label}
|
|
75
|
+
</span>
|
|
76
|
+
{selected && (
|
|
77
|
+
<span className="absolute inset-y-0 left-0 flex items-center pl-3 text-amber-600">
|
|
78
|
+
<CheckIcon className="h-5 w-5" aria-hidden="true" />
|
|
79
|
+
</span>
|
|
80
|
+
)}
|
|
81
|
+
</>
|
|
82
|
+
)}
|
|
83
|
+
</Listbox.Option>
|
|
84
|
+
))}
|
|
85
|
+
</Listbox.Options>
|
|
86
|
+
</Transition>
|
|
87
|
+
</div>
|
|
88
|
+
</Listbox>
|
|
89
|
+
);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export default DropdownSelect;
|