@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.
- package/dist/App.d.ts +6 -3
- package/dist/assets/explainer.worker-8428eb12.js.map +1 -1
- package/dist/components/button/base.d.ts +1 -0
- package/dist/components/button/defaultMini.d.ts +4 -0
- package/dist/components/button/primaryMini.d.ts +4 -0
- package/dist/components/callout.d.ts +2 -0
- package/dist/components/dropdownContext/index.d.ts +13 -0
- package/dist/components/dropdownSelect/index.d.ts +17 -0
- package/dist/components/modal.d.ts +1 -0
- package/dist/components/toolbar/components.d.ts +4 -1
- package/dist/components/toolbar/index.d.ts +2 -0
- package/dist/components/toolbar/toolbar-item.d.ts +4 -0
- package/dist/components/tooltip.d.ts +2 -0
- package/dist/dataSource/dataSelection/config.d.ts +2 -0
- package/dist/dataSource/index.d.ts +1 -1
- package/dist/fields/components.d.ts +0 -1
- package/dist/fields/datasetFields/dimFields.d.ts +2 -2
- package/dist/fields/datasetFields/meaFields.d.ts +2 -2
- package/dist/fields/encodeFields/singleEncodeDropDown.d.ts +17 -0
- package/dist/fields/encodeFields/singleEncodeEditor.d.ts +11 -0
- package/dist/fields/filterField/filterEditDialog.d.ts +1 -1
- package/dist/fields/obComponents/obPill.d.ts +3 -3
- package/dist/graphic-walker.es.js +21205 -19397
- package/dist/graphic-walker.es.js.map +1 -1
- package/dist/graphic-walker.umd.js +181 -236
- package/dist/graphic-walker.umd.js.map +1 -1
- package/dist/insightBoard/index.d.ts +1 -1
- package/dist/interfaces.d.ts +3 -0
- package/dist/renderer/index.d.ts +7 -3
- package/dist/store/visualSpecStore.d.ts +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/media.d.ts +3 -0
- package/dist/vis/react-vega.d.ts +5 -1
- package/dist/vis/theme.d.ts +146 -0
- package/dist/visualSettings/index.d.ts +2 -1
- package/package.json +2 -1
- package/src/App.tsx +24 -16
- package/src/components/button/base.ts +1 -0
- package/src/components/button/default.tsx +6 -2
- package/src/components/button/defaultMini.tsx +17 -0
- package/src/components/button/primary.tsx +6 -2
- package/src/components/button/primaryMini.tsx +21 -0
- package/src/components/callout.tsx +9 -4
- package/src/components/clickMenu.tsx +1 -5
- package/src/components/dataTable/index.tsx +42 -52
- package/src/components/dataTable/pagination.tsx +4 -4
- package/src/components/dataTypeIcon.tsx +1 -1
- package/src/components/dropdownContext/index.tsx +64 -0
- package/src/components/dropdownSelect/index.tsx +92 -0
- package/src/components/modal.tsx +20 -22
- package/src/components/sizeSetting.tsx +2 -2
- package/src/components/tabs/defaultTab.tsx +4 -4
- package/src/components/tabs/editableTab.tsx +5 -5
- package/src/components/toolbar/components.tsx +10 -8
- package/src/components/toolbar/index.tsx +16 -4
- package/src/components/toolbar/toolbar-button.tsx +8 -2
- package/src/components/toolbar/toolbar-item.tsx +18 -9
- package/src/components/toolbar/toolbar-select-button.tsx +21 -8
- package/src/components/toolbar/toolbar-toggle-button.tsx +8 -2
- package/src/components/tooltip.tsx +10 -3
- package/src/dataSource/dataSelection/config.ts +28 -0
- package/src/dataSource/dataSelection/csvData.tsx +77 -32
- package/src/dataSource/dataSelection/gwFile.tsx +0 -8
- package/src/dataSource/dataSelection/index.tsx +1 -2
- package/src/dataSource/dataSelection/publicData.tsx +2 -3
- package/src/dataSource/index.tsx +80 -61
- package/src/fields/aestheticFields.tsx +3 -1
- package/src/fields/components.tsx +20 -38
- package/src/fields/datasetFields/dimFields.tsx +43 -35
- package/src/fields/datasetFields/index.tsx +3 -4
- package/src/fields/datasetFields/meaFields.tsx +73 -47
- package/src/fields/encodeFields/singleEncodeDropDown.tsx +92 -0
- package/src/fields/encodeFields/singleEncodeEditor.tsx +78 -0
- package/src/fields/filterField/filterEditDialog.tsx +63 -98
- package/src/fields/filterField/filterPill.tsx +1 -1
- package/src/fields/filterField/slider.tsx +2 -2
- package/src/fields/filterField/tabs.tsx +11 -21
- package/src/fields/obComponents/obPill.tsx +65 -35
- package/src/index.css +13 -0
- package/src/insightBoard/index.tsx +24 -23
- package/src/insightBoard/mainBoard.tsx +9 -2
- package/src/insightBoard/radioGroupButtons.tsx +7 -0
- package/src/interfaces.ts +5 -1
- package/src/lib/inferMeta.ts +1 -1
- package/src/locales/en-US.json +11 -5
- package/src/locales/i18n.ts +7 -0
- package/src/locales/ja-JP.json +195 -0
- package/src/locales/zh-CN.json +9 -3
- package/src/main.tsx +1 -1
- package/src/renderer/index.tsx +96 -70
- package/src/store/visualSpecStore.ts +16 -0
- package/src/utils/index.ts +19 -0
- package/src/utils/media.ts +31 -0
- package/src/utils/normalization.ts +2 -1
- package/src/vis/react-vega.tsx +36 -5
- package/src/vis/theme.ts +124 -0
- package/src/visualSettings/index.tsx +29 -33
- package/dist/components/container.d.ts +0 -2
- package/src/components/container.tsx +0 -16
|
@@ -8,17 +8,23 @@ export interface ToolbarButtonItem extends IToolbarItem {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
const ToolbarButton = memo<IToolbarProps<ToolbarButtonItem>>(function ToolbarButton(props) {
|
|
11
|
-
const { item, styles } = props;
|
|
11
|
+
const { item, styles, darkModePreference } = props;
|
|
12
12
|
const { icon: Icon, label, disabled, onClick } = item;
|
|
13
13
|
const handlers = useHandlers(() => onClick?.(), disabled ?? false);
|
|
14
14
|
|
|
15
|
+
const mergedIconStyles = {
|
|
16
|
+
...styles?.icon,
|
|
17
|
+
...item.styles?.icon,
|
|
18
|
+
};
|
|
19
|
+
|
|
15
20
|
return (
|
|
16
21
|
<>
|
|
17
22
|
<ToolbarItemContainer
|
|
18
23
|
props={props}
|
|
19
24
|
handlers={onClick ? handlers : null}
|
|
25
|
+
darkModePreference={darkModePreference}
|
|
20
26
|
>
|
|
21
|
-
<Icon style={
|
|
27
|
+
<Icon style={mergedIconStyles} />
|
|
22
28
|
</ToolbarItemContainer>
|
|
23
29
|
</>
|
|
24
30
|
);
|
|
@@ -8,6 +8,7 @@ import { ToolbarContainer, ToolbarItemContainerElement, ToolbarSplitter, useHand
|
|
|
8
8
|
import Toolbar, { ToolbarProps } from ".";
|
|
9
9
|
import Tooltip from "../tooltip";
|
|
10
10
|
import Callout from "../callout";
|
|
11
|
+
import { useCurrentMediaTheme } from "../../utils/media";
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
const ToolbarSplit = styled.div<{ open: boolean }>`
|
|
@@ -34,7 +35,7 @@ const ToolbarSplit = styled.div<{ open: boolean }>`
|
|
|
34
35
|
const FormContainer = styled(ToolbarContainer)`
|
|
35
36
|
width: max-content;
|
|
36
37
|
height: max-content;
|
|
37
|
-
background-color: #fff;
|
|
38
|
+
background-color: ${({ dark }) => dark ? '#000' : '#fff'};
|
|
38
39
|
`;
|
|
39
40
|
|
|
40
41
|
export interface IToolbarItem {
|
|
@@ -48,6 +49,7 @@ export interface IToolbarItem {
|
|
|
48
49
|
disabled?: boolean;
|
|
49
50
|
menu?: ToolbarProps;
|
|
50
51
|
form?: JSX.Element;
|
|
52
|
+
styles?: Partial<Pick<NonNullable<ToolbarProps['styles']>, 'item' | 'icon' | 'splitIcon'>>;
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
export const ToolbarItemSplitter = '-';
|
|
@@ -61,6 +63,7 @@ export type ToolbarItemProps = (
|
|
|
61
63
|
|
|
62
64
|
export interface IToolbarProps<P extends Exclude<ToolbarItemProps, typeof ToolbarItemSplitter> = Exclude<ToolbarItemProps, typeof ToolbarItemSplitter>> {
|
|
63
65
|
item: P;
|
|
66
|
+
darkModePreference: NonNullable<ToolbarProps['darkModePreference']>;
|
|
64
67
|
styles?: ToolbarProps['styles'];
|
|
65
68
|
openedKey: string | null;
|
|
66
69
|
setOpenedKey: (key: string | null) => void;
|
|
@@ -70,6 +73,7 @@ export interface IToolbarProps<P extends Exclude<ToolbarItemProps, typeof Toolba
|
|
|
70
73
|
let idFlag = 0;
|
|
71
74
|
|
|
72
75
|
export const ToolbarItemContainer = memo<{
|
|
76
|
+
darkModePreference: NonNullable<ToolbarProps['darkModePreference']>;
|
|
73
77
|
props: IToolbarProps;
|
|
74
78
|
handlers: ReturnType<typeof useHandlers> | null;
|
|
75
79
|
children: unknown;
|
|
@@ -80,6 +84,7 @@ export const ToolbarItemContainer = memo<{
|
|
|
80
84
|
styles, openedKey, setOpenedKey, renderSlot,
|
|
81
85
|
},
|
|
82
86
|
handlers,
|
|
87
|
+
darkModePreference,
|
|
83
88
|
children,
|
|
84
89
|
...props
|
|
85
90
|
}
|
|
@@ -126,15 +131,18 @@ export const ToolbarItemContainer = memo<{
|
|
|
126
131
|
|
|
127
132
|
useEffect(() => {
|
|
128
133
|
if (opened && menu) {
|
|
129
|
-
renderSlot(<Toolbar {...menu} />);
|
|
134
|
+
renderSlot(<Toolbar {...menu} darkModePreference={darkModePreference} />);
|
|
130
135
|
return () => renderSlot(null);
|
|
131
136
|
}
|
|
132
137
|
}, [opened, menu, renderSlot]);
|
|
133
138
|
|
|
139
|
+
const dark = useCurrentMediaTheme(darkModePreference) === 'dark';
|
|
140
|
+
|
|
134
141
|
return (
|
|
135
142
|
<>
|
|
136
|
-
<Tooltip content={label}>
|
|
143
|
+
<Tooltip content={label} darkModePreference={darkModePreference}>
|
|
137
144
|
<ToolbarItemContainerElement
|
|
145
|
+
dark={dark}
|
|
138
146
|
role="button" tabIndex={disabled ? undefined : 0} aria-label={label} aria-disabled={disabled ?? false}
|
|
139
147
|
split={Boolean(form || menu)}
|
|
140
148
|
style={styles?.item}
|
|
@@ -186,8 +194,8 @@ export const ToolbarItemContainer = memo<{
|
|
|
186
194
|
</ToolbarItemContainerElement>
|
|
187
195
|
</Tooltip>
|
|
188
196
|
{opened && form && (
|
|
189
|
-
<Callout target={`#${id}`}>
|
|
190
|
-
<FormContainer onMouseDown={e => e.stopPropagation()}>
|
|
197
|
+
<Callout target={`#${id}`} darkModePreference={darkModePreference}>
|
|
198
|
+
<FormContainer dark={dark} onMouseDown={e => e.stopPropagation()}>
|
|
191
199
|
{form}
|
|
192
200
|
</FormContainer>
|
|
193
201
|
</Callout>
|
|
@@ -198,20 +206,21 @@ export const ToolbarItemContainer = memo<{
|
|
|
198
206
|
|
|
199
207
|
const ToolbarItem = memo<{
|
|
200
208
|
item: ToolbarItemProps;
|
|
209
|
+
darkModePreference: NonNullable<ToolbarProps['darkModePreference']>;
|
|
201
210
|
styles?: ToolbarProps['styles'];
|
|
202
211
|
openedKey: string | null;
|
|
203
212
|
setOpenedKey: (key: string | null) => void;
|
|
204
213
|
renderSlot: (node: ReactNode) => void;
|
|
205
|
-
}>(function ToolbarItem ({ item, styles, openedKey, setOpenedKey, renderSlot }) {
|
|
214
|
+
}>(function ToolbarItem ({ item, styles, openedKey, setOpenedKey, renderSlot, darkModePreference }) {
|
|
206
215
|
if (item === ToolbarItemSplitter) {
|
|
207
216
|
return <ToolbarSplitter />;
|
|
208
217
|
}
|
|
209
218
|
if ('checked' in item) {
|
|
210
|
-
return <ToolbarToggleButton item={item} styles={styles} openedKey={openedKey} setOpenedKey={setOpenedKey} renderSlot={renderSlot} />;
|
|
219
|
+
return <ToolbarToggleButton item={item} styles={styles} openedKey={openedKey} setOpenedKey={setOpenedKey} renderSlot={renderSlot} darkModePreference={darkModePreference} />;
|
|
211
220
|
} else if ('options' in item) {
|
|
212
|
-
return <ToolbarSelectButton item={item} styles={styles} openedKey={openedKey} setOpenedKey={setOpenedKey} renderSlot={renderSlot} />;
|
|
221
|
+
return <ToolbarSelectButton item={item} styles={styles} openedKey={openedKey} setOpenedKey={setOpenedKey} renderSlot={renderSlot} darkModePreference={darkModePreference} />;
|
|
213
222
|
}
|
|
214
|
-
return <ToolbarButton item={item} styles={styles} openedKey={openedKey} setOpenedKey={setOpenedKey} renderSlot={renderSlot} />;
|
|
223
|
+
return <ToolbarButton item={item} styles={styles} openedKey={openedKey} setOpenedKey={setOpenedKey} renderSlot={renderSlot} darkModePreference={darkModePreference} />;
|
|
215
224
|
});
|
|
216
225
|
|
|
217
226
|
|
|
@@ -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)`
|
|
@@ -16,7 +17,12 @@ const OptionGroup = styled(ToolbarContainer)`
|
|
|
16
17
|
--color: #777;
|
|
17
18
|
--color-hover: #555;
|
|
18
19
|
--blue: #282958;
|
|
19
|
-
background-color:
|
|
20
|
+
--dark-mode-background-color: #1f1f1f;
|
|
21
|
+
--dark-mode-background-color-hover: #2f2f2f;
|
|
22
|
+
--dark-mode-color: #aaa;
|
|
23
|
+
--dark-mode-color-hover: #ccc;
|
|
24
|
+
--dark-mode-blue: #282958;
|
|
25
|
+
background-color: ${({ dark }) => dark ? 'var(--dark-mode-background-color)' : 'var(--background-color)'};
|
|
20
26
|
`;
|
|
21
27
|
|
|
22
28
|
const Option = styled(ToolbarItemContainerElement)`
|
|
@@ -71,7 +77,7 @@ export interface ToolbarSelectButtonItem<T extends string = string> extends IToo
|
|
|
71
77
|
}
|
|
72
78
|
|
|
73
79
|
const ToolbarSelectButton = memo<IToolbarProps<ToolbarSelectButtonItem>>(function ToolbarSelectButton(props) {
|
|
74
|
-
const { item, styles, openedKey, setOpenedKey } = props;
|
|
80
|
+
const { darkModePreference, item, styles, openedKey, setOpenedKey } = props;
|
|
75
81
|
const { key, icon: Icon, disabled, options, value, onSelect } = item;
|
|
76
82
|
const id = `${key}::button`;
|
|
77
83
|
|
|
@@ -115,9 +121,17 @@ const ToolbarSelectButton = memo<IToolbarProps<ToolbarSelectButtonItem>>(functio
|
|
|
115
121
|
const currentOption = options.find(opt => opt.key === value);
|
|
116
122
|
const CurrentIcon = currentOption?.icon;
|
|
117
123
|
|
|
124
|
+
const mergedIconStyles = {
|
|
125
|
+
...styles?.icon,
|
|
126
|
+
...item.styles?.icon,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const dark = useCurrentMediaTheme(darkModePreference) === 'dark';
|
|
130
|
+
|
|
118
131
|
return (
|
|
119
132
|
<>
|
|
120
133
|
<ToolbarItemContainer
|
|
134
|
+
darkModePreference={darkModePreference}
|
|
121
135
|
props={produce(props, draft => {
|
|
122
136
|
if (currentOption) {
|
|
123
137
|
draft.item.label = `${draft.item.label}: ${currentOption.label}`;
|
|
@@ -126,28 +140,26 @@ const ToolbarSelectButton = memo<IToolbarProps<ToolbarSelectButtonItem>>(functio
|
|
|
126
140
|
handlers={handlers}
|
|
127
141
|
aria-haspopup="listbox"
|
|
128
142
|
>
|
|
129
|
-
<Icon style={
|
|
143
|
+
<Icon style={mergedIconStyles} />
|
|
130
144
|
{CurrentIcon && (
|
|
131
145
|
<CurrentIcon
|
|
132
146
|
style={{
|
|
133
|
-
...styles?.icon,
|
|
134
147
|
position: 'absolute',
|
|
135
148
|
left: 'calc(var(--height) - var(--icon-size) * 1.2)',
|
|
136
149
|
bottom: 'calc((var(--height) - var(--icon-size)) * 0.1)',
|
|
137
150
|
width: 'calc(var(--icon-size) * 0.6)',
|
|
138
151
|
height: 'calc(var(--icon-size) * 0.6)',
|
|
139
152
|
margin: 'calc((var(--height) - var(--icon-size)) * 0.2)',
|
|
140
|
-
filter: 'drop-shadow(0 0 0.5px var(--background-color)) '.repeat(4),
|
|
141
153
|
pointerEvents: 'none',
|
|
142
|
-
|
|
154
|
+
...mergedIconStyles,
|
|
143
155
|
}}
|
|
144
156
|
/>
|
|
145
157
|
)}
|
|
146
158
|
<TriggerFlag aria-hidden id={id} />
|
|
147
159
|
</ToolbarItemContainer>
|
|
148
160
|
{opened && (
|
|
149
|
-
<Callout target={`#${id}`}>
|
|
150
|
-
<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()}>
|
|
151
163
|
{options.map((option, idx, arr) => {
|
|
152
164
|
const selected = option.key === value;
|
|
153
165
|
const OptionIcon = option.icon;
|
|
@@ -156,6 +168,7 @@ const ToolbarSelectButton = memo<IToolbarProps<ToolbarSelectButtonItem>>(functio
|
|
|
156
168
|
const next = arr[(idx + 1) % arr.length];
|
|
157
169
|
return (
|
|
158
170
|
<Option
|
|
171
|
+
dark={dark}
|
|
159
172
|
key={option.key}
|
|
160
173
|
id={optionId}
|
|
161
174
|
role="option"
|
|
@@ -46,10 +46,15 @@ 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
|
|
|
53
|
+
const mergedIconStyles = {
|
|
54
|
+
...styles?.icon,
|
|
55
|
+
...item.styles?.icon,
|
|
56
|
+
};
|
|
57
|
+
|
|
53
58
|
return (
|
|
54
59
|
<>
|
|
55
60
|
<ToolbarItemContainer
|
|
@@ -57,9 +62,10 @@ const ToolbarToggleButton = memo<IToolbarProps<ToolbarToggleButtonItem>>(functio
|
|
|
57
62
|
handlers={handlers}
|
|
58
63
|
role="checkbox"
|
|
59
64
|
aria-checked={checked}
|
|
65
|
+
darkModePreference={darkModePreference}
|
|
60
66
|
>
|
|
61
67
|
<ToggleContainer checked={checked}>
|
|
62
|
-
<Icon style={
|
|
68
|
+
<Icon style={mergedIconStyles} />
|
|
63
69
|
</ToggleContainer>
|
|
64
70
|
</ToolbarItemContainer>
|
|
65
71
|
</>
|
|
@@ -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,7 +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;
|
|
37
|
+
background-color: ${({ dark }) => dark ? '#000' : '#fff'};
|
|
35
38
|
border-radius: 1px;
|
|
36
39
|
}
|
|
37
40
|
`;
|
|
@@ -42,6 +45,7 @@ const Tooltip = memo<TooltipProps>(function Tooltip({
|
|
|
42
45
|
autoHide = 3_000,
|
|
43
46
|
showDelay = 250,
|
|
44
47
|
hideDelay = 250,
|
|
48
|
+
darkModePreference = 'media',
|
|
45
49
|
}) {
|
|
46
50
|
const hostId = useMemo(() => flag++, []);
|
|
47
51
|
const [pos, setPos] = useState<[number, number]>([0, 0]);
|
|
@@ -118,6 +122,8 @@ const Tooltip = memo<TooltipProps>(function Tooltip({
|
|
|
118
122
|
}
|
|
119
123
|
}, [root, hostId]);
|
|
120
124
|
|
|
125
|
+
const darkMode = useCurrentMediaTheme(darkModePreference);
|
|
126
|
+
|
|
121
127
|
return (
|
|
122
128
|
<>
|
|
123
129
|
{element}
|
|
@@ -125,7 +131,8 @@ const Tooltip = memo<TooltipProps>(function Tooltip({
|
|
|
125
131
|
root &&
|
|
126
132
|
createPortal(
|
|
127
133
|
<Bubble
|
|
128
|
-
className=
|
|
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'}
|
|
129
136
|
onMouseOver={() => setHover(true)}
|
|
130
137
|
onMouseOut={() => setHover(false)}
|
|
131
138
|
style={{ left: pos[0], top: pos[1] - 4 }}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { IDropdownSelectOption } from "../../components/dropdownSelect";
|
|
2
|
+
|
|
3
|
+
export const charsetOptions: IDropdownSelectOption[] = [
|
|
4
|
+
{
|
|
5
|
+
label: 'UTF-8',
|
|
6
|
+
value: 'utf-8',
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
label: 'GB2312',
|
|
10
|
+
value: 'gb2312',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
label: 'US-ASCII',
|
|
14
|
+
value: 'us-ascii',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
label: 'Big5',
|
|
18
|
+
value: 'big5',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
label: 'Big5-HKSCS',
|
|
22
|
+
value: 'Big5-HKSCS',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
label: 'GB18030',
|
|
26
|
+
value: 'GB18030',
|
|
27
|
+
},
|
|
28
|
+
]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useRef, useCallback } from "react";
|
|
1
|
+
import React, { useRef, useCallback, useState } from "react";
|
|
2
2
|
import { FileReader } from "@kanaries/web-data-loader";
|
|
3
3
|
import { IRow } from "../../interfaces";
|
|
4
4
|
import Table from "../table";
|
|
@@ -8,25 +8,51 @@ import { observer } from "mobx-react-lite";
|
|
|
8
8
|
import { useTranslation } from "react-i18next";
|
|
9
9
|
import DefaultButton from "../../components/button/default";
|
|
10
10
|
import PrimaryButton from "../../components/button/primary";
|
|
11
|
+
import DropdownSelect from "../../components/dropdownSelect";
|
|
12
|
+
import { charsetOptions } from "./config";
|
|
11
13
|
|
|
12
14
|
const Container = styled.div`
|
|
13
15
|
overflow-x: auto;
|
|
16
|
+
min-height: 300px;
|
|
14
17
|
`;
|
|
15
18
|
|
|
16
19
|
interface ICSVData {}
|
|
17
20
|
const CSVData: React.FC<ICSVData> = (props) => {
|
|
18
21
|
const fileRef = useRef<HTMLInputElement>(null);
|
|
19
22
|
const { commonStore } = useGlobalStore();
|
|
20
|
-
const { tmpDSName, tmpDataSource } = commonStore;
|
|
23
|
+
const { tmpDSName, tmpDataSource, tmpDSRawFields } = commonStore;
|
|
24
|
+
const [encoding, setEncoding] = useState<string>("utf-8");
|
|
21
25
|
|
|
22
26
|
const onSubmitData = useCallback(() => {
|
|
23
27
|
commonStore.commitTempDS();
|
|
24
28
|
}, []);
|
|
25
29
|
|
|
26
30
|
const { t } = useTranslation("translation", { keyPrefix: "DataSource.dialog.file" });
|
|
31
|
+
const fileLoaded = tmpDataSource.length > 0 && tmpDSRawFields.length > 0;
|
|
27
32
|
|
|
28
33
|
return (
|
|
29
34
|
<Container>
|
|
35
|
+
{!fileLoaded && (
|
|
36
|
+
<div className="text-center">
|
|
37
|
+
<svg
|
|
38
|
+
className="mx-auto h-12 w-12 text-gray-400"
|
|
39
|
+
fill="none"
|
|
40
|
+
viewBox="0 0 24 24"
|
|
41
|
+
stroke="currentColor"
|
|
42
|
+
aria-hidden="true"
|
|
43
|
+
>
|
|
44
|
+
<path
|
|
45
|
+
vectorEffect="non-scaling-stroke"
|
|
46
|
+
strokeLinecap="round"
|
|
47
|
+
strokeLinejoin="round"
|
|
48
|
+
strokeWidth={2}
|
|
49
|
+
d="M9 13h6m-3-3v6m-9 1V7a2 2 0 012-2h6l2 2h6a2 2 0 012 2v8a2 2 0 01-2 2H5a2 2 0 01-2-2z"
|
|
50
|
+
/>
|
|
51
|
+
</svg>
|
|
52
|
+
<h3 className="mt-2 text-sm font-semibold text-gray-900">{t('choose_file')}</h3>
|
|
53
|
+
<p className="mt-1 text-sm text-gray-500">{t('get_start_desc')}</p>
|
|
54
|
+
</div>
|
|
55
|
+
)}
|
|
30
56
|
<input
|
|
31
57
|
style={{ display: "none" }}
|
|
32
58
|
type="file"
|
|
@@ -39,42 +65,61 @@ const CSVData: React.FC<ICSVData> = (props) => {
|
|
|
39
65
|
file,
|
|
40
66
|
config: { type: "reservoirSampling", size: Infinity },
|
|
41
67
|
onLoading: () => {},
|
|
68
|
+
encoding,
|
|
42
69
|
}).then((data) => {
|
|
43
70
|
commonStore.updateTempDS(data as IRow[]);
|
|
44
71
|
});
|
|
45
72
|
}
|
|
46
73
|
}}
|
|
47
74
|
/>
|
|
48
|
-
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
{!fileLoaded && (
|
|
76
|
+
<div className="my-1 flex justify-center">
|
|
77
|
+
<DefaultButton
|
|
78
|
+
className="mr-2"
|
|
79
|
+
onClick={() => {
|
|
80
|
+
if (fileRef.current) {
|
|
81
|
+
fileRef.current.click();
|
|
82
|
+
}
|
|
83
|
+
}}
|
|
84
|
+
text={t("open")}
|
|
85
|
+
/>
|
|
86
|
+
<div className="inline-block relative">
|
|
87
|
+
<DropdownSelect
|
|
88
|
+
buttonClassName="w-36"
|
|
89
|
+
options={charsetOptions}
|
|
90
|
+
selectedKey={encoding}
|
|
91
|
+
onSelect={(k) => {
|
|
92
|
+
setEncoding(k);
|
|
93
|
+
}}
|
|
94
|
+
/>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
)}
|
|
98
|
+
{fileLoaded && (
|
|
99
|
+
<div className="mb-2 mt-6">
|
|
100
|
+
<label className="block text-xs text-gray-800 dark:text-gray-200 mb-1 font-bold">
|
|
101
|
+
{t("dataset_name")}
|
|
102
|
+
</label>
|
|
103
|
+
<input
|
|
104
|
+
type="text"
|
|
105
|
+
placeholder={t("dataset_name")}
|
|
106
|
+
value={tmpDSName}
|
|
107
|
+
onChange={(e) => {
|
|
108
|
+
commonStore.updateTempName(e.target.value);
|
|
109
|
+
}}
|
|
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
|
+
/>
|
|
112
|
+
<PrimaryButton
|
|
113
|
+
className="mr-2"
|
|
114
|
+
text={t("submit")}
|
|
115
|
+
disabled={tmpDataSource.length === 0}
|
|
116
|
+
onClick={() => {
|
|
117
|
+
onSubmitData();
|
|
118
|
+
}}
|
|
119
|
+
/>
|
|
120
|
+
</div>
|
|
121
|
+
)}
|
|
122
|
+
{fileLoaded && <Table />}
|
|
78
123
|
</Container>
|
|
79
124
|
);
|
|
80
125
|
};
|
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
import React, { useRef, useCallback } from "react";
|
|
2
|
-
import { FileReader } from "@kanaries/web-data-loader";
|
|
3
|
-
import { IRow } from "../../interfaces";
|
|
4
|
-
import Table from "../table";
|
|
5
|
-
import styled from "styled-components";
|
|
6
2
|
import { useGlobalStore } from "../../store";
|
|
7
3
|
import { observer } from "mobx-react-lite";
|
|
8
|
-
import { useTranslation } from "react-i18next";
|
|
9
4
|
|
|
10
|
-
const Container = styled.div`
|
|
11
|
-
overflow-x: auto;
|
|
12
|
-
`;
|
|
13
5
|
|
|
14
6
|
interface GWFileProps {
|
|
15
7
|
fileRef: React.RefObject<HTMLInputElement>;
|
|
@@ -3,7 +3,7 @@ import { useState } from "react";
|
|
|
3
3
|
import CSVData from "./csvData";
|
|
4
4
|
import PublicData from "./publicData";
|
|
5
5
|
import { useTranslation } from "react-i18next";
|
|
6
|
-
import PureTabs from "../../components/tabs/
|
|
6
|
+
import PureTabs from "../../components/tabs/defaultTab";
|
|
7
7
|
|
|
8
8
|
const DataSelection: React.FC = (props) => {
|
|
9
9
|
const [sourceType, setSourceType] = useState<"file" | "public">("file");
|
|
@@ -26,7 +26,6 @@ const DataSelection: React.FC = (props) => {
|
|
|
26
26
|
setSourceType(sk as "public" | "file");
|
|
27
27
|
}}
|
|
28
28
|
/>
|
|
29
|
-
<hr className="mt-1 mb-1" />
|
|
30
29
|
{sourceType === "file" && <CSVData />}
|
|
31
30
|
{sourceType === "public" && <PublicData />}
|
|
32
31
|
</div>
|
|
@@ -36,20 +36,19 @@ const PublicData: React.FC<IPublicDataProps> = props => {
|
|
|
36
36
|
})
|
|
37
37
|
})
|
|
38
38
|
}}
|
|
39
|
-
className="border rounded border-gray-300 p-2 m-2 cursor-pointer hover:bg-gray-50"
|
|
39
|
+
className="border rounded border-gray-300 dark:border-gray-600 p-2 m-2 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 dark:text-gray-200"
|
|
40
40
|
>
|
|
41
41
|
<div>{data.title}</div>
|
|
42
42
|
{/* <p>{data.title}</p> */}
|
|
43
43
|
</div>)
|
|
44
44
|
}
|
|
45
45
|
</div>
|
|
46
|
-
<hr className="m-1" />
|
|
47
46
|
<PrimaryButton
|
|
47
|
+
className='my-1'
|
|
48
48
|
disabled={tmpDataSource.length === 0}
|
|
49
49
|
onClick={() => { commonStore.commitTempDS() }}
|
|
50
50
|
text={t('submit')}
|
|
51
51
|
/>
|
|
52
|
-
<hr className="m-1" />
|
|
53
52
|
<Table />
|
|
54
53
|
</div>
|
|
55
54
|
}
|