@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
@@ -4,6 +4,7 @@ import produce from "immer";
4
4
  import { IToolbarItem, IToolbarProps, ToolbarItemContainer } from "./toolbar-item";
5
5
  import { ToolbarContainer, useHandlers, ToolbarItemContainerElement } from "./components";
6
6
  import Callout from "../callout";
7
+ import { useCurrentMediaTheme } from "../../utils/media";
7
8
 
8
9
 
9
10
  const OptionGroup = styled(ToolbarContainer)`
@@ -21,16 +22,7 @@ const OptionGroup = styled(ToolbarContainer)`
21
22
  --dark-mode-color: #aaa;
22
23
  --dark-mode-color-hover: #ccc;
23
24
  --dark-mode-blue: #282958;
24
- background-color: var(--background-color);
25
- // dark mode
26
- @media (prefers-color-scheme: dark) {
27
- /* --dark-mode-background-color: #1f1f1f;
28
- --dark-mode-background-color-hover: #2f2f2f;
29
- --dark-mode-color: #aaa;
30
- --dark-mode-color-hover: #ccc;
31
- --dark-mode-blue: #282958; */
32
- background-color: var(--dark-mode-background-color);
33
- }
25
+ background-color: ${({ dark }) => dark ? 'var(--dark-mode-background-color)' : 'var(--background-color)'};
34
26
  `;
35
27
 
36
28
  const Option = styled(ToolbarItemContainerElement)`
@@ -85,7 +77,7 @@ export interface ToolbarSelectButtonItem<T extends string = string> extends IToo
85
77
  }
86
78
 
87
79
  const ToolbarSelectButton = memo<IToolbarProps<ToolbarSelectButtonItem>>(function ToolbarSelectButton(props) {
88
- const { item, styles, openedKey, setOpenedKey } = props;
80
+ const { darkModePreference, item, styles, openedKey, setOpenedKey } = props;
89
81
  const { key, icon: Icon, disabled, options, value, onSelect } = item;
90
82
  const id = `${key}::button`;
91
83
 
@@ -134,9 +126,12 @@ const ToolbarSelectButton = memo<IToolbarProps<ToolbarSelectButtonItem>>(functio
134
126
  ...item.styles?.icon,
135
127
  };
136
128
 
129
+ const dark = useCurrentMediaTheme(darkModePreference) === 'dark';
130
+
137
131
  return (
138
132
  <>
139
133
  <ToolbarItemContainer
134
+ darkModePreference={darkModePreference}
140
135
  props={produce(props, draft => {
141
136
  if (currentOption) {
142
137
  draft.item.label = `${draft.item.label}: ${currentOption.label}`;
@@ -163,8 +158,8 @@ const ToolbarSelectButton = memo<IToolbarProps<ToolbarSelectButtonItem>>(functio
163
158
  <TriggerFlag aria-hidden id={id} />
164
159
  </ToolbarItemContainer>
165
160
  {opened && (
166
- <Callout target={`#${id}`}>
167
- <OptionGroup role="listbox" aria-activedescendant={`${id}::${value}`} aria-describedby={id} aria-disabled={disabled} onMouseDown={e => e.stopPropagation()}>
161
+ <Callout target={`#${id}`} darkModePreference={darkModePreference}>
162
+ <OptionGroup dark={dark} role="listbox" aria-activedescendant={`${id}::${value}`} aria-describedby={id} aria-disabled={disabled} onMouseDown={e => e.stopPropagation()}>
168
163
  {options.map((option, idx, arr) => {
169
164
  const selected = option.key === value;
170
165
  const OptionIcon = option.icon;
@@ -173,6 +168,7 @@ const ToolbarSelectButton = memo<IToolbarProps<ToolbarSelectButtonItem>>(functio
173
168
  const next = arr[(idx + 1) % arr.length];
174
169
  return (
175
170
  <Option
171
+ dark={dark}
176
172
  key={option.key}
177
173
  id={optionId}
178
174
  role="option"
@@ -46,7 +46,7 @@ export interface ToolbarToggleButtonItem extends IToolbarItem {
46
46
  }
47
47
 
48
48
  const ToolbarToggleButton = memo<IToolbarProps<ToolbarToggleButtonItem>>(function ToolbarToggleButton(props) {
49
- const { item, styles } = props;
49
+ const { item, styles, darkModePreference } = props;
50
50
  const { icon: Icon, label, disabled, checked, onChange } = item;
51
51
  const handlers = useHandlers(() => onChange(!checked), disabled ?? false);
52
52
 
@@ -62,6 +62,7 @@ const ToolbarToggleButton = memo<IToolbarProps<ToolbarToggleButtonItem>>(functio
62
62
  handlers={handlers}
63
63
  role="checkbox"
64
64
  aria-checked={checked}
65
+ darkModePreference={darkModePreference}
65
66
  >
66
67
  <ToggleContainer checked={checked}>
67
68
  <Icon style={mergedIconStyles} />
@@ -1,7 +1,9 @@
1
1
  import React, { memo, useContext, useEffect, useMemo, useRef, useState } from "react";
2
2
  import { createPortal } from "react-dom";
3
3
  import styled from "styled-components";
4
+ import type { IDarkMode } from "../interfaces";
4
5
  import { ShadowDomContext } from "..";
6
+ import { useCurrentMediaTheme } from "../utils/media";
5
7
 
6
8
  export interface TooltipProps {
7
9
  children: JSX.Element;
@@ -12,12 +14,13 @@ export interface TooltipProps {
12
14
  hideDelay?: number;
13
15
  /** @default 3_000 */
14
16
  autoHide?: number;
17
+ darkModePreference: IDarkMode;
15
18
  }
16
19
 
17
20
  const attrName = "data-tooltip-host-id";
18
21
  let flag = 0;
19
22
 
20
- const Bubble = styled.div`
23
+ const Bubble = styled.div<{ dark: boolean }>`
21
24
  border-radius: 1px;
22
25
  transform: translate(-50%, -100%);
23
26
  filter: drop-shadow(0 1.6px 1.2px rgba(0, 0, 0, 0.15)) drop-shadow(0 -1px 1px rgba(0, 0, 0, 0.12));
@@ -31,10 +34,7 @@ const Bubble = styled.div`
31
34
  width: 8px;
32
35
  height: 8px;
33
36
  transform: translate(-50%, 50%) rotate(45deg);
34
- background-color: #fff;
35
- @media (prefers-color-scheme: dark) {
36
- background-color: #000;
37
- }
37
+ background-color: ${({ dark }) => dark ? '#000' : '#fff'};
38
38
  border-radius: 1px;
39
39
  }
40
40
  `;
@@ -45,6 +45,7 @@ const Tooltip = memo<TooltipProps>(function Tooltip({
45
45
  autoHide = 3_000,
46
46
  showDelay = 250,
47
47
  hideDelay = 250,
48
+ darkModePreference = 'media',
48
49
  }) {
49
50
  const hostId = useMemo(() => flag++, []);
50
51
  const [pos, setPos] = useState<[number, number]>([0, 0]);
@@ -121,6 +122,8 @@ const Tooltip = memo<TooltipProps>(function Tooltip({
121
122
  }
122
123
  }, [root, hostId]);
123
124
 
125
+ const darkMode = useCurrentMediaTheme(darkModePreference);
126
+
124
127
  return (
125
128
  <>
126
129
  {element}
@@ -128,7 +131,8 @@ const Tooltip = memo<TooltipProps>(function Tooltip({
128
131
  root &&
129
132
  createPortal(
130
133
  <Bubble
131
- className="fixed text-xs p-1 px-3 text-gray-500 bg-white dark:bg-zinc-900 z-50"
134
+ className={`${darkMode === 'dark' ? 'dark bg-zinc-900' : 'bg-white'} fixed text-xs p-1 px-3 text-gray-500 z-50`}
135
+ dark={darkMode === 'dark'}
132
136
  onMouseOver={() => setHover(true)}
133
137
  onMouseOut={() => setHover(false)}
134
138
  style={{ left: pos[0], top: pos[1] - 4 }}
@@ -107,7 +107,7 @@ const CSVData: React.FC<ICSVData> = (props) => {
107
107
  onChange={(e) => {
108
108
  commonStore.updateTempName(e.target.value);
109
109
  }}
110
- className="text-xs mr-2 p-2 rounded border border-gray-200 dark:border-gray-800 outline-none focus:outline-none focus:border-blue-500 placeholder:italic placeholder:text-slate-400 dark:bg-stone-900"
110
+ className="text-xs mr-2 p-2 rounded border border-gray-200 dark:border-gray-700 outline-none focus:outline-none focus:border-blue-500 placeholder:italic placeholder:text-slate-400 dark:bg-stone-900"
111
111
  />
112
112
  <PrimaryButton
113
113
  className="mr-2"
@@ -2,7 +2,6 @@ import React, { useRef } from "react";
2
2
  import { observer } from "mobx-react-lite";
3
3
  import { CheckCircleIcon, ArrowPathIcon } from "@heroicons/react/24/outline";
4
4
  import { useTranslation } from "react-i18next";
5
- import { Container } from "../components/container";
6
5
  import Modal from "../components/modal";
7
6
  import { useGlobalStore } from "../store";
8
7
  import { download } from "../utils/save";
@@ -25,7 +24,7 @@ const DataSourceSegment: React.FC<DSSegmentProps> = (props) => {
25
24
  const { currentDataset, datasets, showDSPanel } = commonStore;
26
25
 
27
26
  return (
28
- <Container className="flex items-center">
27
+ <div className="flex items-center m-4 p-4 border border-gray-200 dark:border-gray-700">
29
28
  <GwFile fileRef={gwFileRef} />
30
29
  {!preWorkDone && (
31
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>
@@ -89,7 +88,7 @@ const DataSourceSegment: React.FC<DSSegmentProps> = (props) => {
89
88
  )} */}
90
89
  {preWorkDone && <CheckCircleIcon className="text-green-500 w-5 inline-block ml-2" />}
91
90
  {!preWorkDone && <ArrowPathIcon className="text-yellow-500 w-5 inline-block ml-2" />}
92
- </Container>
91
+ </div>
93
92
  );
94
93
  };
95
94
 
@@ -3,38 +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
- // dark mode
9
- @media (prefers-color-scheme: dark) {
10
- border: 1px solid #2d3748;
11
- }
12
- font-size: 12px;
13
- margin: 0.2em;
14
-
15
- .aes-header{
16
- border-bottom: 1px solid #e5e7eb;
17
- @media (prefers-color-scheme: dark) {
18
- border-bottom: 1px solid #2d3748;
19
- }
20
- padding: 0.6em;
21
- h4 {
22
- font-weight: 400;
23
- }
24
- }
25
- .aes-container{
26
- /* overflow-x: auto; */
27
- }
28
-
29
- `
30
-
31
6
  export const FieldListContainer: React.FC<{ name: string }> = (props) => {
32
7
  const { t } = useTranslation('translation', { keyPrefix: 'constant.draggable_key' });
33
8
 
34
9
  return (
35
- <FieldListSegment>
36
- <div className="fl-header">
37
- <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>
38
13
  </div>
39
14
  <div className="fl-container">{props.children}</div>
40
15
  </FieldListSegment>
@@ -45,12 +20,12 @@ export const AestheticFieldContainer: React.FC<{ name: string }> = props => {
45
20
  const { t } = useTranslation('translation', { keyPrefix: 'constant.draggable_key' });
46
21
 
47
22
  return (
48
- <AestheticSegment>
49
- <div className="aes-header cursor-default select-none">
50
- <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>
51
26
  </div>
52
- <div className="aes-container">{props.children}</div>
53
- </AestheticSegment>
27
+ <div>{props.children}</div>
28
+ </div>
54
29
  );
55
30
  }
56
31
 
@@ -58,12 +33,12 @@ export const FilterFieldContainer: React.FC = props => {
58
33
  const { t } = useTranslation('translation', { keyPrefix: 'constant.draggable_key' });
59
34
 
60
35
  return (
61
- <FilterFieldSegment>
62
- <div className="flt-header cursor-default select-none">
63
- <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>
64
39
  </div>
65
- <div className="flt-container">{props.children}</div>
66
- </FilterFieldSegment>
40
+ <div>{props.children}</div>
41
+ </div>
67
42
  );
68
43
  }
69
44
 
@@ -91,19 +66,11 @@ export const FilterFieldsContainer = styled.div({
91
66
 
92
67
  export const FieldListSegment = styled.div`
93
68
  display: flex;
94
- border: 1px solid #e5e7eb;
95
- @media (prefers-color-scheme: dark) {
96
- border: 1px solid #2d3748;
97
- }
98
69
  margin: 0.2em;
99
70
  font-size: 12px;
100
71
  div.fl-header {
101
72
  /* flex-basis: 100px; */
102
73
  width: 100px;
103
- border-right: 1px solid #e5e7eb;
104
- @media (prefers-color-scheme: dark) {
105
- border-right: 1px solid #2d3748;
106
- }
107
74
  flex-shrink: 0;
108
75
  h4 {
109
76
  margin: 0.6em;
@@ -112,10 +79,6 @@ export const FieldListSegment = styled.div`
112
79
  }
113
80
  div.fl-container {
114
81
  flex-grow: 10;
115
- /* display: flex;
116
- flex-wrap: wrap; */
117
- /* overflow-x: auto;
118
- overflow-y: hidden; */
119
82
  }
120
83
  `;
121
84
 
@@ -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(NestContainer)`
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 dark:border-gray-700 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 dark:border-gray-800 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>
@@ -48,7 +48,7 @@ const SingleEncodeEditor: React.FC<SingleEncodeEditorProps> = (props) => {
48
48
  >
49
49
  <TrashIcon className="w-4" />
50
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">
51
+ <div className="flex-1 flex items-center border border-gray-200 dark:border-gray-700 border-l-0 px-2 space-x-2 truncate">
52
52
  <span className="flex-1 truncate">
53
53
  {channelItem.name}
54
54
  </span>
@@ -1,64 +1,29 @@
1
- import { CheckCircleIcon } from '@heroicons/react/24/outline';
2
- import { observer } from 'mobx-react-lite';
3
- import React from 'react';
4
- import { useTranslation } from 'react-i18next';
5
-
6
- import Modal from '../../components/modal';
7
- import type { IFilterField, IFilterRule } from '../../interfaces';
8
- import { useGlobalStore } from '../../store';
9
- import Tabs, { RuleFormProps } from './tabs';
10
-
11
-
12
- const QuantitativeRuleForm: React.FC<RuleFormProps> = ({
13
- field,
14
- onChange,
15
- }) => {
16
- return (
17
- <Tabs
18
- field={field}
19
- onChange={onChange}
20
- tabs={['range', 'one of']}
21
- />
22
- );
1
+ import { CheckCircleIcon } from "@heroicons/react/24/outline";
2
+ import { observer } from "mobx-react-lite";
3
+ import React from "react";
4
+ import { useTranslation } from "react-i18next";
5
+
6
+ import Modal from "../../components/modal";
7
+ import type { IFilterField, IFilterRule } from "../../interfaces";
8
+ import { useGlobalStore } from "../../store";
9
+ import Tabs, { RuleFormProps } from "./tabs";
10
+ import DefaultButton from "../../components/button/default";
11
+ import PrimaryButton from "../../components/button/primary";
12
+
13
+ const QuantitativeRuleForm: React.FC<RuleFormProps> = ({ field, onChange }) => {
14
+ return <Tabs field={field} onChange={onChange} tabs={["range", "one of"]} />;
23
15
  };
24
16
 
25
- const NominalRuleForm: React.FC<RuleFormProps> = ({
26
- field,
27
- onChange,
28
- }) => {
29
- return (
30
- <Tabs
31
- field={field}
32
- onChange={onChange}
33
- tabs={['one of']}
34
- />
35
- );
17
+ const NominalRuleForm: React.FC<RuleFormProps> = ({ field, onChange }) => {
18
+ return <Tabs field={field} onChange={onChange} tabs={["one of"]} />;
36
19
  };
37
20
 
38
- const OrdinalRuleForm: React.FC<RuleFormProps> = ({
39
- field,
40
- onChange,
41
- }) => {
42
- return (
43
- <Tabs
44
- field={field}
45
- onChange={onChange}
46
- tabs={['range', 'one of']}
47
- />
48
- );
21
+ const OrdinalRuleForm: React.FC<RuleFormProps> = ({ field, onChange }) => {
22
+ return <Tabs field={field} onChange={onChange} tabs={["range", "one of"]} />;
49
23
  };
50
24
 
51
- const TemporalRuleForm: React.FC<RuleFormProps> = ({
52
- field,
53
- onChange,
54
- }) => {
55
- return (
56
- <Tabs
57
- field={field}
58
- onChange={onChange}
59
- tabs={['one of', 'temporal range']}
60
- />
61
- );
25
+ const TemporalRuleForm: React.FC<RuleFormProps> = ({ field, onChange }) => {
26
+ return <Tabs field={field} onChange={onChange} tabs={["one of", "temporal range"]} />;
62
27
  };
63
28
 
64
29
  const EmptyForm: React.FC<RuleFormProps> = () => <React.Fragment />;
@@ -67,7 +32,7 @@ const FilterEditDialog: React.FC = observer(() => {
67
32
  const { vizStore } = useGlobalStore();
68
33
  const { editingFilterIdx, draggableFieldState } = vizStore;
69
34
 
70
- const { t } = useTranslation('translation', { keyPrefix: 'filters' });
35
+ const { t } = useTranslation("translation", { keyPrefix: "filters" });
71
36
 
72
37
  const field = React.useMemo(() => {
73
38
  return editingFilterIdx !== null ? draggableFieldState.filters[editingFilterIdx] : null;
@@ -83,14 +48,20 @@ const FilterEditDialog: React.FC = observer(() => {
83
48
  }
84
49
  }, [field]);
85
50
 
86
- const handleChange = React.useCallback((r: IFilterRule) => {
87
- if (editingFilterIdx !== null) {
88
- setUncontrolledField(uf => ({
89
- ...uf,
90
- rule: r,
91
- }) as IFilterField);
92
- }
93
- }, [editingFilterIdx]);
51
+ const handleChange = React.useCallback(
52
+ (r: IFilterRule) => {
53
+ if (editingFilterIdx !== null) {
54
+ setUncontrolledField(
55
+ (uf) =>
56
+ ({
57
+ ...uf,
58
+ rule: r,
59
+ } as IFilterField)
60
+ );
61
+ }
62
+ },
63
+ [editingFilterIdx]
64
+ );
94
65
 
95
66
  const handleSubmit = React.useCallback(() => {
96
67
  if (editingFilterIdx !== null) {
@@ -100,45 +71,38 @@ const FilterEditDialog: React.FC = observer(() => {
100
71
  vizStore.closeFilterEditing();
101
72
  }, [editingFilterIdx, uncontrolledField]);
102
73
 
103
- const Form = field ? ({
104
- quantitative: QuantitativeRuleForm,
105
- nominal: NominalRuleForm,
106
- ordinal: OrdinalRuleForm,
107
- temporal: TemporalRuleForm,
108
- }[field.semanticType] as React.FC<RuleFormProps>) : EmptyForm;
109
-
74
+ const Form = field
75
+ ? ({
76
+ quantitative: QuantitativeRuleForm,
77
+ nominal: NominalRuleForm,
78
+ ordinal: OrdinalRuleForm,
79
+ temporal: TemporalRuleForm,
80
+ }[field.semanticType] as React.FC<RuleFormProps>)
81
+ : EmptyForm;
82
+
110
83
  return uncontrolledField ? (
111
- <Modal
112
- show={Boolean(uncontrolledField)}
113
- title={t('editing')}
114
- onClose={() => vizStore.closeFilterEditing()}
115
- >
116
- <header className="text-lg font-semibold py-2 outline-none">
117
- {t('form.name')}
118
- </header>
119
- <input className="border py-1 px-4 bg-white text-gray-800" readOnly value={uncontrolledField.name}/>
120
- <header className="text-lg font-semibold py-2 outline-none">
121
- {t('form.rule')}
122
- </header>
123
- <Form
124
- field={uncontrolledField}
125
- onChange={handleChange}
126
- />
127
- <div className="flex justify-center text-green-500 mt-4">
128
- <CheckCircleIcon
129
- width="3em"
130
- height="3em"
131
- role="button"
132
- tabIndex={0}
133
- aria-label="ok"
134
- className="cursor-pointer hover:bg-green-50 p-1"
135
- onClick={handleSubmit}
136
- strokeWidth="1.5"
137
- />
84
+ <Modal show={Boolean(uncontrolledField)} title={t("editing")} onClose={() => vizStore.closeFilterEditing()}>
85
+ <div className="p-4">
86
+ <h2 className="text-base font-semibold py-2 outline-none">{t("form.name")}</h2>
87
+ <span className="inline-flex items-center rounded-full bg-indigo-100 px-3 py-0.5 text-sm font-medium text-indigo-800">
88
+ {uncontrolledField.name}
89
+ </span>
90
+ <h3 className="text-base font-semibold py-2 outline-none">{t("form.rule")}</h3>
91
+ <Form field={uncontrolledField} onChange={handleChange} />
92
+ <div className="mt-4">
93
+ <PrimaryButton
94
+ onClick={handleSubmit}
95
+ text={t("btn.confirm")}
96
+ />
97
+ <DefaultButton
98
+ className="ml-2"
99
+ onClick={() => vizStore.closeFilterEditing()}
100
+ text={t("btn.cancel")}
101
+ />
102
+ </div>
138
103
  </div>
139
104
  </Modal>
140
105
  ) : null;
141
106
  });
142
107
 
143
-
144
108
  export default FilterEditDialog;
@@ -118,7 +118,6 @@ const Slider: React.FC<SliderProps> = React.memo(function Slider ({
118
118
  };
119
119
 
120
120
  const dragHandler = fromEvent(document.body, 'mousemove').pipe(
121
- throttleTime(100),
122
121
  map(ev => {
123
122
  if (!trackRef.current || !dragging) {
124
123
  return null;
@@ -142,6 +141,7 @@ const Slider: React.FC<SliderProps> = React.memo(function Slider ({
142
141
 
143
142
  return pos;
144
143
  }),
144
+ throttleTime(100),
145
145
  filter(pos => {
146
146
  return pos !== null && pos !== range[dragging === 'left' ? 0 : 1];
147
147
  }),
@@ -9,6 +9,8 @@ import { baseVis, IReasonType } from "./std2vegaSpec";
9
9
  import RadioGroupButtons from "./radioGroupButtons";
10
10
  import { formatFieldName, mergeMeasures } from "./utils";
11
11
  import { useTranslation } from "react-i18next";
12
+ import { useCurrentMediaTheme } from "../utils/media";
13
+ import { builtInThemes } from "../vis/theme";
12
14
 
13
15
  const collection = Insight.IntentionWorkerCollection.init();
14
16
 
@@ -41,6 +43,8 @@ const InsightMainBoard: React.FC<InsightMainBoardProps> = (props) => {
41
43
  const [valueExp, setValueExp] = useState<IMeasureWithStat[]>([]);
42
44
  const { t } = useTranslation();
43
45
  const container = useRef<HTMLDivElement>(null);
46
+ const mediaTheme = useCurrentMediaTheme();
47
+ const themeConfig = builtInThemes['g2'][mediaTheme];
44
48
 
45
49
  const dimsWithTypes = useMemo(() => {
46
50
  const dimensions = fields
@@ -111,10 +115,13 @@ const InsightMainBoard: React.FC<InsightMainBoardProps> = (props) => {
111
115
  true
112
116
  );
113
117
  if (container.current) {
114
- embed(container.current, _vegaSpec);
118
+ embed(container.current, _vegaSpec, {
119
+ actions: false,
120
+ config: themeConfig
121
+ });
115
122
  }
116
123
  }
117
- }, [visIndex, recSpaces, visSpaces, fields, dataSource]);
124
+ }, [visIndex, recSpaces, visSpaces, fields, dataSource, themeConfig]);
118
125
 
119
126
  const FilterDesc = useMemo<React.ReactElement[]>(() => {
120
127
  if (filters) {
package/src/interfaces.ts CHANGED
@@ -184,4 +184,7 @@ export interface IVisSpec {
184
184
  export enum ISegmentKey {
185
185
  vis = 'vis',
186
186
  data = 'data'
187
- }
187
+ }
188
+
189
+ export type IThemeKey = 'vega' | 'g2';
190
+ export type IDarkMode = 'media' | 'light' | 'dark';
@@ -167,7 +167,9 @@
167
167
  "btn": {
168
168
  "select_all": "Select All",
169
169
  "unselect_all": "Unselect All",
170
- "reverse": "Reverse Selection"
170
+ "reverse": "Reverse Selection",
171
+ "confirm": "Confirm",
172
+ "cancel": "Cancel"
171
173
  }
172
174
  },
173
175
  "explain": {
@@ -187,6 +189,7 @@
187
189
  "actions": {
188
190
  "prev": "Previous",
189
191
  "next": "Next",
190
- "drop_field": "Drop Field Here"
192
+ "drop_field": "Drop Field Here",
193
+ "confirm": "Confirm"
191
194
  }
192
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({