@kanaries/graphic-walker 0.2.14 → 0.2.16
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 +5 -2
- package/dist/assets/explainer.worker-8428eb12.js.map +1 -1
- package/dist/assets/transform.worker-5d54ff09.js.map +1 -0
- package/dist/assets/viewQuery.worker-ffefc111.js.map +1 -0
- package/dist/components/callout.d.ts +2 -0
- package/dist/components/codeExport/index.d.ts +3 -0
- package/dist/components/loadingLayer.d.ts +2 -0
- package/dist/components/tabs/defaultTab.d.ts +1 -0
- package/dist/components/tabs/editableTab.d.ts +1 -2
- 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 +3 -0
- package/dist/components/tooltip.d.ts +2 -0
- package/dist/dataSource/dataSelection/config.d.ts +1 -0
- package/dist/dataSource/dataSelection/utils.d.ts +2 -0
- package/dist/datasets/tmp/test.json +1 -0
- package/dist/fields/components.d.ts +0 -1
- package/dist/fields/filterField/filterEditDialog.d.ts +1 -1
- package/dist/graphic-walker.es.js +23930 -23320
- package/dist/graphic-walker.es.js.map +1 -1
- package/dist/graphic-walker.umd.js +143 -273
- package/dist/graphic-walker.umd.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/interfaces.d.ts +23 -1
- package/dist/lib/execExp.d.ts +8 -0
- package/dist/lib/interfaces.d.ts +22 -0
- package/dist/lib/op/aggregate.d.ts +3 -0
- package/dist/lib/op/bin.d.ts +3 -0
- package/dist/lib/op/fold.d.ts +3 -0
- package/dist/lib/op/stat.d.ts +8 -0
- package/dist/lib/viewQuery.d.ts +5 -0
- package/dist/models/visSpecHistory.d.ts +2 -0
- package/dist/renderer/index.d.ts +6 -3
- package/dist/renderer/specRenderer.d.ts +13 -0
- package/dist/services.d.ts +4 -1
- package/dist/store/commonStore.d.ts +6 -0
- package/dist/store/index.d.ts +3 -2
- package/dist/store/visualSpecStore.d.ts +11 -4
- package/dist/utils/dataPrep.d.ts +2 -0
- package/dist/utils/index.d.ts +3 -5
- package/dist/utils/media.d.ts +2 -1
- package/dist/utils/save.d.ts +1 -2
- package/dist/vis/react-vega.d.ts +4 -23
- package/dist/vis/spec/aggregate.d.ts +4 -0
- package/dist/vis/spec/encode.d.ts +19 -0
- package/dist/vis/spec/field.d.ts +2 -0
- package/dist/vis/spec/mark.d.ts +7 -0
- package/dist/vis/spec/stack.d.ts +4 -0
- package/dist/vis/spec/view.d.ts +67 -0
- package/dist/vis/theme.d.ts +36 -20
- package/dist/visualSettings/index.d.ts +2 -1
- package/dist/workers/transform.d.ts +2 -0
- package/package.json +4 -3
- package/src/App.tsx +23 -15
- package/src/components/callout.tsx +9 -7
- package/src/components/clickMenu.tsx +1 -7
- package/src/components/codeExport/index.tsx +114 -0
- package/src/components/dataTable/index.tsx +10 -10
- package/src/components/loadingLayer.tsx +7 -0
- package/src/components/modal.tsx +1 -15
- package/src/components/sizeSetting.tsx +2 -2
- package/src/components/tabs/defaultTab.tsx +4 -2
- package/src/components/tabs/editableTab.tsx +75 -40
- package/src/components/toolbar/components.tsx +8 -23
- package/src/components/toolbar/index.tsx +11 -4
- package/src/components/toolbar/toolbar-button.tsx +2 -1
- package/src/components/toolbar/toolbar-item.tsx +17 -12
- package/src/components/toolbar/toolbar-select-button.tsx +9 -13
- package/src/components/toolbar/toolbar-toggle-button.tsx +2 -1
- package/src/components/tooltip.tsx +10 -6
- package/src/dataSource/dataSelection/config.ts +11 -0
- package/src/dataSource/dataSelection/csvData.tsx +72 -40
- package/src/dataSource/dataSelection/gwFile.tsx +2 -2
- package/src/dataSource/dataSelection/utils.ts +28 -0
- package/src/dataSource/index.tsx +2 -3
- package/src/dataSource/utils.ts +8 -3
- package/src/fields/components.tsx +13 -50
- package/src/fields/datasetFields/index.tsx +3 -4
- package/src/fields/datasetFields/meaFields.tsx +12 -4
- package/src/fields/encodeFields/singleEncodeEditor.tsx +1 -1
- package/src/fields/filterField/filterEditDialog.tsx +63 -99
- package/src/fields/filterField/slider.tsx +1 -1
- package/src/index.css +4 -4
- package/src/index.tsx +22 -22
- package/src/insightBoard/mainBoard.tsx +9 -2
- package/src/interfaces.ts +30 -3
- package/src/lib/execExp.ts +147 -0
- package/src/lib/interfaces.ts +39 -0
- package/src/lib/op/aggregate.ts +49 -0
- package/src/lib/op/bin.ts +25 -0
- package/src/lib/op/fold.ts +17 -0
- package/src/lib/op/stat.ts +46 -0
- package/src/lib/viewQuery.ts +23 -0
- package/src/locales/en-US.json +8 -3
- package/src/locales/i18n.ts +7 -1
- package/src/locales/ja-JP.json +197 -0
- package/src/locales/zh-CN.json +8 -3
- package/src/main.tsx +1 -1
- package/src/models/visSpecHistory.ts +14 -0
- package/src/renderer/index.tsx +58 -101
- package/src/renderer/specRenderer.tsx +119 -0
- package/src/segments/segmentNav.tsx +3 -16
- package/src/segments/visNav.tsx +17 -6
- package/src/services.ts +37 -1
- package/src/store/commonStore.ts +14 -9
- package/src/store/index.tsx +11 -4
- package/src/store/visualSpecStore.ts +89 -50
- package/src/utils/dataPrep.ts +24 -0
- package/src/utils/index.ts +16 -17
- package/src/utils/media.ts +16 -11
- package/src/utils/normalization.ts +3 -1
- package/src/utils/save.ts +1 -2
- package/src/vis/react-vega.tsx +11 -332
- package/src/vis/spec/aggregate.ts +13 -0
- package/src/vis/spec/encode.ts +69 -0
- package/src/vis/spec/field.ts +10 -0
- package/src/vis/spec/mark.ts +30 -0
- package/src/vis/spec/stack.ts +11 -0
- package/src/vis/spec/view.ts +138 -0
- package/src/vis/theme.ts +35 -25
- package/src/visualSettings/index.tsx +22 -33
- package/src/workers/transform.ts +12 -0
- package/src/workers/transform.worker.js +13 -0
- package/src/workers/viewQuery.worker.js +16 -0
- package/dist/components/container.d.ts +0 -2
- package/dist/dataSource/pannel.d.ts +0 -5
- package/src/components/container.tsx +0 -25
- package/src/dataSource/pannel.tsx +0 -71
package/src/App.tsx
CHANGED
|
@@ -4,10 +4,9 @@ import { observer } from "mobx-react-lite";
|
|
|
4
4
|
import { LightBulbIcon } from "@heroicons/react/24/outline";
|
|
5
5
|
import { toJS } from "mobx";
|
|
6
6
|
import { useTranslation } from "react-i18next";
|
|
7
|
-
import { IMutField, IRow, ISegmentKey } from "./interfaces";
|
|
7
|
+
import { IDarkMode, IMutField, IRow, ISegmentKey, IThemeKey } from "./interfaces";
|
|
8
8
|
import type { IReactVegaHandler } from "./vis/react-vega";
|
|
9
9
|
import VisualSettings from "./visualSettings";
|
|
10
|
-
import { Container, NestContainer } from "./components/container";
|
|
11
10
|
import ClickMenu from "./components/clickMenu";
|
|
12
11
|
import InsightBoard from "./insightBoard/index";
|
|
13
12
|
import PosFields from "./fields/posFields";
|
|
@@ -15,7 +14,7 @@ import AestheticFields from "./fields/aestheticFields";
|
|
|
15
14
|
import DatasetFields from "./fields/datasetFields/index";
|
|
16
15
|
import ReactiveRenderer from "./renderer/index";
|
|
17
16
|
import DataSourceSegment from "./dataSource/index";
|
|
18
|
-
import { useGlobalStore } from "./store";
|
|
17
|
+
import { IGlobalStore, useGlobalStore } from "./store";
|
|
19
18
|
import { preAnalysis, destroyWorker } from "./services";
|
|
20
19
|
import VisNav from "./segments/visNav";
|
|
21
20
|
import { mergeLocaleRes, setLocaleLanguage } from "./locales/i18n";
|
|
@@ -23,6 +22,8 @@ import FilterField from "./fields/filterField";
|
|
|
23
22
|
import { guardDataKeys } from "./utils/dataPrep";
|
|
24
23
|
import SegmentNav from "./segments/segmentNav";
|
|
25
24
|
import DatasetConfig from "./dataSource/datasetConfig";
|
|
25
|
+
import { useCurrentMediaTheme } from "./utils/media";
|
|
26
|
+
import CodeExport from "./components/codeExport";
|
|
26
27
|
|
|
27
28
|
export interface IGWProps {
|
|
28
29
|
dataSource?: IRow[];
|
|
@@ -37,7 +38,9 @@ export interface IGWProps {
|
|
|
37
38
|
*/
|
|
38
39
|
fieldKeyGuard?: boolean;
|
|
39
40
|
/** @default "vega" */
|
|
40
|
-
themeKey?:
|
|
41
|
+
themeKey?: IThemeKey;
|
|
42
|
+
dark?: IDarkMode;
|
|
43
|
+
storeRef?: React.MutableRefObject<IGlobalStore | null>;
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
const App = observer<IGWProps>(function App (props) {
|
|
@@ -50,6 +53,7 @@ const App = observer<IGWProps>(function App (props) {
|
|
|
50
53
|
hideDataSourceConfig,
|
|
51
54
|
fieldKeyGuard = true,
|
|
52
55
|
themeKey = 'vega',
|
|
56
|
+
dark = 'media'
|
|
53
57
|
} = props;
|
|
54
58
|
const { commonStore, vizStore } = useGlobalStore();
|
|
55
59
|
const [insightReady, setInsightReady] = useState<boolean>(true);
|
|
@@ -117,14 +121,16 @@ const App = observer<IGWProps>(function App (props) {
|
|
|
117
121
|
};
|
|
118
122
|
}, [currentDataset, spec]);
|
|
119
123
|
|
|
124
|
+
const darkMode = useCurrentMediaTheme(dark);
|
|
125
|
+
|
|
120
126
|
const rendererRef = useRef<IReactVegaHandler>(null);
|
|
121
127
|
|
|
122
128
|
return (
|
|
123
|
-
<div className=
|
|
129
|
+
<div className={`${darkMode === 'dark' ? 'dark' : ''} App font-sans bg-white dark:bg-zinc-900 dark:text-white m-0 p-0`}>
|
|
124
130
|
{/* <div className="grow-0">
|
|
125
131
|
<PageNav />
|
|
126
132
|
</div> */}
|
|
127
|
-
<div className="">
|
|
133
|
+
<div className="bg-white dark:bg-zinc-900 dark:text-white">
|
|
128
134
|
{!hideDataSourceConfig && <DataSourceSegment preWorkDone={insightReady} />}
|
|
129
135
|
<div className="px-2 mx-2">
|
|
130
136
|
<SegmentNav />
|
|
@@ -133,8 +139,9 @@ const App = observer<IGWProps>(function App (props) {
|
|
|
133
139
|
}
|
|
134
140
|
</div>
|
|
135
141
|
{segmentKey === ISegmentKey.vis && (
|
|
136
|
-
<
|
|
137
|
-
<VisualSettings rendererHandler={rendererRef} />
|
|
142
|
+
<div style={{ marginTop: "0em", borderTop: "none" }} className="m-4 p-4 border border-gray-200 dark:border-gray-700">
|
|
143
|
+
<VisualSettings rendererHandler={rendererRef} darkModePreference={dark} />
|
|
144
|
+
<CodeExport />
|
|
138
145
|
<div className="md:grid md:grid-cols-12 xl:grid-cols-6">
|
|
139
146
|
<div className="md:col-span-3 xl:col-span-1">
|
|
140
147
|
<DatasetFields />
|
|
@@ -147,7 +154,8 @@ const App = observer<IGWProps>(function App (props) {
|
|
|
147
154
|
<div>
|
|
148
155
|
<PosFields />
|
|
149
156
|
</div>
|
|
150
|
-
<
|
|
157
|
+
<div
|
|
158
|
+
className="m-0.5 p-1 border border-gray-200 dark:border-gray-700"
|
|
151
159
|
style={{ minHeight: "600px", overflow: "auto" }}
|
|
152
160
|
onMouseLeave={() => {
|
|
153
161
|
vizEmbededMenu.show && commonStore.closeEmbededMenu();
|
|
@@ -156,12 +164,12 @@ const App = observer<IGWProps>(function App (props) {
|
|
|
156
164
|
vizEmbededMenu.show && commonStore.closeEmbededMenu();
|
|
157
165
|
}}
|
|
158
166
|
>
|
|
159
|
-
{datasets.length > 0 && <ReactiveRenderer ref={rendererRef} themeKey={themeKey} />}
|
|
167
|
+
{datasets.length > 0 && <ReactiveRenderer ref={rendererRef} themeKey={themeKey} dark={dark} />}
|
|
160
168
|
<InsightBoard />
|
|
161
169
|
{vizEmbededMenu.show && (
|
|
162
170
|
<ClickMenu x={vizEmbededMenu.position[0]} y={vizEmbededMenu.position[1]}>
|
|
163
171
|
<div
|
|
164
|
-
className="flex items-center whitespace-nowrap py-1 px-4 hover:bg-gray-100"
|
|
172
|
+
className="flex items-center whitespace-nowrap py-1 px-4 hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer"
|
|
165
173
|
onClick={() => {
|
|
166
174
|
commonStore.closeEmbededMenu();
|
|
167
175
|
commonStore.setShowInsightBoard(true);
|
|
@@ -174,15 +182,15 @@ const App = observer<IGWProps>(function App (props) {
|
|
|
174
182
|
</div>
|
|
175
183
|
</ClickMenu>
|
|
176
184
|
)}
|
|
177
|
-
</
|
|
185
|
+
</div>
|
|
178
186
|
</div>
|
|
179
187
|
</div>
|
|
180
|
-
</
|
|
188
|
+
</div>
|
|
181
189
|
)}
|
|
182
190
|
{segmentKey === ISegmentKey.data && (
|
|
183
|
-
<
|
|
191
|
+
<div className="m-4 p-4 border border-gray-200 dark:border-gray-700" style={{ marginTop: "0em", borderTop: "none" }}>
|
|
184
192
|
<DatasetConfig />
|
|
185
|
-
</
|
|
193
|
+
</div>
|
|
186
194
|
)}
|
|
187
195
|
</div>
|
|
188
196
|
</div>
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import React, { memo, ReactNode, useContext, useEffect, 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 CalloutProps {
|
|
7
9
|
target: string;
|
|
8
10
|
children: ReactNode;
|
|
11
|
+
darkModePreference?: IDarkMode;
|
|
9
12
|
}
|
|
10
13
|
|
|
11
|
-
const Bubble = styled.div
|
|
14
|
+
const Bubble = styled.div<{ dark: boolean }>`
|
|
12
15
|
border-radius: 1px;
|
|
13
16
|
transform: translate(-50%, 0);
|
|
14
17
|
filter: drop-shadow(0 1.6px 1px rgba(0, 0, 0, 0.24)) drop-shadow(0 -1px 0.8px rgba(0, 0, 0, 0.19));
|
|
@@ -25,15 +28,12 @@ const Bubble = styled.div`
|
|
|
25
28
|
width: 8px;
|
|
26
29
|
height: 8px;
|
|
27
30
|
transform: translate(-50%, -50%) rotate(45deg);
|
|
28
|
-
background-color: #fff;
|
|
31
|
+
background-color: ${({ dark }) => dark ? "#000" : "#fff"};
|
|
29
32
|
border-radius: 1px;
|
|
30
|
-
@media (prefers-color-scheme: dark) {
|
|
31
|
-
background-color: #000;
|
|
32
|
-
}
|
|
33
33
|
}
|
|
34
34
|
`;
|
|
35
35
|
|
|
36
|
-
const Callout = memo<CalloutProps>(function Callout({ target, children }) {
|
|
36
|
+
const Callout = memo<CalloutProps>(function Callout({ target, children, darkModePreference = 'media' }) {
|
|
37
37
|
const shadowDomMeta = useContext(ShadowDomContext);
|
|
38
38
|
const { root } = shadowDomMeta;
|
|
39
39
|
const [pos, setPos] = useState<[number, number] | null>(null);
|
|
@@ -48,11 +48,13 @@ const Callout = memo<CalloutProps>(function Callout({ target, children }) {
|
|
|
48
48
|
}
|
|
49
49
|
}, [target, root]);
|
|
50
50
|
|
|
51
|
+
const darkMode = useCurrentMediaTheme(darkModePreference);
|
|
52
|
+
|
|
51
53
|
return (
|
|
52
54
|
root &&
|
|
53
55
|
pos &&
|
|
54
56
|
createPortal(
|
|
55
|
-
<Bubble role="dialog" className="fixed bg-white dark:bg-zinc-900 z-50" style={{ left: pos[0], top: pos[1] + 4 }}>
|
|
57
|
+
<Bubble role="dialog" dark={darkMode === 'dark'} className="fixed bg-white dark:bg-zinc-900 z-50" style={{ left: pos[0], top: pos[1] + 4 }}>
|
|
56
58
|
{children}
|
|
57
59
|
</Bubble>,
|
|
58
60
|
root
|
|
@@ -3,16 +3,10 @@ import styled from "styled-components";
|
|
|
3
3
|
|
|
4
4
|
const MenuContainer = styled.div`
|
|
5
5
|
min-width: 100px;
|
|
6
|
-
background-color: #fff;
|
|
7
|
-
border: 1px solid #f0f0f0;
|
|
8
6
|
position: absolute;
|
|
9
7
|
z-index: 99;
|
|
10
8
|
cursor: pointer;
|
|
11
9
|
padding: 4px;
|
|
12
|
-
@media (prefers-color-scheme: dark) {
|
|
13
|
-
background-color: #000;
|
|
14
|
-
border: 1px solid #4b5563;
|
|
15
|
-
}
|
|
16
10
|
`;
|
|
17
11
|
interface ClickMenuProps {
|
|
18
12
|
x: number;
|
|
@@ -22,7 +16,7 @@ interface ClickMenuProps {
|
|
|
22
16
|
const ClickMenu: React.FC<ClickMenuProps> = (props) => {
|
|
23
17
|
const { x, y, children } = props;
|
|
24
18
|
return (
|
|
25
|
-
<MenuContainer className="shadow-lg text-sm" style={{ left: x + "px", top: y + "px" }}>
|
|
19
|
+
<MenuContainer className="shadow-lg text-sm bg-white border border-gray-100 dark:bg-black dark:border-gray-700" style={{ left: x + "px", top: y + "px" }}>
|
|
26
20
|
{children}
|
|
27
21
|
</MenuContainer>
|
|
28
22
|
);
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import Modal from "../modal";
|
|
3
|
+
import { observer } from "mobx-react-lite";
|
|
4
|
+
import { useGlobalStore } from "../../store";
|
|
5
|
+
import DefaultButton from "../button/default";
|
|
6
|
+
import PrimaryButton from "../button/primary";
|
|
7
|
+
import { useTranslation } from "react-i18next";
|
|
8
|
+
import DefaultTab, { ITabOption } from "../tabs/defaultTab";
|
|
9
|
+
|
|
10
|
+
const syntaxHighlight = (json: any) => {
|
|
11
|
+
if (typeof json != "string") {
|
|
12
|
+
json = JSON.stringify(json, undefined, 4);
|
|
13
|
+
}
|
|
14
|
+
json = json
|
|
15
|
+
.replace(/&/g, "&")
|
|
16
|
+
.replace(/</g, "<")
|
|
17
|
+
.replace(/>/g, ">")
|
|
18
|
+
.replace(/\n/g, "<br>")
|
|
19
|
+
.replace(/\t/g, " ")
|
|
20
|
+
.replace(/\s/g, " ");
|
|
21
|
+
return json.replace(
|
|
22
|
+
/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,
|
|
23
|
+
function (match) {
|
|
24
|
+
var cls = "text-sky-500"; // number
|
|
25
|
+
if (/^"/.test(match)) {
|
|
26
|
+
if (/:$/.test(match)) {
|
|
27
|
+
cls = "text-purple-500"; // key
|
|
28
|
+
} else {
|
|
29
|
+
cls = "text-emerald-500"; // string
|
|
30
|
+
}
|
|
31
|
+
} else if (/true|false/.test(match)) {
|
|
32
|
+
cls = "text-blue-500";
|
|
33
|
+
} else if (/null/.test(match)) {
|
|
34
|
+
cls = "text-sky-500";
|
|
35
|
+
}
|
|
36
|
+
return '<span class="' + cls + '">' + match + "</span>";
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const CodeExport: React.FC = observer((props) => {
|
|
42
|
+
const { commonStore, vizStore } = useGlobalStore();
|
|
43
|
+
const { showCodeExportPanel } = commonStore;
|
|
44
|
+
const { t } = useTranslation();
|
|
45
|
+
const [tabKey, setTabKey] = useState<string>("graphic-walker");
|
|
46
|
+
const [code, setCode] = useState<any>("");
|
|
47
|
+
|
|
48
|
+
const specTabs: ITabOption[] = [
|
|
49
|
+
{
|
|
50
|
+
key: "graphic-walker",
|
|
51
|
+
label: "Graphic-Walker",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
key: "vega-lite",
|
|
55
|
+
label: "Vega-Lite",
|
|
56
|
+
disabled: true
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (showCodeExportPanel) {
|
|
62
|
+
if (tabKey === "graphic-walker") {
|
|
63
|
+
const res = vizStore.exportViewSpec();
|
|
64
|
+
setCode(res);
|
|
65
|
+
} else {
|
|
66
|
+
setCode("vega code");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}, [tabKey, showCodeExportPanel]);
|
|
70
|
+
return (
|
|
71
|
+
<Modal
|
|
72
|
+
show={showCodeExportPanel}
|
|
73
|
+
onClose={() => {
|
|
74
|
+
commonStore.setShowCodeExportPanel(false);
|
|
75
|
+
}}
|
|
76
|
+
>
|
|
77
|
+
<div>
|
|
78
|
+
<h1>Code Export</h1>
|
|
79
|
+
<DefaultTab
|
|
80
|
+
tabs={specTabs}
|
|
81
|
+
selectedKey={tabKey}
|
|
82
|
+
onSelected={(k) => {
|
|
83
|
+
setTabKey(k as string);
|
|
84
|
+
}}
|
|
85
|
+
/>
|
|
86
|
+
{tabKey === "graphic-walker" && (
|
|
87
|
+
<div className="text-sm px-6 max-h-56 overflow-auto">
|
|
88
|
+
<div dangerouslySetInnerHTML={{ __html: syntaxHighlight(code) }} />
|
|
89
|
+
</div>
|
|
90
|
+
)}
|
|
91
|
+
<div className="mt-4 flex justify-start">
|
|
92
|
+
<PrimaryButton
|
|
93
|
+
// text={t("actions.confirm")}
|
|
94
|
+
className="mr-2 px-6"
|
|
95
|
+
text="Copy to Clipboard"
|
|
96
|
+
onClick={() => {
|
|
97
|
+
navigator.clipboard.writeText(JSON.stringify(code));
|
|
98
|
+
commonStore.setShowCodeExportPanel(false);
|
|
99
|
+
}}
|
|
100
|
+
/>
|
|
101
|
+
<DefaultButton
|
|
102
|
+
text={t("actions.cancel")}
|
|
103
|
+
className="mr-2 px-6"
|
|
104
|
+
onClick={() => {
|
|
105
|
+
commonStore.setShowCodeExportPanel(false);
|
|
106
|
+
}}
|
|
107
|
+
/>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
</Modal>
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
export default CodeExport;
|
|
@@ -48,15 +48,15 @@ function getHeaderClassNames(field: IMutField) {
|
|
|
48
48
|
function getSemanticColors(field: IMutField): string {
|
|
49
49
|
switch (field.semanticType) {
|
|
50
50
|
case "nominal":
|
|
51
|
-
return "bg-sky-100 text-sky-800";
|
|
51
|
+
return "border border-transparent bg-sky-100 text-sky-800 dark:bg-sky-900 dark:text-sky-100 dark:border-sky-600";
|
|
52
52
|
case "ordinal":
|
|
53
|
-
return "bg-indigo-100 text-indigo-800";
|
|
53
|
+
return "border border-transparent bg-indigo-100 text-indigo-800 dark:bg-indigo-900 dark:text-indigo-100 dark:border-indigo-600";
|
|
54
54
|
case "quantitative":
|
|
55
|
-
return "bg-purple-100 text-purple-800";
|
|
55
|
+
return "border border-transparent bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-100 dark:border-purple-600";
|
|
56
56
|
case "temporal":
|
|
57
|
-
return "bg-yellow-100 text-yellow-800";
|
|
57
|
+
return "border border-transparent bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-100 dark:border-yellow-600";
|
|
58
58
|
default:
|
|
59
|
-
return "bg-gray-400";
|
|
59
|
+
return "border border-transparent bg-gray-400";
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -97,7 +97,7 @@ const DataTable: React.FC<DataTableProps> = (props) => {
|
|
|
97
97
|
/>
|
|
98
98
|
<table className="min-w-full divide-y">
|
|
99
99
|
<thead className="bg-gray-50 dark:bg-gray-900">
|
|
100
|
-
<tr className="divide-x divide-gray-200 dark:divide-gray-
|
|
100
|
+
<tr className="divide-x divide-gray-200 dark:divide-gray-700">
|
|
101
101
|
{metas.map((field, fIndex) => (
|
|
102
102
|
<th key={field.fid} className={""}>
|
|
103
103
|
<div
|
|
@@ -152,9 +152,9 @@ const DataTable: React.FC<DataTableProps> = (props) => {
|
|
|
152
152
|
))}
|
|
153
153
|
</tr>
|
|
154
154
|
</thead>
|
|
155
|
-
<tbody className="divide-y divide-gray-100 dark:divide-gray-
|
|
156
|
-
{data.slice(from, to).map((row, index) => (
|
|
157
|
-
<tr className={"divide-x divide-gray-200 dark:divide-gray-
|
|
155
|
+
<tbody className="divide-y divide-gray-100 dark:divide-gray-700 bg-white dark:bg-zinc-900">
|
|
156
|
+
{data.slice(from, to + 1).map((row, index) => (
|
|
157
|
+
<tr className={"divide-x divide-gray-200 dark:divide-gray-700 " + (index % 2 ? "bg-gray-50 dark:bg-gray-900" : "")} key={index}>
|
|
158
158
|
{metas.map((field) => (
|
|
159
159
|
<td
|
|
160
160
|
key={field.fid + index}
|
|
@@ -163,7 +163,7 @@ const DataTable: React.FC<DataTableProps> = (props) => {
|
|
|
163
163
|
" whitespace-nowrap py-2 pl-4 pr-3 text-xs text-gray-500 dark:text-gray-300 sm:pl-6"
|
|
164
164
|
}
|
|
165
165
|
>
|
|
166
|
-
{row[field.fid]}
|
|
166
|
+
{`${row[field.fid]}`}
|
|
167
167
|
</td>
|
|
168
168
|
))}
|
|
169
169
|
</tr>
|
package/src/components/modal.tsx
CHANGED
|
@@ -25,16 +25,6 @@ const Container = styled.div`
|
|
|
25
25
|
}
|
|
26
26
|
max-height: 800px;
|
|
27
27
|
overflow: auto;
|
|
28
|
-
> div.header {
|
|
29
|
-
background-color: #f0f0f0;
|
|
30
|
-
display: flex;
|
|
31
|
-
padding: 12px;
|
|
32
|
-
font-size: 14px;
|
|
33
|
-
align-items: center;
|
|
34
|
-
@media (prefers-color-scheme: dark) {
|
|
35
|
-
background-color: #000;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
28
|
> div.container {
|
|
39
29
|
padding: 0.5em 1em 1em 1em;
|
|
40
30
|
}
|
|
@@ -42,10 +32,6 @@ const Container = styled.div`
|
|
|
42
32
|
left: 50%;
|
|
43
33
|
top: 50%;
|
|
44
34
|
transform: translate(-50%, -50%);
|
|
45
|
-
background-color: #fff;
|
|
46
|
-
@media (prefers-color-scheme: dark) {
|
|
47
|
-
background-color: #000;
|
|
48
|
-
}
|
|
49
35
|
/* box-shadow: 0px 0px 12px 3px rgba(0, 0, 0, 0.19); */
|
|
50
36
|
border-radius: 4px;
|
|
51
37
|
z-index: 999;
|
|
@@ -74,7 +60,7 @@ const Modal: React.FC<ModalProps> = (props) => {
|
|
|
74
60
|
}
|
|
75
61
|
}}
|
|
76
62
|
>
|
|
77
|
-
<Container role="dialog" className="shadow-lg rounded-md border border-gray-100 dark:border-gray-800" onMouseDown={(e) => e.stopPropagation()}>
|
|
63
|
+
<Container role="dialog" className="bg-white dark:bg-zinc-900 shadow-lg rounded-md border border-gray-100 dark:border-gray-800" onMouseDown={(e) => e.stopPropagation()}>
|
|
78
64
|
<div className="absolute top-0 right-0 hidden pt-4 pr-4 sm:block">
|
|
79
65
|
<button
|
|
80
66
|
type="button"
|
|
@@ -14,7 +14,7 @@ export const ResizeDialog: React.FC<SizeSettingProps> = (props) => {
|
|
|
14
14
|
const { t } = useTranslation("translation", { keyPrefix: "main.tabpanel.settings.size_setting" });
|
|
15
15
|
|
|
16
16
|
return (
|
|
17
|
-
|
|
17
|
+
<div className="text-zinc-400">
|
|
18
18
|
{children}
|
|
19
19
|
<div className="mt-4 w-60">
|
|
20
20
|
<input
|
|
@@ -52,7 +52,7 @@ export const ResizeDialog: React.FC<SizeSettingProps> = (props) => {
|
|
|
52
52
|
{`${t("height")}: ${height}`}
|
|
53
53
|
</output>
|
|
54
54
|
</div>
|
|
55
|
-
|
|
55
|
+
</div>
|
|
56
56
|
);
|
|
57
57
|
};
|
|
58
58
|
|
|
@@ -7,6 +7,7 @@ function classNames(...classes: string[]) {
|
|
|
7
7
|
export interface ITabOption {
|
|
8
8
|
label: string | ReactElement;
|
|
9
9
|
key: string;
|
|
10
|
+
disabled?: boolean;
|
|
10
11
|
}
|
|
11
12
|
interface DefaultProps {
|
|
12
13
|
tabs: ITabOption[];
|
|
@@ -26,14 +27,15 @@ export default function Default(props: DefaultProps) {
|
|
|
26
27
|
role="tab"
|
|
27
28
|
tabIndex={0}
|
|
28
29
|
onClick={() => {
|
|
29
|
-
onSelected(tab.key, tabIndex)
|
|
30
|
+
!tab.disabled && onSelected(tab.key, tabIndex)
|
|
30
31
|
}}
|
|
31
32
|
key={tab.key}
|
|
32
33
|
className={classNames(
|
|
33
34
|
tab.key === selectedKey
|
|
34
35
|
? 'border-indigo-500 text-indigo-600 dark:border-indigo-400 dark:text-indigo-300'
|
|
35
36
|
: 'border-transparent text-gray-500 hover:text-gray-700 dark:hover:text-gray-200 hover:border-gray-300 dark:text-gray-400',
|
|
36
|
-
'whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm cursor-pointer'
|
|
37
|
+
'whitespace-nowrap py-2 px-1 border-b-2 font-medium text-sm cursor-pointer',
|
|
38
|
+
tab.disabled ? 'opacity-50 cursor-not-allowed' : ''
|
|
37
39
|
)}
|
|
38
40
|
>{tab.label}</span>
|
|
39
41
|
))}
|
|
@@ -1,74 +1,109 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useState } from "react";
|
|
2
2
|
import { useTranslation } from "react-i18next";
|
|
3
|
-
|
|
3
|
+
import { PencilSquareIcon } from "@heroicons/react/24/outline";
|
|
4
|
+
import Modal from "../modal";
|
|
5
|
+
import { unstable_batchedUpdates } from "react-dom";
|
|
6
|
+
import DefaultButton from "../button/default";
|
|
7
|
+
import PrimaryButton from "../button/primary";
|
|
4
8
|
|
|
5
9
|
function classNames(...classes: string[]) {
|
|
6
|
-
return classes.filter(Boolean).join(
|
|
10
|
+
return classes.filter(Boolean).join(" ");
|
|
7
11
|
}
|
|
8
12
|
|
|
9
13
|
export interface ITabOption {
|
|
10
14
|
label: string;
|
|
11
15
|
key: string;
|
|
12
|
-
|
|
16
|
+
editable?: boolean;
|
|
13
17
|
}
|
|
14
18
|
interface EditableTabsProps {
|
|
15
19
|
tabs: ITabOption[];
|
|
16
20
|
selectedKey: string;
|
|
17
21
|
onSelected: (selectedKey: string, index: number) => void;
|
|
18
|
-
allowEdit?: boolean;
|
|
19
22
|
onEditLabel?: (label: string, index: number) => void;
|
|
20
23
|
}
|
|
21
24
|
export default function EditableTabs(props: EditableTabsProps) {
|
|
22
|
-
const { tabs, selectedKey, onSelected,
|
|
23
|
-
const [
|
|
25
|
+
const { tabs, selectedKey, onSelected, onEditLabel } = props;
|
|
26
|
+
const [editingIndex, setEditingIndex] = useState<number>(-1);
|
|
27
|
+
const [name, setName] = useState<string>("");
|
|
24
28
|
const { t } = useTranslation();
|
|
25
29
|
|
|
26
|
-
const clearEditStatus = useCallback(() => {
|
|
27
|
-
setEditList(new Array(tabs.length).fill(false))
|
|
28
|
-
}, [tabs.length]);
|
|
29
|
-
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
clearEditStatus();
|
|
32
|
-
}, [clearEditStatus]);
|
|
33
|
-
|
|
34
30
|
return (
|
|
35
|
-
<div className="border-b border-gray-200 dark:border-gray-
|
|
36
|
-
<
|
|
31
|
+
<div className="border-b border-gray-200 dark:border-gray-700 overflow-x-auto overflow-y-hidden">
|
|
32
|
+
<Modal
|
|
33
|
+
show={editingIndex > -1}
|
|
34
|
+
onClose={() => {
|
|
35
|
+
setEditingIndex(-1);
|
|
36
|
+
}}
|
|
37
|
+
>
|
|
38
|
+
<div>
|
|
39
|
+
<span className="block text-sm font-medium leading-6">{t('main.tablist.chart_name')}</span>
|
|
40
|
+
<div className="mt-2">
|
|
41
|
+
<input
|
|
42
|
+
value={name}
|
|
43
|
+
onChange={(e) => {
|
|
44
|
+
setName(e.target.value);
|
|
45
|
+
}}
|
|
46
|
+
type="text"
|
|
47
|
+
name="text"
|
|
48
|
+
className="block w-full rounded-md border-0 px-2 py-1.5 bg-transparent shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
|
|
49
|
+
/>
|
|
50
|
+
</div>
|
|
51
|
+
<div className="mt-4 flex justify-end">
|
|
52
|
+
<DefaultButton
|
|
53
|
+
className="mr-2"
|
|
54
|
+
text={t("actions.cancel")}
|
|
55
|
+
onClick={() => {
|
|
56
|
+
unstable_batchedUpdates(() => {
|
|
57
|
+
setEditingIndex(-1);
|
|
58
|
+
setName("");
|
|
59
|
+
});
|
|
60
|
+
}}
|
|
61
|
+
/>
|
|
62
|
+
<PrimaryButton
|
|
63
|
+
text={t("actions.confirm")}
|
|
64
|
+
onClick={() => {
|
|
65
|
+
unstable_batchedUpdates(() => {
|
|
66
|
+
onEditLabel && onEditLabel(name, editingIndex);
|
|
67
|
+
setEditingIndex(-1);
|
|
68
|
+
setName("");
|
|
69
|
+
});
|
|
70
|
+
}}
|
|
71
|
+
/>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</Modal>
|
|
75
|
+
<nav className="-mb-px flex h-8 border-gray-200 dark:border-gray-700" role="tablist" aria-label="Tabs">
|
|
37
76
|
{tabs.map((tab, tabIndex) => (
|
|
38
77
|
<span
|
|
39
78
|
role="tab"
|
|
40
79
|
tabIndex={0}
|
|
41
|
-
dangerouslySetInnerHTML={{
|
|
42
|
-
|
|
43
|
-
}}
|
|
80
|
+
// dangerouslySetInnerHTML={{
|
|
81
|
+
// __html: tab.label
|
|
82
|
+
// }}
|
|
44
83
|
onClick={() => {
|
|
45
|
-
onSelected(tab.key, tabIndex)
|
|
46
|
-
}}
|
|
47
|
-
onDoubleClick={() => {
|
|
48
|
-
setEditList(v => {
|
|
49
|
-
const nv = [...v];
|
|
50
|
-
nv[tabIndex] = true;
|
|
51
|
-
return nv
|
|
52
|
-
})
|
|
53
|
-
}}
|
|
54
|
-
contentEditable={editList[tabIndex]}
|
|
55
|
-
onInput={(e) => {
|
|
56
|
-
onEditLabel && onEditLabel(`${e.currentTarget.textContent}`, tabIndex)
|
|
57
|
-
}}
|
|
58
|
-
onKeyDown={(e) => {
|
|
59
|
-
if (e.key === 'Enter') {
|
|
60
|
-
clearEditStatus();
|
|
61
|
-
e.preventDefault();
|
|
62
|
-
}
|
|
84
|
+
onSelected(tab.key, tabIndex);
|
|
63
85
|
}}
|
|
64
86
|
key={tab.key}
|
|
65
87
|
className={classNames(
|
|
66
88
|
tab.key === selectedKey
|
|
67
89
|
? "border rounded-t"
|
|
68
90
|
: "text-gray-500 dark:text-gray-400 hover:text-gray-700 hover:bg-gray-50 dark:hover:text-gray-200 dark:hover:bg-gray-800",
|
|
69
|
-
"whitespace-nowrap border-gray-200 dark:border-gray-700 py-1 px-2 pr-6 text-sm cursor-
|
|
91
|
+
"whitespace-nowrap border-gray-200 dark:border-gray-700 py-1 px-2 pr-6 text-sm cursor-default dark:text-white"
|
|
92
|
+
)}
|
|
93
|
+
>
|
|
94
|
+
{tab.label}{" "}
|
|
95
|
+
{tab.key === selectedKey && tab.editable && (
|
|
96
|
+
<PencilSquareIcon
|
|
97
|
+
className="w-3 inline cursor-pointer"
|
|
98
|
+
onClick={() => {
|
|
99
|
+
unstable_batchedUpdates(() => {
|
|
100
|
+
setEditingIndex(tabIndex);
|
|
101
|
+
setName(tab.label);
|
|
102
|
+
});
|
|
103
|
+
}}
|
|
104
|
+
/>
|
|
70
105
|
)}
|
|
71
|
-
|
|
106
|
+
</span>
|
|
72
107
|
))}
|
|
73
108
|
</nav>
|
|
74
109
|
</div>
|