Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add: Storybookにテーマが適用されるように #2299

Merged
merged 11 commits into from
Oct 20, 2024
41 changes: 40 additions & 1 deletion .storybook/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import { markdownItPlugin } from "@/plugins/markdownItPlugin";

import "@quasar/extras/material-icons/material-icons.css";
import "quasar/dist/quasar.sass";
import "../src/styles/_index.scss";
import "@/styles/_index.scss";
import { UnreachableError } from "@/type/utility";
import { setThemeToCss, setFontToCss } from "@/domain/dom";
import { themes } from "@/domain/theme";

setup((app) => {
app.use(Quasar, {
Expand Down Expand Up @@ -61,6 +64,42 @@ const preview: Preview = {
defaultTheme: "light",
attributeName: "is-dark-theme",
}),

// テーマの設定をCSSへ反映する
() => {
sevenc-nanashi marked this conversation as resolved.
Show resolved Hide resolved
let observer: MutationObserver | undefined = undefined;
return {
async mounted() {
setFontToCss("default");

const root = document.documentElement;
let lastIsDark: boolean | undefined = undefined;
observer = new MutationObserver(() => {
const isDark = root.getAttribute("is-dark-theme") === "true";
if (lastIsDark === isDark) return;
lastIsDark = isDark;

const theme = themes.find((theme) => theme.isDark === isDark);
if (!theme)
throw new UnreachableError("assert: theme !== undefined");

setThemeToCss(theme);
});

observer.observe(root, {
attributes: true,
attributeFilter: ["is-dark-theme"],
});
},
unmounted() {
if (observer) {
observer.disconnect();
}
},

template: `<story />`,
};
},
],
argTypesEnhancers: [addActionsWithEmits],
};
Expand Down
18 changes: 0 additions & 18 deletions public/themes/dark.json

This file was deleted.

18 changes: 0 additions & 18 deletions public/themes/default.json

This file was deleted.

11 changes: 0 additions & 11 deletions src/backend/browser/sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
EngineSettings,
HotkeySettingType,
Sandbox,
ThemeConf,
} from "@/type/preload";
import { AssetTextFileNames } from "@/type/staticResources";

