Skip to content

Commit 5e2be64

Browse files
authored
feat: Theme Selector (#1661)
- Added a theme selector to settings menu when there are multiple themes available - IrisGrid and Monaco now respond dynamically to selected theme change - Misc moving of theme utils - Added vite config for local plugin dev resolves #1660
1 parent 4c0200e commit 5e2be64

37 files changed

+459
-233
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,4 @@ packages/*/package-lock.json
4141
/playwright/.cache/
4242
/tests/*-snapshots/*
4343
!/tests/*-snapshots/*-linux*
44+
vite.config.local.ts

README.md

+39
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,45 @@ If your DHC address is different from the default `http://localhost:10000`, edit
5252
VITE_PROXY_URL=http://<dhc-host>:<port>
5353
```
5454

55+
## Local Plugin Development
56+
The plugins repo supports [serving plugins locally](https://github.com/deephaven/deephaven-plugins/blob/main/README.md#serve-plugins). DHC can be configured to proxy `js-plugins`requests to the local dev server by setting `VITE_JS_PLUGINS_DEV_PORT` in `packages/code-studio/.env.development.local`.
57+
58+
e.g. To point to the default dev port:
59+
60+
```
61+
VITE_JS_PLUGINS_DEV_PORT=4100
62+
```
63+
64+
## Local Vite Config
65+
If you'd like to override the vite config for local dev, you can define a `packages/code-studio/vite.config.local.ts` file that extends from `vite.config.ts`. This file is excluded via `.gitignore` which makes it easy to keep local overrides in tact.
66+
67+
The config can be used by running:
68+
69+
`npm run start:app -- -- -- --config=vite.config.local.ts`
70+
71+
For example, to proxy `js-plugins` requests to a local server, you could use this `vite.config.local.ts`:
72+
73+
```typescript
74+
export default defineConfig((config: ConfigEnv) => {
75+
const baseConfig = (createBaseConfig as UserConfigFn)(config) as UserConfig;
76+
77+
return {
78+
...baseConfig,
79+
server: {
80+
...baseConfig.server,
81+
proxy: {
82+
...baseConfig.server?.proxy,
83+
'/js-plugins': {
84+
target: 'http://localhost:5173',
85+
changeOrigin: true,
86+
rewrite: path => path.replace(/^\/js-plugins/, ''),
87+
},
88+
},
89+
},
90+
};
91+
});
92+
```
93+
5594
## Debugging from VSCode
5695

5796
We have a pre-defined launch config that lets you set breakpoints directly in VSCode for debugging browser code. The `Launch Deephaven` config will launch a new Chrome window that stores its data in your repo workspace. With this setup, you only need to install the React and Redux devtool extensions once. They will persist to future launches using the launch config.

package-lock.json

+4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/app-utils/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"@deephaven/auth-plugins": "file:../auth-plugins",
3333
"@deephaven/chart": "file:../chart",
3434
"@deephaven/components": "file:../components",
35+
"@deephaven/console": "file:../console",
3536
"@deephaven/dashboard": "file:../dashboard",
3637
"@deephaven/icons": "file:../icons",
3738
"@deephaven/iris-grid": "file:../iris-grid",

packages/app-utils/src/components/AppBootstrap.test.tsx

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
import React, { useContext } from 'react';
2+
import { act, render, screen } from '@testing-library/react';
23
import { AUTH_HANDLER_TYPE_ANONYMOUS } from '@deephaven/auth-plugins';
34
import { ApiContext } from '@deephaven/jsapi-bootstrap';
5+
import { PluginModuleMap, PluginsContext } from '@deephaven/plugin';
46
import { BROADCAST_LOGIN_MESSAGE } from '@deephaven/jsapi-utils';
57
import type {
68
CoreClient,
79
IdeConnection,
810
dh as DhType,
911
} from '@deephaven/jsapi-types';
1012
import { TestUtils } from '@deephaven/utils';
11-
import { act, render, screen } from '@testing-library/react';
1213
import AppBootstrap from './AppBootstrap';
13-
import { PluginsContext } from './PluginsBootstrap';
14-
import { PluginModuleMap } from '../plugins';
1514

1615
const { asMock } = TestUtils;
1716

packages/app-utils/src/components/ThemeBootstrap.tsx

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { useContext, useMemo } from 'react';
22
import { ChartThemeProvider } from '@deephaven/chart';
3+
import { MonacoThemeProvider } from '@deephaven/console';
34
import { ThemeProvider } from '@deephaven/components';
4-
import { PluginsContext } from '@deephaven/plugin';
5-
import { getThemeDataFromPlugins } from '../plugins';
5+
import { IrisGridThemeProvider } from '@deephaven/iris-grid';
6+
import { getThemeDataFromPlugins, PluginsContext } from '@deephaven/plugin';
67

78
export interface ThemeBootstrapProps {
89
children: React.ReactNode;
@@ -22,7 +23,11 @@ export function ThemeBootstrap({ children }: ThemeBootstrapProps): JSX.Element {
2223

2324
return (
2425
<ThemeProvider themes={themes}>
25-
<ChartThemeProvider>{children}</ChartThemeProvider>
26+
<ChartThemeProvider>
27+
<MonacoThemeProvider>
28+
<IrisGridThemeProvider>{children}</IrisGridThemeProvider>
29+
</MonacoThemeProvider>
30+
</ChartThemeProvider>
2631
</ThemeProvider>
2732
);
2833
}

packages/app-utils/src/plugins/PluginUtils.test.tsx

-104
This file was deleted.

packages/app-utils/src/plugins/PluginUtils.tsx

-37
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { getThemeKey, ThemeData } from '@deephaven/components';
21
import Log from '@deephaven/log';
32
import {
43
type PluginModuleMap,
@@ -11,8 +10,6 @@ import {
1110
PluginType,
1211
isLegacyAuthPlugin,
1312
isLegacyPlugin,
14-
isThemePlugin,
15-
ThemePlugin,
1613
} from '@deephaven/plugin';
1714
import loadRemoteModule from './loadRemoteModule';
1815

@@ -172,37 +169,3 @@ export function getAuthPluginComponent(
172169

173170
return component;
174171
}
175-
176-
/**
177-
* Extract theme data from theme plugins in the given plugin map.
178-
* @param pluginMap
179-
*/
180-
export function getThemeDataFromPlugins(
181-
pluginMap: PluginModuleMap
182-
): ThemeData[] {
183-
const themePluginEntries = [...pluginMap.entries()].filter(
184-
(entry): entry is [string, ThemePlugin] => isThemePlugin(entry[1])
185-
);
186-
187-
log.debug('Getting theme data from plugins', themePluginEntries);
188-
189-
return themePluginEntries
190-
.map(([pluginName, plugin]) => {
191-
// Normalize to an array since config can be an array of configs or a
192-
// single config
193-
const configs = Array.isArray(plugin.themes)
194-
? plugin.themes
195-
: [plugin.themes];
196-
197-
return configs.map(
198-
({ name, baseTheme, styleContent }) =>
199-
({
200-
baseThemeKey: `default-${baseTheme ?? 'dark'}`,
201-
themeKey: getThemeKey(pluginName, name),
202-
name,
203-
styleContent,
204-
}) as const
205-
);
206-
})
207-
.flat();
208-
}

packages/app-utils/tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
{ "path": "../auth-plugins" },
1111
{ "path": "../chart" },
1212
{ "path": "../components" },
13+
{ "path": "../console" },
1314
{ "path": "../dashboard" },
1415
{ "path": "../iris-grid" },
1516
{ "path": "../jsapi-bootstrap" },

packages/code-studio/src/settings/SettingsMenu.tsx

+39-1
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,24 @@ import {
66
vsRecordKeys,
77
vsInfo,
88
vsLayers,
9+
vsPaintcan,
910
dhUserIncognito,
1011
dhUser,
1112
} from '@deephaven/icons';
12-
import { Button, CopyButton, Tooltip } from '@deephaven/components';
13+
import {
14+
Button,
15+
CopyButton,
16+
ThemeContext,
17+
ThemePicker,
18+
Tooltip,
19+
} from '@deephaven/components';
1320
import { ServerConfigValues, User } from '@deephaven/redux';
1421
import {
1522
BROADCAST_CHANNEL_NAME,
1623
BROADCAST_LOGOUT_MESSAGE,
1724
makeMessage,
1825
} from '@deephaven/jsapi-utils';
26+
import { assertNotNull } from '@deephaven/utils';
1927
import Logo from './community-wordmark-app.svg';
2028
import FormattingSectionContent from './FormattingSectionContent';
2129
import LegalNotice from './LegalNotice';
@@ -51,6 +59,8 @@ export class SettingsMenu extends Component<
5159

5260
static SHORTCUT_SECTION_KEY = 'SettingsMenu.shortcuts';
5361

62+
static THEME_SECTION_KEY = 'SettingsMenu.theme';
63+
5464
static focusFirstInputInContainer(container: HTMLDivElement | null): void {
5565
const input = container?.querySelector('input, select, textarea');
5666
if (input) {
@@ -232,6 +242,34 @@ export class SettingsMenu extends Component<
232242
<ColumnSpecificSectionContent scrollTo={this.handleScrollTo} />
233243
</SettingsMenuSection>
234244

245+
<ThemeContext.Consumer>
246+
{contextValue => {
247+
assertNotNull(contextValue, 'ThemeContext value is null');
248+
249+
return contextValue.themes.length > 1 ? (
250+
<SettingsMenuSection
251+
sectionKey={SettingsMenu.THEME_SECTION_KEY}
252+
isExpanded={this.isSectionExpanded(
253+
SettingsMenu.THEME_SECTION_KEY
254+
)}
255+
onToggle={this.handleSectionToggle}
256+
title={
257+
<>
258+
<FontAwesomeIcon
259+
icon={vsPaintcan}
260+
transform="grow-4"
261+
className="mr-2"
262+
/>
263+
Theme
264+
</>
265+
}
266+
>
267+
<ThemePicker />
268+
</SettingsMenuSection>
269+
) : null;
270+
}}
271+
</ThemeContext.Consumer>
272+
235273
<SettingsMenuSection
236274
sectionKey={SettingsMenu.SHORTCUT_SECTION_KEY}
237275
isExpanded={this.isSectionExpanded(

0 commit comments

Comments
 (0)