@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
@@ -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 truncate">
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);
@@ -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,44 +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
- title={t('editing')}
113
- onClose={() => vizStore.closeFilterEditing()}
114
- >
115
- <header className="text-lg font-semibold py-2 outline-none">
116
- {t('form.name')}
117
- </header>
118
- <input className="border py-1 px-4" readOnly value={uncontrolledField.name}/>
119
- <header className="text-lg font-semibold py-2 outline-none">
120
- {t('form.rule')}
121
- </header>
122
- <Form
123
- field={uncontrolledField}
124
- onChange={handleChange}
125
- />
126
- <div className="flex justify-center text-green-500 mt-4">
127
- <CheckCircleIcon
128
- width="3em"
129
- height="3em"
130
- role="button"
131
- tabIndex={0}
132
- aria-label="ok"
133
- className="cursor-pointer hover:bg-green-50 p-1"
134
- onClick={handleSubmit}
135
- strokeWidth="1.5"
136
- />
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>
137
103
  </div>
138
104
  </Modal>
139
105
  ) : null;
140
106
  });
141
107
 
142
-
143
108
  export default FilterEditDialog;
@@ -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')}
@@ -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
  }),
@@ -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,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) {
@@ -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
 
@@ -183,4 +184,7 @@ export interface IVisSpec {
183
184
  export enum ISegmentKey {
184
185
  vis = 'vis',
185
186
  data = 'data'
186
- }
187
+ }
188
+
189
+ export type IThemeKey = 'vega' | 'g2';
190
+ export type IDarkMode = 'media' | 'light' | 'dark';
@@ -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;