@kanaries/graphic-walker 0.2.12 → 0.2.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,171 +1,27 @@
1
- import React, { useMemo } from "react";
2
- import styled from "styled-components";
1
+ import React from "react";
3
2
  import { observer } from "mobx-react-lite";
4
- import { IMutField } from "../interfaces";
5
3
  import { useGlobalStore } from "../store";
6
- import { useTranslation } from "react-i18next";
4
+ import DataTable from "../components/dataTable";
5
+ import { toJS } from "mobx";
7
6
 
8
7
  interface TableProps {
9
8
  size?: number;
10
9
  }
11
- const Container = styled.div`
12
- overflow-x: auto;
13
- table {
14
- box-sizing: content-box;
15
- border-collapse: collapse;
16
- font-size: 12px;
17
- thead {
18
- th {
19
- }
20
- th.number {
21
- border-top: 3px solid #5cdbd3;
22
- }
23
- th.text {
24
- border-top: 3px solid #69c0ff;
25
- }
26
- }
27
- tbody {
28
- td {
29
- }
30
- td.number {
31
- text-align: right;
32
- }
33
- td.text {
34
- text-align: left;
35
- }
36
- }
37
- }
38
- `;
39
- const ANALYTIC_TYPE_LIST = ["dimension", "measure"];
40
- const SEMANTIC_TYPE_LIST = ["nominal", "ordinal", "quantitative", "temporal"];
41
- // function getCellType(field: IMutField): 'number' | 'text' {
42
- // return field.dataType === 'number' || field.dataType === 'integer' ? 'number' : 'text';
43
- // }
44
- function getHeaderType(field: IMutField): "number" | "text" {
45
- return field.analyticType === "dimension" ? "text" : "number";
46
- }
47
-
48
- function getHeaderClassNames(field: IMutField) {
49
- return field.analyticType === "dimension" ? "border-t-4 border-blue-400" : "border-t-4 border-teal-400";
50
- }
51
-
52
- function getSemanticColors(field: IMutField): string {
53
- switch (field.semanticType) {
54
- case "nominal":
55
- return "bg-indigo-100 text-indigo-800";
56
- case "ordinal":
57
- return "bg-purple-100 text-purple-800"
58
- case "quantitative":
59
- return "bg-green-100 text-green-800"
60
- case "temporal":
61
- return "bg-yellow-100 text-yellow-800"
62
- default:
63
- return "bg-gray-400";
64
- }
65
- }
66
10
 
