@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.
Files changed (99) hide show
  1. package/dist/App.d.ts +4 -2
  2. package/dist/assets/explainer.worker-8428eb12.js.map +1 -1
  3. package/dist/components/button/base.d.ts +1 -0
  4. package/dist/components/button/defaultMini.d.ts +4 -0
  5. package/dist/components/button/primaryMini.d.ts +4 -0
  6. package/dist/components/dataTable/index.d.ts +10 -0
  7. package/dist/components/dataTable/pagination.d.ts +10 -0
  8. package/dist/components/dropdownContext/index.d.ts +13 -0
  9. package/dist/components/dropdownSelect/index.d.ts +17 -0
  10. package/dist/components/modal.d.ts +1 -0
  11. package/dist/components/tabs/defaultTab.d.ts +14 -0
  12. package/dist/components/tabs/{pureTab.d.ts → editableTab.d.ts} +2 -2
  13. package/dist/components/toolbar/toolbar-item.d.ts +1 -0
  14. package/dist/dataSource/dataSelection/config.d.ts +2 -0
  15. package/dist/dataSource/datasetConfig/index.d.ts +3 -0
  16. package/dist/dataSource/index.d.ts +1 -1
  17. package/dist/fields/datasetFields/dimFields.d.ts +2 -2
  18. package/dist/fields/datasetFields/meaFields.d.ts +2 -2
  19. package/dist/fields/encodeFields/singleEncodeDropDown.d.ts +17 -0
  20. package/dist/fields/encodeFields/singleEncodeEditor.d.ts +11 -0
  21. package/dist/fields/obComponents/obPill.d.ts +3 -3
  22. package/dist/graphic-walker.es.js +21038 -19133
  23. package/dist/graphic-walker.es.js.map +1 -1
  24. package/dist/graphic-walker.umd.js +247 -170
  25. package/dist/graphic-walker.umd.js.map +1 -1
  26. package/dist/insightBoard/index.d.ts +1 -1
  27. package/dist/interfaces.d.ts +5 -0
  28. package/dist/renderer/index.d.ts +3 -1
  29. package/dist/segments/segmentNav.d.ts +3 -0
  30. package/dist/store/commonStore.d.ts +7 -1
  31. package/dist/store/visualSpecStore.d.ts +1 -0
  32. package/dist/utils/index.d.ts +2 -0
  33. package/dist/utils/media.d.ts +2 -0
  34. package/dist/vis/react-vega.d.ts +2 -0
  35. package/dist/vis/theme.d.ts +130 -0
  36. package/package.json +2 -1
  37. package/src/App.tsx +74 -46
  38. package/src/components/button/base.ts +1 -0
  39. package/src/components/button/default.tsx +6 -2
  40. package/src/components/button/defaultMini.tsx +17 -0
  41. package/src/components/button/primary.tsx +6 -2
  42. package/src/components/button/primaryMini.tsx +21 -0
  43. package/src/components/callout.tsx +4 -1
  44. package/src/components/clickMenu.tsx +4 -2
  45. package/src/components/container.tsx +9 -0
  46. package/src/components/dataTable/index.tsx +177 -0
  47. package/src/components/dataTable/pagination.tsx +44 -0
  48. package/src/components/dataTypeIcon.tsx +1 -1
  49. package/src/components/dropdownContext/index.tsx +64 -0
  50. package/src/components/dropdownSelect/index.tsx +92 -0
  51. package/src/components/modal.tsx +26 -14
  52. package/src/components/tabs/defaultTab.tsx +43 -0
  53. package/src/components/tabs/{pureTab.tsx → editableTab.tsx} +7 -7
  54. package/src/components/toolbar/components.tsx +18 -1
  55. package/src/components/toolbar/index.tsx +5 -0
  56. package/src/components/toolbar/toolbar-button.tsx +6 -1
  57. package/src/components/toolbar/toolbar-item.tsx +4 -0
  58. package/src/components/toolbar/toolbar-select-button.tsx +21 -4
  59. package/src/components/toolbar/toolbar-toggle-button.tsx +6 -1
  60. package/src/components/tooltip.tsx +4 -1
  61. package/src/dataSource/dataSelection/config.ts +28 -0
  62. package/src/dataSource/dataSelection/csvData.tsx +77 -32
  63. package/src/dataSource/dataSelection/gwFile.tsx +0 -8
  64. package/src/dataSource/dataSelection/index.tsx +1 -2
  65. package/src/dataSource/dataSelection/publicData.tsx +2 -3
  66. package/src/dataSource/datasetConfig/index.tsx +21 -0
  67. package/src/dataSource/index.tsx +81 -61
  68. package/src/dataSource/table.tsx +11 -155
  69. package/src/fields/aestheticFields.tsx +3 -1
  70. package/src/fields/components.tsx +21 -2
  71. package/src/fields/datasetFields/dimFields.tsx +43 -35
  72. package/src/fields/datasetFields/index.tsx +2 -2
  73. package/src/fields/datasetFields/meaFields.tsx +73 -47
  74. package/src/fields/encodeFields/singleEncodeDropDown.tsx +92 -0
  75. package/src/fields/encodeFields/singleEncodeEditor.tsx +78 -0
  76. package/src/fields/filterField/filterEditDialog.tsx +2 -1
  77. package/src/fields/filterField/filterPill.tsx +1 -1
  78. package/src/fields/filterField/slider.tsx +1 -1
  79. package/src/fields/filterField/tabs.tsx +11 -21
  80. package/src/fields/obComponents/obPill.tsx +65 -35
  81. package/src/index.css +13 -0
  82. package/src/insightBoard/index.tsx +24 -23
  83. package/src/insightBoard/radioGroupButtons.tsx +7 -0
  84. package/src/interfaces.ts +6 -0
  85. package/src/lib/inferMeta.ts +1 -1
  86. package/src/locales/en-US.json +14 -3
  87. package/src/locales/zh-CN.json +12 -1
  88. package/src/main.tsx +1 -1
  89. package/src/renderer/index.tsx +3 -1
  90. package/src/segments/segmentNav.tsx +58 -0
  91. package/src/segments/visNav.tsx +2 -2
  92. package/src/store/commonStore.ts +28 -2
  93. package/src/store/visualSpecStore.ts +16 -0
  94. package/src/utils/index.ts +19 -0
  95. package/src/utils/media.ts +26 -0
  96. package/src/utils/normalization.ts +2 -1
  97. package/src/vis/react-vega.tsx +20 -4
  98. package/src/vis/theme.ts +126 -0
  99. 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