Expand Down Expand Up @@ -245,16 +244,6 @@ export const api: Sandbox = {
// TODO: Impl
return;
},
async getAvailableThemes() {
// NOTE: Electron版では起動時にテーマ情報が必要なので、
// この実装とは違って起動時に読み込んだキャッシュを返すだけになっている。
return Promise.all(
// FIXME: themeファイルのいい感じのパスの設定
["/themes/default.json", "/themes/dark.json"].map((url) =>
fetch(url).then((res) => res.json() as Promise<ThemeConf>),
),
);
},
vuexReady() {
// NOTE: 何もしなくて良さそう
return Promise.resolve();
Expand Down
20 changes: 1 addition & 19 deletions src/backend/electron/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import { EngineAndVvppController } from "./engineAndVvppController";
import { failure, success } from "@/type/result";
import { AssetTextFileNames } from "@/type/staticResources";
import {
ThemeConf,
EngineInfo,
SystemError,
defaultHotkeySettings,
Expand All @@ -40,6 +39,7 @@ import {
EngineId,
TextAsset,
} from "@/type/preload";
import { themes } from "@/domain/theme";

type SingleInstanceLockData = {
filePath: string | undefined;
Expand Down Expand Up @@ -226,20 +226,6 @@ function checkMultiEngineEnabled(): boolean {
return enabled;
}

// テーマの読み込み
const themes = readThemeFiles();
function readThemeFiles() {
const themes: ThemeConf[] = [];
const dir = path.join(__static, "themes");
for (const file of fs.readdirSync(dir)) {
const theme = JSON.parse(
fs.readFileSync(path.join(dir, file)).toString(),
) as ThemeConf;
themes.push(theme);
}
return themes;
}

const appState = {
willQuit: false,
};
Expand Down Expand Up @@ -652,10 +638,6 @@ registerIpcMainHandle<IpcMainHandle>({
}
},

GET_AVAILABLE_THEMES: () => {
return themes;
},

OPEN_LOG_DIRECTORY: () => {
void shell.openPath(app.getPath("logs"));
},
Expand Down
4 changes: 0 additions & 4 deletions src/backend/electron/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,6 @@ const api: Sandbox = {
void ipcRendererInvokeProxy.SET_NATIVE_THEME(source);
},

getAvailableThemes: () => {
return ipcRendererInvokeProxy.GET_AVAILABLE_THEMES();
},

vuexReady: () => {
void ipcRendererInvokeProxy.ON_VUEX_READY();
},
Expand Down
41 changes: 41 additions & 0 deletions src/domain/theme/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { ThemeConf } from "@/type/preload";

const light = {
name: "Default",
displayName: "デフォルト",
order: 1,
isDark: false,
colors: {
primary: "#A5D4AD",
display: "#121212",
"display-on-primary": "#121212",
"display-hyperlink": "#0969DA",
background: "#FFFFFF",
surface: "#EEEEEE",
warning: "#C10015",
"text-splitter-hover": "#CCDDFF",
"active-point-focus": "#E0EAFF",
"active-point-hover": "#EEF3FF",
},
} as const satisfies ThemeConf;

const dark = {
name: "Dark",
displayName: "ダーク",
order: 2,
isDark: true,
colors: {
primary: "#86C591",
display: "#E1E1E1",
"display-on-primary": "#1F1F1F",
"display-hyperlink": "#58A6FF",
background: "#1F1F1F",
surface: "#2B2B2B",
warning: "#F27483",
"text-splitter-hover": "#394152",
"active-point-focus": "#292F38",
"active-point-hover": "#272A2F",
},
} as const satisfies ThemeConf;

export const themes = [light, dark];
3 changes: 2 additions & 1 deletion src/store/setting.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { SettingStoreState, SettingStoreTypes } from "./type";
import { createDotNotationUILockAction as createUILockAction } from "./ui";
import { createDotNotationPartialStore as createPartialStore } from "./vuex";
import { themes } from "@/domain/theme";
import {
showAlertDialog,
showQuestionDialog,
Expand Down Expand Up @@ -83,7 +84,7 @@ export const settingStore = createPartialStore<SettingStoreTypes>({
});

mutations.SET_AVAILABLE_THEMES({
themes: await window.backend.getAvailableThemes(),
themes,
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

StoreのSET_AVAILABLE_THEMES残すのありだと思います!
ただここの関数はバックエンドの設定をフロント側に持ってくる部分なのですが、若干意図不明なコードになったかもですね!

ということで、カスタムテーマなどで動的に増やすことも見据えて残してあることをコメントで書いとこうと思います 🙏

void actions.SET_CURRENT_THEME_SETTING({
currentTheme: await window.backend.getSetting("currentTheme"),
Expand Down
4 changes: 4 additions & 0 deletions src/store/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,10 @@ export const uiStore = createPartialStore<UiStoreTypes>({
},
},

/**
* 選択可能なテーマをセットする。
* NOTE: カスタムテーマが導入された場合を見越して残している。
*/
SET_AVAILABLE_THEMES: {
mutation(state, { themes }) {
state.availableThemes = themes;
Expand Down
10 changes: 7 additions & 3 deletions src/styles/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@
// 優先度を強引に上げる
body:not(#dummy) {
user-select: none;
border-left: solid #{vars.$window-border-width} #{colors.$splitter};
border-right: solid #{vars.$window-border-width} #{colors.$splitter};
border-bottom: solid #{vars.$window-border-width} #{colors.$splitter};
color: colors.$display;
background: colors.$background;

// Storybookでは枠線を付けない
&:not(.sb-show-main) {
border-left: solid #{vars.$window-border-width} #{colors.$splitter};
border-right: solid #{vars.$window-border-width} #{colors.$splitter};
border-bottom: solid #{vars.$window-border-width} #{colors.$splitter};
}
}

body[data-editor-font="default"] {
Expand Down
6 changes: 0 additions & 6 deletions src/type/ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
MessageBoxReturnValue,
NativeThemeType,
TextAsset,
ThemeConf,
ToolbarSettingType,
} from "@/type/preload";
import { AltPortInfos } from "@/store/type";
Expand Down Expand Up @@ -169,11 +168,6 @@ export type IpcIHData = {
return: ToolbarSettingType;
};

GET_AVAILABLE_THEMES: {
args: [];
return: ThemeConf[];
};

ON_VUEX_READY: {
args: [];
return: void;
Expand Down
1 change: 0 additions & 1 deletion src/type/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,6 @@ export interface Sandbox {
getDefaultHotkeySettings(): Promise<HotkeySettingType[]>;
getDefaultToolbarSetting(): Promise<ToolbarSettingType>;
setNativeTheme(source: NativeThemeType): void;
getAvailableThemes(): Promise<ThemeConf[]>;
vuexReady(): void;
getSetting<Key extends keyof ConfigType>(key: Key): Promise<ConfigType[Key]>;
setSetting<Key extends keyof ConfigType>(
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading