@kanaries/graphic-walker 0.2.14 → 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 (52) hide show
  1. package/dist/App.d.ts +3 -2
  2. package/dist/components/callout.d.ts +2 -0
  3. package/dist/components/toolbar/components.d.ts +4 -1
  4. package/dist/components/toolbar/index.d.ts +2 -0
  5. package/dist/components/toolbar/toolbar-item.d.ts +3 -0
  6. package/dist/components/tooltip.d.ts +2 -0
  7. package/dist/fields/components.d.ts +0 -1
  8. package/dist/fields/filterField/filterEditDialog.d.ts +1 -1
  9. package/dist/graphic-walker.es.js +15103 -14997
  10. package/dist/graphic-walker.es.js.map +1 -1
  11. package/dist/graphic-walker.umd.js +134 -264
  12. package/dist/graphic-walker.umd.js.map +1 -1
  13. package/dist/interfaces.d.ts +2 -0
  14. package/dist/renderer/index.d.ts +6 -4
  15. package/dist/utils/media.d.ts +2 -1
  16. package/dist/vis/react-vega.d.ts +4 -2
  17. package/dist/vis/theme.d.ts +36 -20
  18. package/dist/visualSettings/index.d.ts +2 -1
  19. package/package.json +1 -1
  20. package/src/App.tsx +19 -14
  21. package/src/components/callout.tsx +9 -7
  22. package/src/components/clickMenu.tsx +1 -7
  23. package/src/components/modal.tsx +1 -15
  24. package/src/components/sizeSetting.tsx +2 -2
  25. package/src/components/tabs/editableTab.tsx +2 -2
  26. package/src/components/toolbar/components.tsx +8 -23
  27. package/src/components/toolbar/index.tsx +11 -4
  28. package/src/components/toolbar/toolbar-button.tsx +2 -1
  29. package/src/components/toolbar/toolbar-item.tsx +17 -12
  30. package/src/components/toolbar/toolbar-select-button.tsx +9 -13
  31. package/src/components/toolbar/toolbar-toggle-button.tsx +2 -1
  32. package/src/components/tooltip.tsx +10 -6
  33. package/src/dataSource/dataSelection/csvData.tsx +1 -1
  34. package/src/dataSource/index.tsx +2 -3
  35. package/src/fields/components.tsx +13 -50
  36. package/src/fields/datasetFields/index.tsx +3 -4
  37. package/src/fields/encodeFields/singleEncodeEditor.tsx +1 -1
  38. package/src/fields/filterField/filterEditDialog.tsx +63 -99
  39. package/src/fields/filterField/slider.tsx +1 -1
  40. package/src/insightBoard/mainBoard.tsx +9 -2
  41. package/src/interfaces.ts +4 -1
  42. package/src/locales/en-US.json +5 -2
  43. package/src/locales/i18n.ts +7 -0
  44. package/src/locales/ja-JP.json +195 -0
  45. package/src/locales/zh-CN.json +5 -2
  46. package/src/renderer/index.tsx +96 -71
  47. package/src/utils/media.ts +16 -11
  48. package/src/vis/react-vega.tsx +18 -3
  49. package/src/vis/theme.ts +23 -25
  50. package/src/visualSettings/index.tsx +12 -32
  51. package/dist/components/container.d.ts +0 -2
  52. package/src/components/container.tsx +0 -25
@@ -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
+ }
@@ -167,7 +167,9 @@
167
167
  "btn": {
168
168
  "select_all": "全部选中",
169
169
  "unselect_all": "全部取消",
170
- "reverse": "选择反向"
170
+ "reverse": "选择反向",
171
+ "confirm": "确认",
172
+ "cancel": "取消"
171
173
  }
172
174
  },
173
175
  "explain": {
@@ -187,6 +189,7 @@
187
189
  "actions": {
188
190
  "prev": "向前",
189
191
  "next": "向后",
190
- "drop_field": "拖拽字段至此"
192
+ "drop_field": "拖拽字段至此",
193
+ "confirm": "确认"
191
194
  }
192
195
  }
@@ -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, { themeKey?: 'vega' | 'g2' }>(function ReactiveRenderer ({ themeKey }, 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, { themeKey?: 'vega' | 'g2
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,45 +75,64 @@ const ReactiveRenderer = forwardRef<IReactVegaHandler, { themeKey?: 'vega' | 'g2
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
- themeKey={themeKey}
109
- />
110
- </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
+ );
111
136
  });
112
137
 
113
138
  export default observer(ReactiveRenderer);
@@ -1,4 +1,5 @@
1
1
  import { useEffect, useState } from "react";
2
+ import { IDarkMode } from "../interfaces";
2
3
 
3
4
  export function currentMediaTheme(): "dark" | "light" {
4
5
  if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
@@ -8,19 +9,23 @@ export function currentMediaTheme(): "dark" | "light" {
8
9
  }
9
10
  }
10
11
 
11
- export function useCurrentMediaTheme(): "dark" | "light" {
12
- const [theme, setTheme] = useState<"dark" | "light">(currentMediaTheme());
12
+ export function useCurrentMediaTheme(mode: IDarkMode | undefined = 'media'): "dark" | "light" {
13
+ const [theme, setTheme] = useState<"dark" | "light">(mode === 'media' ? currentMediaTheme() : mode);
13
14
 
14
15
  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
- }, []);
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]);
24
29
 
25
30
  return theme;
26
31
  }
@@ -8,7 +8,7 @@ import styled from 'styled-components';
8
8
  import { autoMark } from '../utils/autoMark';
9
9
  import { COUNT_FIELD_ID } from '../constants';
10
10
 
11
- import { IViewField, IRow, IStackMode } from '../interfaces';
11
+ import { IViewField, IRow, IStackMode, IDarkMode, IThemeKey } from '../interfaces';
12
12
  import { useTranslation } from 'react-i18next';
13
13
  import { getVegaTimeFormatRules } from './temporalFormat';
14
14
  import { builtInThemes } from './theme';
@@ -49,7 +49,8 @@ interface ReactVegaProps {
49
49
  selectEncoding: SingleViewProps['selectEncoding'];
50
50
  brushEncoding: SingleViewProps['brushEncoding'];
51
51
  /** @default "vega" */
52
- themeKey?: 'vega' | 'g2';
52
+ themeKey?: IThemeKey;
53
+ dark?: IDarkMode;
53
54
  }