- <TabList role="tablist">
372
- {
373
- tabs.map((tab, i) => (
374
- <TabHeader
375
- key={i}
376
- role="tab"
377
- aria-selected={which === tab}
378
- id={`filter-tab-${tab.replaceAll(/ /g, '_')}`}
379
- aria-controls={`filter-panel-${tab.replaceAll(/ /g, '_')}`}
380
- tabIndex={-1}
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 '@heroicons/react/24/outline';
2
- import { observer } from 'mobx-react-lite';
3
- import React 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
-
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('translation', { keyPrefix: 'constant.aggregator' });
23
+ const { t } = useTranslation("translation", { keyPrefix: "constant.aggregator" });
24
24
 
25
- return <Pill
26
- ref={provided.innerRef}
27
- colType={field.analyticType === 'dimension' ? 'discrete' : 'continuous'}
28
- {...provided.draggableProps}
29
- {...provided.dragHandleProps}
30
- >
31
- {field.name}&nbsp;
32
- {field.analyticType === 'measure' && field.fid !== COUNT_FIELD_ID && visualConfig.defaultAggregated && (
33
- <select
34
- className="bg-transparent text-gray-700 float-right focus:outline-none focus:border-gray-500"
35
- value={field.aggName || ''}
36
- onChange={(e) => { vizStore.setFieldAggregator(dkey.id, fIndex, e.target.value) }}
37
- >
38
- {
39
- AGGREGATOR_LIST.map(op => <option value={op} key={op}>{t(op)}</option>)
40
- }
41
- </select>
42
- )}
43
- {field.analyticType === 'dimension' && field.sort === 'ascending' && <BarsArrowUpIcon className='float-right w-3' role="status" aria-label="Sorted in ascending order" />}
44
- {field.analyticType === 'dimension' && field.sort === 'descending' && <BarsArrowDownIcon className='float-right w-3' role="status" aria-label="Sorted in descending order" />}
45
- </Pill>
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>&nbsp;
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 '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';
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
- return <div>
16
- {
17
- showInsightBoard && <Modal onClose={onCloseModal}>
18
- <InsightMainBoard
19
- dataSource={currentDataset.dataSource}
20
- fields={toJS(draggableFieldState.fields)}
21
- viewDs={viewDimensions}
22
- viewMs={viewMeasures}
23
- filters={toJS(filters)}
24
- />
25
- </Modal>
26
- }
27
- </div>
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
  }
@@ -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;
@@ -50,8 +50,8 @@
50
50
  },
51
51
  "draggable_key": {
52
52
  "fields": "Fields",
53
- "columns": "Columns",
54
- "rows": "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
  }
@@ -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
@@ -9,7 +9,7 @@ inject();
9
9
 
10
10
  ReactDOM.render(
11
11
  <React.StrictMode>
12
- <GraphicWalker />
12
+ <GraphicWalker themeKey="g2" />
13
13
  </React.StrictMode>,
14
14
  document.getElementById("root")
15
15
  );
@@ -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 (props, ref) {
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);
@@ -1,6 +1,6 @@
1
1
  import React, { useCallback } from "react";
2
2
  import { observer } from "mobx-react-lite";
3
- import PureTabs, { ITabOption } from "../components/tabs/pureTab";
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
- <PureTabs
39
+ <EditableTabs
40
40
  selectedKey={visList[visIndex].visId}
41
41
  tabs={tabs}
42
42
  onEditLabel={editLabelHandler}
@@ -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) {