67
11
  const Table: React.FC<TableProps> = (props) => {
68
12
  const { size = 10 } = props;
69
13
  const { commonStore } = useGlobalStore();
70
14
  const { tmpDSRawFields, tmpDataSource } = commonStore;
71
- const { t } = useTranslation();
72
-
73
- const analyticTypeList = useMemo<{ value: string; label: string }[]>(() => {
74
- return ANALYTIC_TYPE_LIST.map((at) => ({
75
- value: at,
76
- label: t(`constant.analytic_type.${at}`),
77
- }));
78
- }, []);
79
-
80
- const semanticTypeList = useMemo<{ value: string; label: string }[]>(() => {
81
- return SEMANTIC_TYPE_LIST.map((st) => ({
82
- value: st,
83
- label: t(`constant.semantic_type.${st}`),
84
- }));
85
- }, []);
86
15
 
87
16
  return (
88
- <Container className="rounded border-gray-200 border">
89
- <table className="min-w-full divide-y divide-gray-30">
90
- <thead className="bg-gray-50">
91
- <tr className="divide-x divide-gray-200">
92
- {tmpDSRawFields.map((field, fIndex) => (
93
- <th key={field.fid} className={""}>
94
- <div
95
- className={
96
- getHeaderClassNames(field) +
97
- " whitespace-nowrap py-3.5 px-6 text-left text-xs font-semibold text-gray-900 sm:pl-6"
98
- }
99
- >
100
- <b>{field.name || field.fid}</b>
101
- <div>
102
- <select
103
- className={
104
- "px-2 py font-normal mt-2 rounded-full text-xs text-white " +
105
- (field.analyticType === "dimension" ? "bg-blue-500" : "bg-teal-500")
106
- }
107
- // className="border-b border-gray-200 bg-gray-50 pl-0 mt-2 font-light"
108
- value={field.analyticType}
109
- onChange={(e) => {
110
- commonStore.updateTempFieldAnalyticType(
111
- field.fid,
112
- e.target.value as IMutField["analyticType"]
113
- );
114
- }}
115
- >
116
- {analyticTypeList.map((type) => (
117
- <option key={type.value} value={type.value}>
118
- {type.label}
119
- </option>
120
- ))}
121
- </select>
122
- </div>
123
- <div>
124
- <select
125
- className={
126
- "inline-block px-2.5 py-0.5 text-xs font-medium mt-1 rounded-full text-xs text-white " +
127
- getSemanticColors(field)
128
- }
129
- // className="border-b border-gray-200 bg-gray-50 pl-0 mt-2 font-light"
130
- value={field.semanticType}
131
- onChange={(e) => {
132
- commonStore.updateTempFieldSemanticType(
133
- field.fid,
134
- e.target.value as IMutField["semanticType"]
135
- );
136
- }}
137
- >
138
- {semanticTypeList.map((type) => (
139
- <option key={type.value} value={type.value}>
140
- {type.label}
141
- </option>
142
- ))}
143
- </select>
144
- </div>
145
- </div>
146
- </th>
147
- ))}
148
- </tr>
149
- </thead>
150
- <tbody className="divide-y divide-gray-200 bg-white">
151
- {tmpDataSource.slice(0, size).map((record, index) => (
152
- <tr className={"divide-x divide-gray-200 " + (index % 2 ? "bg-gray-50" : "")} key={index}>
153
- {tmpDSRawFields.map((field) => (
154
- <td
155
- key={field.fid + index}
156
- className={
157
- getHeaderType(field) +
158
- " whitespace-nowrap py-2 pl-4 pr-3 text-xs text-gray-500 sm:pl-6"
159
- }
160
- >
161
- {record[field.fid]}
162
- </td>
163
- ))}
164
- </tr>
165
- ))}
166
- </tbody>
167
- </table>
168
- </Container>
17
+ <DataTable
18
+ size={size}
19
+ metas={toJS(tmpDSRawFields)}
20
+ data={tmpDataSource}
21
+ onMetaChange={(fid, fIndex, diffMeta) => {
22
+ commonStore.updateTempDatasetMetas(fid, diffMeta);
23
+ }}
24
+ />
169
25
  );
170
26
  };
171
27
 
package/src/interfaces.ts CHANGED
@@ -178,4 +178,9 @@ export interface IVisSpec {
178
178
  readonly name?: [string, Record<string, any>?];
179
179
  readonly encodings: DeepReadonly<DraggableFieldState>;
180
180
  readonly config: DeepReadonly<IVisualConfig>;
181
+ }
182
+
183
+ export enum ISegmentKey {
184
+ vis = 'vis',
185
+ data = 'data'
181
186
  }
@@ -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": {
@@ -177,5 +181,9 @@
177
181
  "children_major_factor": "major factor",
178
182
  "children_outlier": "outlier"
179
183
  }
184
+ },
185
+ "actions": {
186
+ "prev": "Previous",
187
+ "next": "Next"
180
188
  }
181
189
  }
@@ -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": {
@@ -177,5 +181,9 @@
177
181
  "children_major_factor": "子节点主要因素",
178
182
  "children_outlier": "子节点异常"
179
183
  }
184
+ },
185
+ "actions": {
186
+ "prev": "向前",
187
+ "next": "向后"
180
188
  }
181
189
  }
@@ -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);
@@ -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) {