@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.
Files changed (99) hide show
  1. package/dist/App.d.ts +6 -3
  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/callout.d.ts +2 -0
  7. package/dist/components/dropdownContext/index.d.ts +13 -0
  8. package/dist/components/dropdownSelect/index.d.ts +17 -0
  9. package/dist/components/modal.d.ts +1 -0
  10. package/dist/components/toolbar/components.d.ts +4 -1
  11. package/dist/components/toolbar/index.d.ts +2 -0
  12. package/dist/components/toolbar/toolbar-item.d.ts +4 -0
  13. package/dist/components/tooltip.d.ts +2 -0
  14. package/dist/dataSource/dataSelection/config.d.ts +2 -0
  15. package/dist/dataSource/index.d.ts +1 -1
  16. package/dist/fields/components.d.ts +0 -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/filterField/filterEditDialog.d.ts +1 -1
  22. package/dist/fields/obComponents/obPill.d.ts +3 -3
  23. package/dist/graphic-walker.es.js +21205 -19397
  24. package/dist/graphic-walker.es.js.map +1 -1
  25. package/dist/graphic-walker.umd.js +181 -236
  26. package/dist/graphic-walker.umd.js.map +1 -1
  27. package/dist/insightBoard/index.d.ts +1 -1
  28. package/dist/interfaces.d.ts +3 -0
  29. package/dist/renderer/index.d.ts +7 -3
  30. package/dist/store/visualSpecStore.d.ts +1 -0
  31. package/dist/utils/index.d.ts +2 -0
  32. package/dist/utils/media.d.ts +3 -0
  33. package/dist/vis/react-vega.d.ts +5 -1
  34. package/dist/vis/theme.d.ts +146 -0
  35. package/dist/visualSettings/index.d.ts +2 -1
  36. package/package.json +2 -1
  37. package/src/App.tsx +24 -16
  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 +9 -4
  44. package/src/components/clickMenu.tsx +1 -5
  45. package/src/components/dataTable/index.tsx +42 -52
  46. package/src/components/dataTable/pagination.tsx +4 -4
  47. package/src/components/dataTypeIcon.tsx +1 -1
  48. package/src/components/dropdownContext/index.tsx +64 -0
  49. package/src/components/dropdownSelect/index.tsx +92 -0
  50. package/src/components/modal.tsx +20 -22
  51. package/src/components/sizeSetting.tsx +2 -2
  52. package/src/components/tabs/defaultTab.tsx +4 -4
  53. package/src/components/tabs/editableTab.tsx +5 -5
  54. package/src/components/toolbar/components.tsx +10 -8
  55. package/src/components/toolbar/index.tsx +16 -4
  56. package/src/components/toolbar/toolbar-button.tsx +8 -2
  57. package/src/components/toolbar/toolbar-item.tsx +18 -9
  58. package/src/components/toolbar/toolbar-select-button.tsx +21 -8
  59. package/src/components/toolbar/toolbar-toggle-button.tsx +8 -2
  60. package/src/components/tooltip.tsx +10 -3
  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/index.tsx +80 -61
  67. package/src/fields/aestheticFields.tsx +3 -1
  68. package/src/fields/components.tsx +20 -38
  69. package/src/fields/datasetFields/dimFields.tsx +43 -35
  70. package/src/fields/datasetFields/index.tsx +3 -4
  71. package/src/fields/datasetFields/meaFields.tsx +73 -47
  72. package/src/fields/encodeFields/singleEncodeDropDown.tsx +92 -0
  73. package/src/fields/encodeFields/singleEncodeEditor.tsx +78 -0
  74. package/src/fields/filterField/filterEditDialog.tsx +63 -98
  75. package/src/fields/filterField/filterPill.tsx +1 -1
  76. package/src/fields/filterField/slider.tsx +2 -2
  77. package/src/fields/filterField/tabs.tsx +11 -21
  78. package/src/fields/obComponents/obPill.tsx +65 -35
  79. package/src/index.css +13 -0
  80. package/src/insightBoard/index.tsx +24 -23
  81. package/src/insightBoard/mainBoard.tsx +9 -2
  82. package/src/insightBoard/radioGroupButtons.tsx +7 -0
  83. package/src/interfaces.ts +5 -1
  84. package/src/lib/inferMeta.ts +1 -1
  85. package/src/locales/en-US.json +11 -5
  86. package/src/locales/i18n.ts +7 -0
  87. package/src/locales/ja-JP.json +195 -0
  88. package/src/locales/zh-CN.json +9 -3
  89. package/src/main.tsx +1 -1
  90. package/src/renderer/index.tsx +96 -70
  91. package/src/store/visualSpecStore.ts +16 -0
  92. package/src/utils/index.ts +19 -0
  93. package/src/utils/media.ts +31 -0
  94. package/src/utils/normalization.ts +2 -1
  95. package/src/vis/react-vega.tsx +36 -5
  96. package/src/vis/theme.ts +124 -0
  97. package/src/visualSettings/index.tsx +29 -33
  98. package/dist/components/container.d.ts +0 -2
  99. package/src/components/container.tsx +0 -16
@@ -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",
@@ -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"
@@ -165,7 +167,9 @@
165
167
  "btn": {
166
168
  "select_all": "Select All",
167
169
  "unselect_all": "Unselect All",
168
- "reverse": "Reverse Selection"
170
+ "reverse": "Reverse Selection",
171
+ "confirm": "Confirm",
172
+ "cancel": "Cancel"
169
173
  }
170
174
  },
171
175
  "explain": {
@@ -184,6 +188,8 @@
184
188
  },
185
189
  "actions": {
186
190
  "prev": "Previous",
187
- "next": "Next"
191
+ "next": "Next",
192
+ "drop_field": "Drop Field Here",
193
+ "confirm": "Confirm"
188
194
  }
189
195
  }
@@ -3,6 +3,7 @@ import { initReactI18next } from 'react-i18next';
3
3
  import LanguageDetector from 'i18next-browser-languagedetector';
4
4
 
5
5
  import localeEnUs from './en-US.json';
6
+ import localeJaJp from './ja-JP.json';
6
7
  import localeZhCn from './zh-CN.json';
7
8
 
8
9
 
@@ -19,6 +20,12 @@ const locales: Resource & { 'en-US': any } = {
19
20
  'zh-CN': {
20
21
  translation: localeZhCn,
21
22
  },
23
+ 'ja': {
24
+ translation: localeJaJp,
25
+ },
26
+ 'ja-JP': {
27
+ translation: localeJaJp,
28
+ }
22
29
  } as const;
23
30
 
24
31
  i18n.use(initReactI18next).use(LanguageDetector).init({
@@ -0,0 +1,195 @@
1
+ {
2
+ "constant": {
3
+ "row_count": "行数",
4
+ "analytic_type": {
5
+ "dimension": "次元",
6
+ "measure": "尺度"
7
+ },
8
+ "semantic_type": {
9
+ "nominal": "名義尺度",
10
+ "ordinal": "順序尺度",
11
+ "temporal": "時間尺度",
12
+ "quantitative": "数量尺度"
13
+ },
14
+ "mark_type": {
15
+ "__enum__": "マークタイプ",
16
+ "auto": "自動",
17
+ "bar": "バー",
18
+ "line": "ライン",
19
+ "area": "エリア",
20
+ "trail": "トレイル",
21
+ "point": "散布図",
22
+ "circle": "円",
23
+ "tick": "棒線図",
24
+ "rect": "長方形",
25
+ "arc": "アーク",
26
+ "boxplot": "ボックスプロット"
27
+ },
28
+ "stack_mode": {
29
+ "__enum__": "スタックモード",
30
+ "none": "なし",
31
+ "stack": "スタック",
32
+ "normalize": "正規化"
33
+ },
34
+ "layout_type": {
35
+ "__enum__": "レイアウトタイプ",
36
+ "auto": "自動",
37
+ "fixed": "固定"
38
+ },
39
+ "exploration_mode": {
40
+ "__enum__": "探索モード",
41
+ "none": "オフ",
42
+ "brush": "ブラシ",
43
+ "point": "ポイント"
44
+ },
45
+ "brush_mode": {
46
+ "__enum__": "ブラシの方向",
47
+ "default": "両方",
48
+ "x": "X軸のみ",
49
+ "y": "Y軸のみ"
50
+ },
51
+ "draggable_key": {
52
+ "fields": "フィールド",
53
+ "columns": "X軸",
54
+ "rows": "Y軸",
55
+ "color": "色",
56
+ "opacity": "不透明度",
57
+ "size": "大きさ",
58
+ "shape": "形状",
59
+ "theta": "角度",
60
+ "radius": "半径",
61
+ "filters": "フィルター"
62
+ },
63
+ "aggregator": {
64
+ "sum": "合計",
65
+ "mean": "平均",
66
+ "count": "件数",
67
+ "median": "中央値",
68
+ "min": "最小値",
69
+ "max": "最大値",
70
+ "q1": "第一四分位数",
71
+ "q3": "第三四分位数",
72
+ "stdev": "標準偏差",
73
+ "variance": "分散"
74
+ },
75
+ "filter_type": {
76
+ "one_of": "1つ以上",
77
+ "range": "範囲",
78
+ "temporal_range": "日付範囲"
79
+ }
80
+ },
81
+ "App": {
82
+ "labels": {
83
+ "data_interpretation": "データ解釈"
84
+ },
85
+ "segments": {
86
+ "vis": "可視化",
87
+ "data": "データ"
88
+ }
89
+ },
90
+ "DataSource": {
91
+ "labels": {
92
+ "cur_dataset": "現在のデータセット"
93
+ },
94
+ "buttons": {
95
+ "create_dataset": "データセットを作成",
96
+ "export_as_file": "ファイルとして保存",
97
+ "import_file": "Graphic Walkerファイルをインポート"
98
+ },
99
+ "dialog": {
100
+ "create_data_source": "新しいデータソース",
101
+ "data_types": "データソースの種類",
102
+ "text_file_data": "ローカルファイル",
103
+ "public_data": "公開データセット",
104
+ "data_source_type": "データソースの種類:{{sourceType}}",
105
+ "file": {
106
+ "open": "開く...",
107
+ "submit": "提出する",
108
+ "dataset_name": "データセット名",
109
+ "choose_file": "データファイルを選択してください",
110
+ "get_start_desc": "データセットの作成を始めましょう。"
111
+ },
112
+ "public": {
113
+ "submit": "提出する"
114
+ }
115
+ }
116
+ },
117
+ "main": {
118
+ "tablist": {
119
+ "new": "+ 新規作成",
120
+ "autoTitle": "グラフ {{idx}}"
121
+ },
122
+ "tabpanel": {
123
+ "menubar": {
124
+ "undo": "元に戻す",
125
+ "redo": "やり直し"
126
+ },
127
+ "settings": {
128
+ "toggle": {
129
+ "aggregation": "集計",
130
+ "stack": "スタック",
131
+ "axes_resize": "軸のサイズ変更",
132
+ "debug": "デバッグ"
133
+ },
134
+ "sort": "並べ替えの順序",
135
+ "button": {
136
+ "ascending": "昇順に並べ替える",
137
+ "descending": "降順に並べ替える",
138
+ "transpose": "転置",
139
+ "export_chart": "エクスポート",
140
+ "export_chart_as": "{{type}}としてエクスポート"
141
+ },
142
+ "size": "サイズ変更",
143
+ "size_setting": {
144
+ "width": "幅",
145
+ "height": "高さ"
146
+ }
147
+ },
148
+ "DatasetFields": {
149
+ "field_list": "フィールド一覧"
150
+ }
151
+ }
152
+ },
153
+ "filters": {
154
+ "to_edit": "このルールを編集",
155
+ "empty_rule": "! (空のルール)",
156
+ "editing": "ルールを編集",
157
+ "form": {
158
+ "name": "フィールド",
159
+ "rule": "ルール"
160
+ },
161
+ "header": {
162
+ "visibility": "表示",
163
+ "value": "ラベル",
164
+ "count": "件数"
165
+ },
166
+ "selected_keys": "{{ count }}件が選択されました",
167
+ "btn": {
168
+ "select_all": "すべて選択",
169
+ "unselect_all": "すべての選択を解除",
170
+ "reverse": "選択を反転する",
171
+ "confirm": "確認",
172
+ "cancel": "キャンセル"
173
+ }
174
+ },
175
+ "explain": {
176
+ "lg_than": "より大きい",
177
+ "sm_than": "より小さい",
178
+ "expection": "期待値",
179
+ "unrecognized": "未認識",
180
+ "score": "スコア",
181
+ "contains": "を含む",
182
+ "reason": {
183
+ "selection_dim_distribution": "次元のヒント",
184
+ "selection_mea_distribution": "尺度のヒント",
185
+ "children_major_factor": "主要要因",
186
+ "children_outlier": "外れ値"
187
+ }
188
+ },
189
+ "actions": {
190
+ "prev": "前へ",
191
+ "next": "次へ",
192
+ "drop_field": "ここにフィールドをドロップ",
193
+ "confirm": "確認"
194
+ }
195
+ }
@@ -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": "确认"
@@ -165,7 +167,9 @@
165
167
  "btn": {
166
168
  "select_all": "全部选中",
167
169
  "unselect_all": "全部取消",
168
- "reverse": "选择反向"
170
+ "reverse": "选择反向",
171
+ "confirm": "确认",
172
+ "cancel": "取消"
169
173
  }
170
174
  },
171
175
  "explain": {
@@ -184,6 +188,8 @@
184
188
  },
185
189
  "actions": {
186
190
  "prev": "向前",
187
- "next": "向后"
191
+ "next": "向后",
192
+ "drop_field": "拖拽字段至此",
193
+ "confirm": "确认"
188
194
  }
189
195
  }
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
  );
