Skip to content

Commit cda0738

Browse files
committed
allow VegaChart to know current theme mode
1 parent 716b30c commit cda0738

File tree

14 files changed

+102
-57
lines changed

14 files changed

+102
-57
lines changed

chartlets.js/package-lock.json

Lines changed: 0 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

chartlets.js/packages/demo/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
"dependencies": {
3030
"@emotion/react": "^11.13.3",
3131
"@emotion/styled": "^11.13.0",
32-
"@fontsource/roboto": "^5.1.0",
3332
"@mui/material": "^6.1.5",
3433
"chartlets": "file:../lib",
3534
"react": "^18.3.1",

chartlets.js/packages/demo/src/App.tsx

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useMemo, useState } from "react";
1+
import { useMemo } from "react";
22
import {
33
CssBaseline,
44
ThemeProvider,
@@ -10,12 +10,12 @@ import { initializeContributions } from "chartlets";
1010
import mui from "chartlets/plugins/mui";
1111
import vega from "chartlets/plugins/vega";
1212

13-
import { type AppState, appStore } from "@/store";
14-
import ExtensionsInfo from "./components/ExtensionInfo";
13+
import { type AppState, appStore, useAppStore } from "@/store";
1514
import ControlBar from "./components/ControlBar";
15+
import ExtensionsInfo from "./components/ExtensionInfo";
16+
import Header from "./components/Header";
1617
import PanelsControl from "./components/PanelsControl";
1718
import PanelsRow from "./components/PanelsRow";
18-
import Header, { type Mode } from "./components/Header";
1919

2020
initializeContributions({
2121
plugins: [mui(), vega()],
@@ -39,13 +39,14 @@ const fontFamily = "Roboto, Arial, sans-serif";
3939

4040
function App() {
4141
const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
42-
const systemMode = prefersDarkMode ? "dark" : "light";
43-
const [mode, setMode] = useState<Mode>(systemMode);
42+
const systemThemeMode = prefersDarkMode ? "dark" : "light";
43+
const themeMode = useAppStore((state) => state.themeMode);
44+
4445
const theme = useMemo(
4546
() =>
4647
createTheme({
4748
palette: {
48-
mode: mode === "system" ? systemMode : mode,
49+
mode: themeMode === "system" ? systemThemeMode : themeMode,
4950
},
5051
typography: { fontFamily },
5152
components: {
@@ -56,13 +57,13 @@ function App() {
5657
},
5758
},
5859
}),
59-
[mode, systemMode],
60+
[themeMode, systemThemeMode],
6061
);
6162

6263
return (
6364
<ThemeProvider theme={theme}>
6465
<CssBaseline />
65-
<Header mode={mode} setMode={setMode} />
66+
<Header />
6667
<ExtensionsInfo />
6768
<ControlBar />
6869
<PanelsControl />

chartlets.js/packages/demo/src/components/Header.tsx

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,32 @@ import Box from "@mui/material/Box";
22
import Button from "@mui/material/Button";
33
import Typography from "@mui/material/Typography";
44

5-
export type Mode = "dark" | "light" | "system";
6-
const modes: Mode[] = ["dark", "light", "system"];
5+
import { type ThemeMode, themeModes, useAppStore } from "@/store";
76

8-
interface HeaderProps {
9-
mode: Mode;
10-
setMode: (mode: Mode) => void;
11-
}
12-
13-
function Header({ mode, setMode }: HeaderProps) {
7+
function Header() {
8+
const { themeMode, setThemeMode } = useAppStore();
149
return (
1510
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
1611
<Typography fontSize="3em" fontWeight="bold">
1712
Chartlets Demo
1813
</Typography>
1914
<Button
2015
onClick={() => {
21-
const nextMode = modes[(modes.findIndex((m) => m === mode) + 1) % 3];
22-
setMode(nextMode);
16+
setThemeMode(getNextThemeMode(themeMode));
2317
}}
2418
variant="contained"
2519
size="small"
2620
>
27-
{mode}
21+
{themeMode}
2822
</Button>
2923
</Box>
3024
);
3125
}
3226

3327
export default Header;
28+
29+
const getNextThemeMode = (themeMode: ThemeMode) => {
30+
return themeModes[
31+
(themeModes.findIndex((m) => m === themeMode) + 1) % themeModes.length
32+
];
33+
};

chartlets.js/packages/demo/src/store.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@ export interface Dataset {
55
title: string;
66
}
77

8+
export type ThemeMode = "dark" | "light" | "system";
9+
export const themeModes: ThemeMode[] = ["dark", "light", "system"];
10+
811
export interface AppState {
912
datasets: Dataset[];
1013
selectedDatasetId: string | null;
1114
setSelectedDatasetId(setSelectedDatasetId: string | null): void;
15+
themeMode: ThemeMode;
16+
setThemeMode(themeMode: ThemeMode): void;
1217
}
1318

1419
export const appStore = create<AppState>((set, get) => ({
@@ -23,6 +28,10 @@ export const appStore = create<AppState>((set, get) => ({
2328
set({ selectedDatasetId });
2429
}
2530
},
31+
themeMode: "system",
32+
setThemeMode: (themeMode: "dark" | "light" | "system") => {
33+
set({ themeMode });
34+
},
2635
}));
2736

2837
export const useAppStore = appStore;

chartlets.js/packages/lib/src/actions/handleHostStoreChange.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export function handleHostStoreChange() {
2828
// there are no extensions (yet)
2929
return;
3030
}
31+
synchronizeThemeMode(hostStore);
3132
const propertyRefs = getHostStorePropertyRefs();
3233
if (!propertyRefs || propertyRefs.length === 0) {
3334
// Exit if there are is nothing to be changed
@@ -95,3 +96,16 @@ function getHostStorePropertyRefs(): PropertyRef[] {
9596
});
9697
return propertyRefs;
9798
}
99+
100+
function synchronizeThemeMode(hostStore: HostStore) {
101+
const newThemeMode = hostStore.get("themeMode");
102+
const oldThemeMode = store.getState().themeMode;
103+
if (
104+
(newThemeMode === "dark" ||
105+
newThemeMode === "light" ||
106+
newThemeMode === "system") &&
107+
newThemeMode !== oldThemeMode
108+
) {
109+
store.setState({ themeMode: newThemeMode });
110+
}
111+
}

chartlets.js/packages/lib/src/hooks.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@ const selectContributionsResult = (state: StoreState) =>
1818
const selectContributionsRecord = (state: StoreState) =>
1919
state.contributionsRecord;
2020

21+
const selectThemeMode = (state: StoreState) => state.themeMode;
22+
2123
const useStore = store;
2224

2325
export const useConfiguration = () => useStore(selectConfiguration);
2426
export const useExtensions = () => useStore(selectExtensions);
2527
export const useContributionsResult = () => useStore(selectContributionsResult);
2628
export const useContributionsRecord = () => useStore(selectContributionsRecord);
29+
export const useThemeMode = () => useStore(selectThemeMode);
2730

2831
export function makeContributionsHook<S extends object = object>(
2932
contribPoint: string,

chartlets.js/packages/lib/src/index.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,20 @@ export type {
66
ComponentChangeEvent,
77
ComponentChangeHandler,
88
} from "@/types/state/event";
9+
export type { ThemeMode } from "@/types/state/store";
10+
911
// Actions (store changes)
1012
export { initializeContributions } from "@/actions/initializeContributions";
1113
export { handleComponentChange } from "@/actions/handleComponentChange";
1214
export { updateContributionContainer } from "@/actions/updateContributionContainer";
15+
1316
// Component registry
1417
export type { Registry } from "@/component/Registry";
18+
1519
// React components
1620
export { Component, type ComponentProps } from "@/component/Component";
1721
export { Children } from "@/component/Children";
22+
1823
// React hooks
1924
export {
2025
useConfiguration,
@@ -24,7 +29,9 @@ export {
2429
useContributions,
2530
useComponentChangeHandlers,
2631
makeContributionsHook,
32+
useThemeMode,
2733
} from "@/hooks";
34+
2835
// Application interface
2936
export type {
3037
FrameworkOptions,
@@ -33,6 +40,3 @@ export type {
3340
Plugin,
3441
PluginLike,
3542
} from "@/types/state/options";
36-
// Some common utilities
37-
export { isObject } from "@/utils/isObject";
38-
export { isString } from "@/utils/isString";

chartlets.js/packages/lib/src/plugins/mui/Select.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import MuiMenuItem from "@mui/material/MenuItem";
44
import MuiSelect, { type SelectChangeEvent } from "@mui/material/Select";
55

66
import type { ComponentState, ComponentProps } from "@/index";
7-
import { isString } from "@/index";
7+
import { isString } from "@/utils/isString";
88

99
export type SelectOption =
1010
| string

chartlets.js/packages/lib/src/plugins/vega/VegaChart.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@ import type { TopLevelSpec } from "vega-lite";
33

44
import type { ComponentState, ComponentProps } from "@/index";
55
import { useSignalListeners } from "./hooks/useSignalListeners";
6-
import { useTheme, type VegaTheme } from "./hooks/useTheme";
6+
import { useVegaTheme, type VegaTheme } from "./hooks/useVegaTheme";
77

8-
interface PlotState extends ComponentState {
8+
interface VegaChartState extends ComponentState {
99
theme?: VegaTheme | "default" | "system";
1010
chart?:
1111
| TopLevelSpec // This is the vega-lite specification type
1212
| null;
1313
}
1414

15-
interface PlotProps extends ComponentProps, PlotState {}
15+
interface VegaChartProps extends ComponentProps, VegaChartState {}
1616

1717
export function VegaChart({
1818
type,
@@ -21,9 +21,10 @@ export function VegaChart({
2121
theme,
2222
chart,
2323
onChange,
24-
}: PlotProps) {
24+
}: VegaChartProps) {
2525
const signalListeners = useSignalListeners(chart, type, id, onChange);
26-
const vegaTheme = useTheme(theme);
26+
const vegaTheme = useVegaTheme(theme);
27+
console.info("---> vegaTheme:", vegaTheme);
2728
if (chart) {
2829
return (
2930
<VegaLite

0 commit comments

Comments
 (0)