54
55
  const NULL_FIELD: IViewField = {
55
56
  dragId: '',
@@ -104,6 +105,7 @@ interface SingleViewProps {
104
105
  asCrossFilterTrigger: boolean;
105
106
  selectEncoding: 'default' | 'none';
106
107
  brushEncoding: 'x' | 'y' | 'default' | 'none';
108
+ hideLegend?: boolean;
107
109
  }
108
110
 
109
111
  function availableChannels (geomType: string): Set<string> {
@@ -202,9 +204,16 @@ function getSingleView(props: SingleViewProps) {
202
204
  brushEncoding,
203
205
  enableCrossFilter,
204
206
  asCrossFilterTrigger,
207
+ hideLegend = false,
205
208
  } = props
206
209
  const fields: IViewField[] = [x, y, color, opacity, size, shape, row, column, xOffset, yOffset, theta, radius]
207
210
  let markType = geomType;
211
+ let config: any = {};
212
+ if (hideLegend) {
213
+ config.legend = {
214
+ disable: true
215
+ };
216
+ }
208
217
  if (geomType === 'auto') {
209
218
  const types: ISemanticType[] = [];
210
219
  if (x !== NULL_FIELD) types.push(x.semanticType)//types.push(getFieldType(x));
@@ -219,6 +228,7 @@ function getSingleView(props: SingleViewProps) {
219
228
  channelStack(encoding, stack);
220
229
  if (!enableCrossFilter || brushEncoding === 'none' && selectEncoding === 'none') {
221
230
  return {
231
+ config,
222
232
  mark: {
223
233
  type: markType,
224
234
  opacity: 0.96,
@@ -326,6 +336,7 @@ function getSingleView(props: SingleViewProps) {
326
336
 
327
337
  if (brushEncoding !== 'none') {
328
338
  return {
339
+ config,
329
340
  transform: asCrossFilterTrigger ? [] : [
330
341
  { filter: { param: BRUSH_SIGNAL_NAME } }
331
342
  ],
@@ -346,6 +357,7 @@ function getSingleView(props: SingleViewProps) {
346
357
  }
347
358
 
348
359
  return {
360
+ config,
349
361
  transform: asCrossFilterTrigger ? [] : [
350
362
  { filter: { param: POINT_SIGNAL_NAME } }
351
363
  ],
@@ -391,12 +403,13 @@ const ReactVega = forwardRef<IReactVegaHandler, ReactVegaProps>(function ReactVe
391
403
  selectEncoding,
392
404
  brushEncoding,
393
405
  themeKey = 'vega',
406
+ dark = 'media'
394
407
  } = props;
395
408
  // const container = useRef<HTMLDivElement>(null);
396
409
  // const containers = useRef<(HTMLDivElement | null)[]>([]);
397
410
  const [viewPlaceholders, setViewPlaceholders] = useState<React.MutableRefObject<HTMLDivElement>[]>([]);
398
411
  const { i18n } = useTranslation();
399
- const mediaTheme = useCurrentMediaTheme();
412
+ const mediaTheme = useCurrentMediaTheme(dark);
400
413
  const themeConfig = builtInThemes[themeKey]?.[mediaTheme];
401
414
  useEffect(() => {
402
415
  const clickSub = geomClick$.subscribe(([values, e]) => {
@@ -553,6 +566,7 @@ const ReactVega = forwardRef<IReactVegaHandler, ReactVegaProps>(function ReactVe
553
566
  for (let i = 0; i < rowRepeatFields.length; i++) {
554
567
  for (let j = 0; j < colRepeatFields.length; j++, index++) {
555
568
  const sourceId = index;
569
+ const hasLegend = i === 0 && j === colRepeatFields.length - 1;
556
570
  const singleView = getSingleView({
557
571
  x: colRepeatFields[j] || NULL_FIELD,
558
572
  y: rowRepeatFields[i] || NULL_FIELD,
@@ -573,6 +587,7 @@ const ReactVega = forwardRef<IReactVegaHandler, ReactVegaProps>(function ReactVe
573
587
  brushEncoding,
574
588
  enableCrossFilter: crossFilterTriggerIdx !== -1,
575
589
  asCrossFilterTrigger: crossFilterTriggerIdx === sourceId,
590
+ hideLegend: !hasLegend,
576
591
  });
577
592
  const node = i * colRepeatFields.length + j < viewPlaceholders.length ? viewPlaceholders[i * colRepeatFields.length + j].current : null
578
593
  let commonSpec = { ...spec };
package/src/vis/theme.ts CHANGED
@@ -1,22 +1,30 @@
1
1
  const DEFAULT_COLOR = "#5B8FF9";
2
-
2
+ const DARK_COMMON_DESIGN = {
3
+ background: "transparent",
4
+ header: {
5
+ titleColor: "#d1d5db", // change title color to white
6
+ labelColor: "#d1d5db", // change label color to white
7
+ },
8
+ axis: {
9
+ gridColor: "#666",
10
+ domainColor: "#d1d5db", // change axis color to white
11
+ tickColor: "#d1d5db", // change tick color to white
12
+ labelColor: "#d1d5db", // change label color to white
13
+ titleColor: "#d1d5db", // change title color to white
14
+ },
15
+ legend: {
16
+ labelColor: "#d1d5db", // change legend label color to white
17
+ titleColor: "#d1d5db" // change legend title color to white
18
+ },
19
+ view: {
20
+ stroke: '#666'
21
+ }
22
+ }
3
23
  export const VegaTheme = {
4
24
  light: {
5
25
  background: "transparent",
6
26
  },
7
- dark: {
8
- background: "transparent",
9
- axis: {
10
- gridColor: "#666",
11
- domainColor: "#d1d5db", // change axis color to white
12
- tickColor: "#d1d5db", // change tick color to white
13
- labelColor: "#d1d5db", // change label color to white
14
- },
15
- legend: {
16
- labelColor: "#d1d5db", // change legend label color to white
17
- titleColor: "#d1d5db" // change legend title color to white
18
- },
19
- },
27
+ dark: DARK_COMMON_DESIGN
20
28
  } as const;
21
29
 
22
30
  export const AntVTheme = {
@@ -65,6 +73,7 @@ export const AntVTheme = {
65
73
  },
66
74
  },
67
75
  dark: {
76
+ ...DARK_COMMON_DESIGN,
68
77
  area: { fill: DEFAULT_COLOR },
69
78
  bar: { fill: DEFAULT_COLOR },
70
79
  circle: { fill: DEFAULT_COLOR },
@@ -76,17 +85,6 @@ export const AntVTheme = {
76
85
  errorbar: { stroke: DEFAULT_COLOR },
77
86
  errorband: { fill: DEFAULT_COLOR },
78
87
  arc: { fill: DEFAULT_COLOR },
79
- background: "transparent", // change background color to dark gray
80
- axis: {
81
- gridColor: "#666",
82
- domainColor: "#d1d5db", // change axis color to white
83
- tickColor: "#d1d5db", // change tick color to white
84
- labelColor: "#d1d5db", // change label color to white
85
- },
86
- legend: {
87
- labelColor: "#d1d5db", // change legend label color to white
88
- titleColor: "#d1d5db" // change legend title color to white
89
- },
90
88
  range: {
91
89
  category: [
92
90
  "#5B8FF9",