@@ -1,44 +1,50 @@
1
- import { runInAction, toJS } from 'mobx';
2
- import { observer } from 'mobx-react-lite';
3
- import { Resizable } from 're-resizable';
4
- import React, { useState, useCallback, useEffect, useRef, forwardRef } from 'react';
5
- import { applyFilter } from '../services';
6
- import { useGlobalStore } from '../store';
7
- import ReactVega, { IReactVegaHandler } from '../vis/react-vega';
1
+ import { runInAction, toJS } from "mobx";
2
+ import { observer } from "mobx-react-lite";
3
+ import { Resizable } from "re-resizable";
4
+ import React, { useState, useCallback, useEffect, useRef, forwardRef } from "react";
5
+ import { applyFilter } from "../services";
6
+ import { useGlobalStore } from "../store";
7
+ import ReactVega, { IReactVegaHandler } from "../vis/react-vega";
8
+ import { IDarkMode, IThemeKey } from "../interfaces";
8
9
 
9
-
10
- const ReactiveRenderer = forwardRef<IReactVegaHandler, {}>(function ReactiveRenderer (props, ref) {
10
+ const ReactiveRenderer = forwardRef<IReactVegaHandler, { themeKey?: IThemeKey; dark?: IDarkMode }>(function ReactiveRenderer(
11
+ { themeKey, dark },
12
+ ref
13
+ ) {
11
14
  const { vizStore, commonStore } = useGlobalStore();
12
15
  const { draggableFieldState, visualConfig } = vizStore;
13
16
  const { geoms, interactiveScale, defaultAggregated, stack, showActions, size, exploration } = visualConfig;
14
17
  const { currentDataset } = commonStore;
15
18
  const { filters } = draggableFieldState;
16
19
 
17
- const rows = toJS(draggableFieldState.rows)
18
- const columns = toJS(draggableFieldState.columns)
19
- const color = toJS(draggableFieldState.color)
20
- const opacity = toJS(draggableFieldState.opacity)
21
- const shape = toJS(draggableFieldState.shape)
22
- const theta = toJS(draggableFieldState.theta)
23
- const radius = toJS(draggableFieldState.radius)
24
- const sizeChannel = toJS(draggableFieldState.size)
20
+ const rows = toJS(draggableFieldState.rows);
21
+ const columns = toJS(draggableFieldState.columns);
22
+ const color = toJS(draggableFieldState.color);
23
+ const opacity = toJS(draggableFieldState.opacity);
24
+ const shape = toJS(draggableFieldState.shape);
25
+ const theta = toJS(draggableFieldState.theta);
26
+ const radius = toJS(draggableFieldState.radius);
27
+ const sizeChannel = toJS(draggableFieldState.size);
25
28
 
26
- const rowLeftFacetFields = rows.slice(0, -1).filter(f => f.analyticType === 'dimension');
27
- const colLeftFacetFields = columns.slice(0, -1).filter(f => f.analyticType === 'dimension');
29
+ const rowLeftFacetFields = rows.slice(0, -1).filter((f) => f.analyticType === "dimension");
30
+ const colLeftFacetFields = columns.slice(0, -1).filter((f) => f.analyticType === "dimension");
28
31
 
29
32
  const hasFacet = rowLeftFacetFields.length > 0 || colLeftFacetFields.length > 0;
30
33
 
31
- const shouldTriggerMenu = exploration.mode === 'none';
32
-
33
- const handleGeomClick = useCallback((values: any, e: any) => {
34
- if (shouldTriggerMenu) {
35
- e.stopPropagation();
36
- runInAction(() => {
37
- commonStore.showEmbededMenu([e.pageX, e.pageY])
38
- commonStore.setFilters(values);
39
- });
40
- }
41
- }, [shouldTriggerMenu]);
34
+ const shouldTriggerMenu = exploration.mode === "none";
35
+
36
+ const handleGeomClick = useCallback(
37
+ (values: any, e: any) => {
38
+ if (shouldTriggerMenu) {
39
+ e.stopPropagation();
40
+ runInAction(() => {
41
+ commonStore.showEmbededMenu([e.pageX, e.pageY]);
42
+ commonStore.setFilters(values);
43
+ });
44
+ }
45
+ },
46
+ [shouldTriggerMenu]
47
+ );
42
48
 
43
49
  // apply filters
44
50
  const { dataSource } = currentDataset;
@@ -54,14 +60,14 @@ const ReactiveRenderer = forwardRef<IReactVegaHandler, {}>(function ReactiveRend
54
60
  const p = filters.length === 0 ? Promise.resolve(dataSource) : applyFilter(dataSource, filters);
55
61
  pendingPromiseRef.current = p;
56
62
 
57
- p.then(d => {
63
+ p.then((d) => {
58
64
  if (p !== pendingPromiseRef.current) {
59
65
  // This promise is out-of-date
60
66
  return;
61
67
  }
62
68
 
63
69
  setData(d);
64
- }).catch(err => {
70
+ }).catch((err) => {
65
71
  console.error(err);
66
72
  });
67
73
 
@@ -69,44 +75,64 @@ const ReactiveRenderer = forwardRef<IReactVegaHandler, {}>(function ReactiveRend
69
75
  pendingPromiseRef.current = null;
70
76
  };
71
77
  }, [dataSource, filters]);
72
-
73
- return <Resizable className={(size.mode === 'fixed' && !hasFacet) ? "border-blue-400 border-2 overflow-hidden" : ""}
74
- style={{ padding: '12px' }}
75
- onResizeStop={(e, direction, ref, d) => {
76
- vizStore.setChartLayout({
77
- mode: 'fixed',
78
- width: size.width + d.width,
79
- height: size.height + d.height
80
- })
81
- }}
82
- size={{
83
- width: size.width + 'px',
84
- height: size.height + 'px',
85
- }}>
86
- <ReactVega
87
- layoutMode={size.mode}
88
- interactiveScale={interactiveScale}
89
- geomType={geoms[0]}
90
- defaultAggregate={defaultAggregated}
91
- stack={stack}
92
- dataSource={data}
93
- rows={rows}
94
- columns={columns}
95
- color={color[0]}
96
- theta={theta[0]}
97
- radius={radius[0]}
98
- shape={shape[0]}
99
- opacity={opacity[0]}
100
- size={sizeChannel[0]}
101
- showActions={showActions}
102
- width={size.width - 12 * 4}
103
- height={size.height - 12 * 4}
104
- ref={ref}
105
- brushEncoding={exploration.mode === 'brush' ? exploration.brushDirection : 'none'}
106
- selectEncoding={exploration.mode === 'point' ? 'default' : 'none'}
107
- onGeomClick={handleGeomClick}
108
- />
109
- </Resizable>
78
+ const enableResize = size.mode === "fixed" && !hasFacet;
79
+ return (
80
+ <Resizable
81
+ className={enableResize ? "border-blue-400 border-2 overflow-hidden" : ""}
82
+ style={{ padding: "12px" }}
83
+ onResizeStop={(e, direction, ref, d) => {
84
+ vizStore.setChartLayout({
85
+ mode: "fixed",
86
+ width: size.width + d.width,
87
+ height: size.height + d.height,
88
+ });
89
+ }}
90
+ enable={
91
+ enableResize
92
+ ? undefined
93
+ : {
94
+ top: false,
95
+ right: false,
96
+ bottom: false,
97
+ left: false,
98
+ topRight: false,
99
+ bottomRight: false,
100
+ bottomLeft: false,
101
+ topLeft: false,
102
+ }
103
+ }
104
+ size={{
105
+ width: size.width + "px",
106
+ height: size.height + "px",
107
+ }}
108
+ >
109
+ <ReactVega
110
+ layoutMode={size.mode}
111
+ interactiveScale={interactiveScale}
112
+ geomType={geoms[0]}
113
+ defaultAggregate={defaultAggregated}
114
+ stack={stack}
115
+ dataSource={data}
116
+ rows={rows}
117
+ columns={columns}
118
+ color={color[0]}
119
+ theta={theta[0]}
120
+ radius={radius[0]}
121
+ shape={shape[0]}
122
+ opacity={opacity[0]}
123
+ size={sizeChannel[0]}
124
+ showActions={showActions}
125
+ width={size.width - 12 * 4}
126
+ height={size.height - 12 * 4}
127
+ ref={ref}
128
+ brushEncoding={exploration.mode === "brush" ? exploration.brushDirection : "none"}
129
+ selectEncoding={exploration.mode === "point" ? "default" : "none"}
130
+ onGeomClick={handleGeomClick}
131
+ themeKey={themeKey}
132
+ dark={dark}
133
+ />
134
+ </Resizable>
135
+ );
110
136
  });
111
137
 
112
138
  export default observer(ReactiveRenderer);
@@ -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, {
@@ -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,31 @@
1
+ import { useEffect, useState } from "react";
2
+ import { IDarkMode } from "../interfaces";
3
+
4
+ export function currentMediaTheme(): "dark" | "light" {
5
+ if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
6
+ return "dark";
7
+ } else {
8
+ return "light";
9
+ }
10
+ }
11
+
12
+ export function useCurrentMediaTheme(mode: IDarkMode | undefined = 'media'): "dark" | "light" {
13
+ const [theme, setTheme] = useState<"dark" | "light">(mode === 'media' ? currentMediaTheme() : mode);
14
+
15
+ useEffect(() => {
16
+ if (mode === 'media') {
17
+ const mediaQuery = window.matchMedia?.("(prefers-color-scheme: dark)") as MediaQueryList | undefined;
18
+ const listener = (e: MediaQueryListEvent) => {
19
+ setTheme(e.matches ? "dark" : "light");
20
+ };
21
+ mediaQuery?.addEventListener("change", listener);
22
+ return () => {
23
+ mediaQuery?.removeEventListener("change", listener);
24
+ };
25
+ } else {
26
+ setTheme(mode);
27
+ }
28
+ }, [mode]);
29
+
30
+ return theme;
31
+ }
@@ -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
  }