@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.
- package/dist/App.d.ts +3 -2
- package/dist/components/callout.d.ts +2 -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 +3 -0
- package/dist/components/tooltip.d.ts +2 -0
- package/dist/fields/components.d.ts +0 -1
- package/dist/fields/filterField/filterEditDialog.d.ts +1 -1
- package/dist/graphic-walker.es.js +15103 -14997
- package/dist/graphic-walker.es.js.map +1 -1
- package/dist/graphic-walker.umd.js +134 -264
- package/dist/graphic-walker.umd.js.map +1 -1
- package/dist/interfaces.d.ts +2 -0
- package/dist/renderer/index.d.ts +6 -4
- package/dist/utils/media.d.ts +2 -1
- package/dist/vis/react-vega.d.ts +4 -2
- package/dist/vis/theme.d.ts +36 -20
- package/dist/visualSettings/index.d.ts +2 -1
- package/package.json +1 -1
- package/src/App.tsx +19 -14
- package/src/components/callout.tsx +9 -7
- package/src/components/clickMenu.tsx +1 -7
- package/src/components/modal.tsx +1 -15
- package/src/components/sizeSetting.tsx +2 -2
- package/src/components/tabs/editableTab.tsx +2 -2
- 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/csvData.tsx +1 -1
- package/src/dataSource/index.tsx +2 -3
- package/src/fields/components.tsx +13 -50
- package/src/fields/datasetFields/index.tsx +3 -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/insightBoard/mainBoard.tsx +9 -2
- package/src/interfaces.ts +4 -1
- package/src/locales/en-US.json +5 -2
- package/src/locales/i18n.ts +7 -0
- package/src/locales/ja-JP.json +195 -0
- package/src/locales/zh-CN.json +5 -2
- package/src/renderer/index.tsx +96 -71
- package/src/utils/media.ts +16 -11
- package/src/vis/react-vega.tsx +18 -3
- package/src/vis/theme.ts +23 -25
- package/src/visualSettings/index.tsx +12 -32
- package/dist/components/container.d.ts +0 -2
- package/src/components/container.tsx +0 -25
package/dist/interfaces.d.ts
CHANGED
package/dist/renderer/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import { IReactVegaHandler } from
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { IReactVegaHandler } from "../vis/react-vega";
|
|
3
|
+
import { IDarkMode, IThemeKey } from "../interfaces";
|
|
3
4
|
declare const _default: React.MemoExoticComponent<React.ForwardRefExoticComponent<Pick<{
|
|
4
|
-
themeKey?:
|
|
5
|
-
|
|
5
|
+
themeKey?: IThemeKey | undefined;
|
|
6
|
+
dark?: IDarkMode | undefined;
|
|
7
|
+
} & React.RefAttributes<IReactVegaHandler>, "dark" | "key" | "themeKey"> & React.RefAttributes<IReactVegaHandler>>>;
|
|
6
8
|
export default _default;
|
package/dist/utils/media.d.ts
CHANGED
package/dist/vis/react-vega.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { IViewField, IRow, IStackMode } from '../interfaces';
|
|
2
|
+
import { IViewField, IRow, IStackMode, IDarkMode, IThemeKey } from '../interfaces';
|
|
3
3
|
export interface IReactVegaHandler {
|
|
4
4
|
getSVGData: () => Promise<string[]>;
|
|
5
5
|
getCanvasData: () => Promise<string[]>;
|
|
@@ -28,7 +28,8 @@ interface ReactVegaProps {
|
|
|
28
28
|
selectEncoding: SingleViewProps['selectEncoding'];
|
|
29
29
|
brushEncoding: SingleViewProps['brushEncoding'];
|
|
30
30
|
/** @default "vega" */
|
|
31
|
-
themeKey?:
|
|
31
|
+
themeKey?: IThemeKey;
|
|
32
|
+
dark?: IDarkMode;
|
|
32
33
|
}
|
|
33
34
|
interface SingleViewProps {
|
|
34
35
|
x: IViewField;
|
|
@@ -50,6 +51,7 @@ interface SingleViewProps {
|
|
|
50
51
|
asCrossFilterTrigger: boolean;
|
|
51
52
|
selectEncoding: 'default' | 'none';
|
|
52
53
|
brushEncoding: 'x' | 'y' | 'default' | 'none';
|
|
54
|
+
hideLegend?: boolean;
|
|
53
55
|
}
|
|
54
56
|
declare const ReactVega: React.ForwardRefExoticComponent<ReactVegaProps & React.RefAttributes<IReactVegaHandler>>;
|
|
55
57
|
export default ReactVega;
|
package/dist/vis/theme.d.ts
CHANGED
|
@@ -3,16 +3,24 @@ export declare const VegaTheme: {
|
|
|
3
3
|
readonly background: "transparent";
|
|
4
4
|
};
|
|
5
5
|
readonly dark: {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
readonly tickColor: "#d1d5db";
|
|
11
|
-
readonly labelColor: "#d1d5db";
|
|
6
|
+
background: string;
|
|
7
|
+
header: {
|
|
8
|
+
titleColor: string;
|
|
9
|
+
labelColor: string;
|
|
12
10
|
};
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
axis: {
|
|
12
|
+
gridColor: string;
|
|
13
|
+
domainColor: string;
|
|
14
|
+
tickColor: string;
|
|
15
|
+
labelColor: string;
|
|
16
|
+
titleColor: string;
|
|
17
|
+
};
|
|
18
|
+
legend: {
|
|
19
|
+
labelColor: string;
|
|
20
|
+
titleColor: string;
|
|
21
|
+
};
|
|
22
|
+
view: {
|
|
23
|
+
stroke: string;
|
|
16
24
|
};
|
|
17
25
|
};
|
|
18
26
|
};
|
|
@@ -98,17 +106,6 @@ export declare const AntVTheme: {
|
|
|
98
106
|
readonly arc: {
|
|
99
107
|
readonly fill: "#5B8FF9";
|
|
100
108
|
};
|
|
101
|
-
readonly background: "transparent";
|
|
102
|
-
readonly axis: {
|
|
103
|
-
readonly gridColor: "#666";
|
|
104
|
-
readonly domainColor: "#d1d5db";
|
|
105
|
-
readonly tickColor: "#d1d5db";
|
|
106
|
-
readonly labelColor: "#d1d5db";
|
|
107
|
-
};
|
|
108
|
-
readonly legend: {
|
|
109
|
-
readonly labelColor: "#d1d5db";
|
|
110
|
-
readonly titleColor: "#d1d5db";
|
|
111
|
-
};
|
|
112
109
|
readonly range: {
|
|
113
110
|
readonly category: readonly ["#5B8FF9", "#61DDAA", "#65789B", "#F6BD16", "#7262FD", "#78D3F8", "#9661BC", "#F6903D", "#008685", "#F08BB4"];
|
|
114
111
|
readonly diverging: readonly ["#7b3294", "#c2a5cf", "#f7f7f7", "#a6dba0", "#008837"];
|
|
@@ -120,6 +117,25 @@ export declare const AntVTheme: {
|
|
|
120
117
|
readonly range: readonly ["#f7fbff", "#08306b"];
|
|
121
118
|
};
|
|
122
119
|
};
|
|
120
|
+
readonly background: string;
|
|
121
|
+
readonly header: {
|
|
122
|
+
titleColor: string;
|
|
123
|
+
labelColor: string;
|
|
124
|
+
};
|
|
125
|
+
readonly axis: {
|
|
126
|
+
gridColor: string;
|
|
127
|
+
domainColor: string;
|
|
128
|
+
tickColor: string;
|
|
129
|
+
labelColor: string;
|
|
130
|
+
titleColor: string;
|
|
131
|
+
};
|
|
132
|
+
readonly legend: {
|
|
133
|
+
labelColor: string;
|
|
134
|
+
titleColor: string;
|
|
135
|
+
};
|
|
136
|
+
readonly view: {
|
|
137
|
+
stroke: string;
|
|
138
|
+
};
|
|
123
139
|
};
|
|
124
140
|
};
|
|
125
141
|
export declare const builtInThemes: {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { IDarkMode } from '../interfaces';
|
|
2
3
|
import { IReactVegaHandler } from '../vis/react-vega';
|
|
3
|
-
export declare const LiteContainer: import("styled-components").StyledComponent<"div", any, {}, never>;
|
|
4
4
|
interface IVisualSettings {
|
|
5
|
+
darkModePreference: IDarkMode;
|
|
5
6
|
rendererHandler?: React.RefObject<IReactVegaHandler>;
|
|
6
7
|
}
|
|
7
8
|
declare const _default: React.FunctionComponent<IVisualSettings>;
|
package/package.json
CHANGED
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";
|
|
@@ -23,6 +22,7 @@ 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
26
|
|
|
27
27
|
export interface IGWProps {
|
|
28
28
|
dataSource?: IRow[];
|
|
@@ -37,7 +37,8 @@ export interface IGWProps {
|
|
|
37
37
|
*/
|
|
38
38
|
fieldKeyGuard?: boolean;
|
|
39
39
|
/** @default "vega" */
|
|
40
|
-
themeKey?:
|
|
40
|
+
themeKey?: IThemeKey;
|
|
41
|
+
dark?: IDarkMode;
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
const App = observer<IGWProps>(function App (props) {
|
|
@@ -50,6 +51,7 @@ const App = observer<IGWProps>(function App (props) {
|
|
|
50
51
|
hideDataSourceConfig,
|
|
51
52
|
fieldKeyGuard = true,
|
|
52
53
|
themeKey = 'vega',
|
|
54
|
+
dark = 'media',
|
|
53
55
|
} = props;
|
|
54
56
|
const { commonStore, vizStore } = useGlobalStore();
|
|
55
57
|
const [insightReady, setInsightReady] = useState<boolean>(true);
|
|
@@ -117,14 +119,16 @@ const App = observer<IGWProps>(function App (props) {
|
|
|
117
119
|
};
|
|
118
120
|
}, [currentDataset, spec]);
|
|
119
121
|
|
|
122
|
+
const darkMode = useCurrentMediaTheme(dark);
|
|
123
|
+
|
|
120
124
|
const rendererRef = useRef<IReactVegaHandler>(null);
|
|
121
125
|
|
|
122
126
|
return (
|
|
123
|
-
<div className=
|
|
127
|
+
<div className={`${darkMode === 'dark' ? 'dark' : ''} App font-sans bg-white dark:bg-zinc-900 dark:text-white m-0 p-0`}>
|
|
124
128
|
{/* <div className="grow-0">
|
|
125
129
|
<PageNav />
|
|
126
130
|
</div> */}
|
|
127
|
-
<div className="">
|
|
131
|
+
<div className="bg-white dark:bg-zinc-900 dark:text-white">
|
|
128
132
|
{!hideDataSourceConfig && <DataSourceSegment preWorkDone={insightReady} />}
|
|
129
133
|
<div className="px-2 mx-2">
|
|
130
134
|
<SegmentNav />
|
|
@@ -133,8 +137,8 @@ const App = observer<IGWProps>(function App (props) {
|
|
|
133
137
|
}
|
|
134
138
|
</div>
|
|
135
139
|
{segmentKey === ISegmentKey.vis && (
|
|
136
|
-
<
|
|
137
|
-
<VisualSettings rendererHandler={rendererRef} />
|
|
140
|
+
<div style={{ marginTop: "0em", borderTop: "none" }} className="m-4 p-4 border border-gray-200 dark:border-gray-700">
|
|
141
|
+
<VisualSettings rendererHandler={rendererRef} darkModePreference={dark} />
|
|
138
142
|
<div className="md:grid md:grid-cols-12 xl:grid-cols-6">
|
|
139
143
|
<div className="md:col-span-3 xl:col-span-1">
|
|
140
144
|
<DatasetFields />
|
|
@@ -147,7 +151,8 @@ const App = observer<IGWProps>(function App (props) {
|
|
|
147
151
|
<div>
|
|
148
152
|
<PosFields />
|
|
149
153
|
</div>
|
|
150
|
-
<
|
|
154
|
+
<div
|
|
155
|
+
className="m-0.5 p-1 border border-gray-200 dark:border-gray-700"
|
|
151
156
|
style={{ minHeight: "600px", overflow: "auto" }}
|
|
152
157
|
onMouseLeave={() => {
|
|
153
158
|
vizEmbededMenu.show && commonStore.closeEmbededMenu();
|
|
@@ -156,12 +161,12 @@ const App = observer<IGWProps>(function App (props) {
|
|
|
156
161
|
vizEmbededMenu.show && commonStore.closeEmbededMenu();
|
|
157
162
|
}}
|
|
158
163
|
>
|
|
159
|
-
{datasets.length > 0 && <ReactiveRenderer ref={rendererRef} themeKey={themeKey} />}
|
|
164
|
+
{datasets.length > 0 && <ReactiveRenderer ref={rendererRef} themeKey={themeKey} dark={dark} />}
|
|
160
165
|
<InsightBoard />
|
|
161
166
|
{vizEmbededMenu.show && (
|
|
162
167
|
<ClickMenu x={vizEmbededMenu.position[0]} y={vizEmbededMenu.position[1]}>
|
|
163
168
|
<div
|
|
164
|
-
className="flex items-center whitespace-nowrap py-1 px-4 hover:bg-gray-100"
|
|
169
|
+
className="flex items-center whitespace-nowrap py-1 px-4 hover:bg-gray-100 dark:hover:bg-gray-800 cursor-pointer"
|
|
165
170
|
onClick={() => {
|
|
166
171
|
commonStore.closeEmbededMenu();
|
|
167
172
|
commonStore.setShowInsightBoard(true);
|
|
@@ -174,15 +179,15 @@ const App = observer<IGWProps>(function App (props) {
|
|
|
174
179
|
</div>
|
|
175
180
|
</ClickMenu>
|
|
176
181
|
)}
|
|
177
|
-
</
|
|
182
|
+
</div>
|
|
178
183
|
</div>
|
|
179
184
|
</div>
|
|
180
|
-
</
|
|
185
|
+
</div>
|
|
181
186
|
)}
|
|
182
187
|
{segmentKey === ISegmentKey.data && (
|
|
183
|
-
<
|
|
188
|
+
<div className="m-4 p-4 border border-gray-200 dark:border-gray-700" style={{ marginTop: "0em", borderTop: "none" }}>
|
|
184
189
|
<DatasetConfig />
|
|
185
|
-
</
|
|
190
|
+
</div>
|
|
186
191
|
)}
|
|
187
192
|
</div>
|
|
188
193
|
</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
|
);
|
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
|
|
|
@@ -32,8 +32,8 @@ export default function EditableTabs(props: EditableTabsProps) {
|
|
|
32
32
|
}, [clearEditStatus]);
|
|
33
33
|
|
|
34
34
|
return (
|
|
35
|
-
<div className="border-b border-gray-200 dark:border-gray-
|
|
36
|
-
<nav className="-mb-px flex h-8 border-gray-200 dark:border-gray-
|
|
35
|
+
<div className="border-b border-gray-200 dark:border-gray-700 overflow-x-auto overflow-y-hidden" onMouseLeave={clearEditStatus}>
|
|
36
|
+
<nav className="-mb-px flex h-8 border-gray-200 dark:border-gray-700" role="tablist" aria-label="Tabs">
|
|
37
37
|
{tabs.map((tab, tabIndex) => (
|
|
38
38
|
<span
|
|
39
39
|
role="tab"
|
|
@@ -39,21 +39,15 @@ export const useHandlers = (action: () => void, disabled: boolean, triggerKeys:
|
|
|
39
39
|
}), [allowPropagation]);
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
-
export const ToolbarContainer = styled.div
|
|
42
|
+
export const ToolbarContainer = styled.div<{ dark: boolean }>`
|
|
43
43
|
--height: 36px;
|
|
44
44
|
--icon-size: 18px;
|
|
45
45
|
width: 100%;
|
|
46
46
|
height: var(--height);
|
|
47
|
-
background-color: var(--background-color);
|
|
48
|
-
color: var(--color);
|
|
47
|
+
background-color: ${({ dark }) => dark ? 'var(--background-color-dark)' : 'var(--background-color)'};
|
|
48
|
+
color: ${({ dark }) => dark ? 'var(--color-dark)' : 'var(--color)'};
|
|
49
49
|
border: 1px solid;
|
|
50
|
-
border-color: #e5e7eb;
|
|
51
|
-
// dark mode
|
|
52
|
-
@media (prefers-color-scheme: dark) {
|
|
53
|
-
background-color: var(--background-color-dark);
|
|
54
|
-
color: var(--color-dark);
|
|
55
|
-
border-color: #4b5563;
|
|
56
|
-
}
|
|
50
|
+
border-color: ${({ dark }) => dark ? '#4b5563' : '#e5e7eb'};
|
|
57
51
|
/* box-shadow: 0px 1px 3px 1px rgba(136, 136, 136, 0.1); */
|
|
58
52
|
border-radius: 2px;
|
|
59
53
|
overflow: hidden;
|
|
@@ -73,7 +67,7 @@ export const ToolbarSplitter = styled.div`
|
|
|
73
67
|
background: #bbbbbb50;
|
|
74
68
|
`;
|
|
75
69
|
|
|
76
|
-
export const ToolbarItemContainerElement = styled.div<{ split: boolean }>`
|
|
70
|
+
export const ToolbarItemContainerElement = styled.div<{ split: boolean; dark: boolean }>`
|
|
77
71
|
display: inline-flex;
|
|
78
72
|
flex-direction: row;
|
|
79
73
|
user-select: none;
|
|
@@ -81,7 +75,7 @@ export const ToolbarItemContainerElement = styled.div<{ split: boolean }>`
|
|
|
81
75
|
width: ${({ split }) => split ? 'calc(var(--height) + 10px)' : 'var(--height)'};
|
|
82
76
|
height: var(--height);
|
|
83
77
|
overflow: hidden;
|
|
84
|
-
color: var(--color);
|
|
78
|
+
color: ${({ dark }) => dark ? 'var(--dark-mode-color)' : 'var(--color)'};
|
|
85
79
|
position: relative;
|
|
86
80
|
> svg {
|
|
87
81
|
flex-grow: 0;
|
|
@@ -102,8 +96,8 @@ export const ToolbarItemContainerElement = styled.div<{ split: boolean }>`
|
|
|
102
96
|
&[aria-disabled=false] {
|
|
103
97
|
cursor: pointer;
|
|
104
98
|
:hover, :focus, &.open {
|
|
105
|
-
--background-color: #FEFEFE;
|
|
106
|
-
color: var(--color-hover);
|
|
99
|
+
--background-color: ${({ dark }) => dark ? '#202020' : '#FEFEFE'};
|
|
100
|
+
color: ${({ dark }) => dark ? 'var(--dark-mode-color-hover)' : 'var(--color-hover)'};
|
|
107
101
|
&.split * svg {
|
|
108
102
|
pointer-events: none;
|
|
109
103
|
transform: translate(-50%, -20%);
|
|
@@ -114,14 +108,5 @@ export const ToolbarItemContainerElement = styled.div<{ split: boolean }>`
|
|
|
114
108
|
background-color: var(--background-color);
|
|
115
109
|
}
|
|
116
110
|
}
|
|
117
|
-
@media (prefers-color-scheme: dark) {
|
|
118
|
-
color: var(--dark-mode-color);
|
|
119
|
-
&[aria-disabled=false] {
|
|
120
|
-
:hover, :focus, &.open {
|
|
121
|
-
--background-color: #202020;
|
|
122
|
-
color: var(--dark-mode-color-hover);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
111
|
transition: color 100ms, background-image 100ms;
|
|
127
112
|
`;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import React, { CSSProperties, memo, ReactNode, useState } from "react";
|
|
2
2
|
import styled from "styled-components";
|
|
3
|
+
import type { IDarkMode } from "../../interfaces";
|
|
4
|
+
import { useCurrentMediaTheme } from "../../utils/media";
|
|
3
5
|
import { ToolbarContainer, ToolbarSplitter } from "./components";
|
|
4
6
|
import ToolbarItem, { ToolbarItemProps, ToolbarItemSplitter } from "./toolbar-item";
|
|
5
7
|
|
|
6
8
|
|
|
7
|
-
const Root = styled.div
|
|
9
|
+
const Root = styled.div<{ darkModePreference: IDarkMode }>`
|
|
8
10
|
width: 100%;
|
|
9
11
|
--background-color: #f7f7f7;
|
|
10
12
|
--color: #777;
|
|
@@ -16,9 +18,11 @@ const Root = styled.div`
|
|
|
16
18
|
--dark-mode-color-hover: #ccc;
|
|
17
19
|
--dark-mode-blue: #282958;
|
|
18
20
|
--dark-mode-blue-dark: #1d1e38;
|
|
21
|
+
--dark-mode-preference: ${({ darkModePreference }) => darkModePreference};
|
|
19
22
|
`;
|
|
20
23
|
|
|
21
24
|
export interface ToolbarProps {
|
|
25
|
+
darkModePreference?: IDarkMode;
|
|
22
26
|
items: ToolbarItemProps[];
|
|
23
27
|
styles?: Partial<{
|
|
24
28
|
root: CSSProperties & Record<string, string>;
|
|
@@ -29,13 +33,15 @@ export interface ToolbarProps {
|
|
|
29
33
|
}>;
|
|
30
34
|
}
|
|
31
35
|
|
|
32
|
-
const Toolbar = memo<ToolbarProps>(function Toolbar ({ items, styles }) {
|
|
36
|
+
const Toolbar = memo<ToolbarProps>(function Toolbar ({ darkModePreference = 'media', items, styles }) {
|
|
33
37
|
const [openedKey, setOpenedKey] = useState<string | null>(null);
|
|
34
38
|
const [slot, setSlot] = useState<ReactNode>(null);
|
|
35
39
|
|
|
40
|
+
const dark = useCurrentMediaTheme(darkModePreference) === 'dark';
|
|
41
|
+
|
|
36
42
|
return (
|
|
37
|
-
<Root style={styles?.root}>
|
|
38
|
-
<ToolbarContainer style={styles?.container}>
|
|
43
|
+
<Root darkModePreference={darkModePreference} style={styles?.root}>
|
|
44
|
+
<ToolbarContainer dark={dark} style={styles?.container}>
|
|
39
45
|
{items.map((item, i) => {
|
|
40
46
|
if (item === ToolbarItemSplitter) {
|
|
41
47
|
return <ToolbarSplitter key={i} />;
|
|
@@ -48,6 +54,7 @@ const Toolbar = memo<ToolbarProps>(function Toolbar ({ items, styles }) {
|
|
|
48
54
|
openedKey={openedKey}
|
|
49
55
|
setOpenedKey={setOpenedKey}
|
|
50
56
|
renderSlot={node => setSlot(node)}
|
|
57
|
+
darkModePreference={darkModePreference}
|
|
51
58
|
/>
|
|
52
59
|
);
|
|
53
60
|
})}
|
|
@@ -8,7 +8,7 @@ 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
|
|
|
@@ -22,6 +22,7 @@ const ToolbarButton = memo<IToolbarProps<ToolbarButtonItem>>(function ToolbarBut
|
|
|
22
22
|
<ToolbarItemContainer
|
|
23
23
|
props={props}
|
|
24
24
|
handlers={onClick ? handlers : null}
|
|
25
|
+
darkModePreference={darkModePreference}
|
|
25
26
|
>
|
|
26
27
|
<Icon style={mergedIconStyles} />
|
|
27
28
|
</ToolbarItemContainer>
|
|
@@ -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,10 +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
|
-
@media (prefers-color-scheme: dark) {
|
|
39
|
-
background-color: #000;
|
|
40
|
-
}
|
|
38
|
+
background-color: ${({ dark }) => dark ? '#000' : '#fff'};
|
|
41
39
|
`;
|
|
42
40
|
|
|
43
41
|
export interface IToolbarItem {
|
|
@@ -65,6 +63,7 @@ export type ToolbarItemProps = (
|
|
|
65
63
|
|
|
66
64
|
export interface IToolbarProps<P extends Exclude<ToolbarItemProps, typeof ToolbarItemSplitter> = Exclude<ToolbarItemProps, typeof ToolbarItemSplitter>> {
|
|
67
65
|
item: P;
|
|
66
|
+
darkModePreference: NonNullable<ToolbarProps['darkModePreference']>;
|
|
68
67
|
styles?: ToolbarProps['styles'];
|
|
69
68
|
openedKey: string | null;
|
|
70
69
|
setOpenedKey: (key: string | null) => void;
|
|
@@ -74,6 +73,7 @@ export interface IToolbarProps<P extends Exclude<ToolbarItemProps, typeof Toolba
|
|
|
74
73
|
let idFlag = 0;
|
|
75
74
|
|
|
76
75
|
export const ToolbarItemContainer = memo<{
|
|
76
|
+
darkModePreference: NonNullable<ToolbarProps['darkModePreference']>;
|
|
77
77
|
props: IToolbarProps;
|
|
78
78
|
handlers: ReturnType<typeof useHandlers> | null;
|
|
79
79
|
children: unknown;
|
|
@@ -84,6 +84,7 @@ export const ToolbarItemContainer = memo<{
|
|
|
84
84
|
styles, openedKey, setOpenedKey, renderSlot,
|
|
85
85
|
},
|
|
86
86
|
handlers,
|
|
87
|
+
darkModePreference,
|
|
87
88
|
children,
|
|
88
89
|
...props
|
|
89
90
|
}
|
|
@@ -130,15 +131,18 @@ export const ToolbarItemContainer = memo<{
|
|
|
130
131
|
|
|
131
132
|
useEffect(() => {
|
|
132
133
|
if (opened && menu) {
|
|
133
|
-
renderSlot(<Toolbar {...menu} />);
|
|
134
|
+
renderSlot(<Toolbar {...menu} darkModePreference={darkModePreference} />);
|
|
134
135
|
return () => renderSlot(null);
|
|
135
136
|
}
|
|
136
137
|
}, [opened, menu, renderSlot]);
|
|
137
138
|
|
|
139
|
+
const dark = useCurrentMediaTheme(darkModePreference) === 'dark';
|
|
140
|
+
|
|
138
141
|
return (
|
|
139
142
|
<>
|
|
140
|
-
<Tooltip content={label}>
|
|
143
|
+
<Tooltip content={label} darkModePreference={darkModePreference}>
|
|
141
144
|
<ToolbarItemContainerElement
|
|
145
|
+
dark={dark}
|
|
142
146
|
role="button" tabIndex={disabled ? undefined : 0} aria-label={label} aria-disabled={disabled ?? false}
|
|
143
147
|
split={Boolean(form || menu)}
|
|
144
148
|
style={styles?.item}
|
|
@@ -190,8 +194,8 @@ export const ToolbarItemContainer = memo<{
|
|
|
190
194
|
</ToolbarItemContainerElement>
|
|
191
195
|
</Tooltip>
|
|
192
196
|
{opened && form && (
|
|
193
|
-
<Callout target={`#${id}`}>
|
|
194
|
-
<FormContainer onMouseDown={e => e.stopPropagation()}>
|
|
197
|
+
<Callout target={`#${id}`} darkModePreference={darkModePreference}>
|
|
198
|
+
<FormContainer dark={dark} onMouseDown={e => e.stopPropagation()}>
|
|
195
199
|
{form}
|
|
196
200
|
</FormContainer>
|
|
197
201
|
</Callout>
|
|
@@ -202,20 +206,21 @@ export const ToolbarItemContainer = memo<{
|
|
|
202
206
|
|
|
203
207
|
const ToolbarItem = memo<{
|
|
204
208
|
item: ToolbarItemProps;
|
|
209
|
+
darkModePreference: NonNullable<ToolbarProps['darkModePreference']>;
|
|
205
210
|
styles?: ToolbarProps['styles'];
|
|
206
211
|
openedKey: string | null;
|
|
207
212
|
setOpenedKey: (key: string | null) => void;
|
|
208
213
|
renderSlot: (node: ReactNode) => void;
|
|
209
|
-
}>(function ToolbarItem ({ item, styles, openedKey, setOpenedKey, renderSlot }) {
|
|
214
|
+
}>(function ToolbarItem ({ item, styles, openedKey, setOpenedKey, renderSlot, darkModePreference }) {
|
|
210
215
|
if (item === ToolbarItemSplitter) {
|
|
211
216
|
return <ToolbarSplitter />;
|
|
212
217
|
}
|
|
213
218
|
if ('checked' in item) {
|
|
214
|
-
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} />;
|
|
215
220
|
} else if ('options' in item) {
|
|
216
|
-
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} />;
|
|
217
222
|
}
|
|
218
|
-
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} />;
|
|
219
224
|
});
|
|
220
225
|
|
|
221
226
|
|