preference: Encapsulate in a custom element.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg
2022-02-09 23:29:39 -08:00
parent b366195415
commit ffe87a9729
4 changed files with 117 additions and 66 deletions

View File

@@ -1,7 +1,6 @@
html, :host {
body { contain: strict;
height: 100%; display: flow-root;
margin: 0;
cursor: default; cursor: default;
user-select: none; user-select: none;
font-family: menu, "Helvetica Neue", sans-serif; font-family: menu, "Helvetica Neue", sans-serif;
@@ -74,7 +73,7 @@ td:nth-child(odd) {
} }
#content { #content {
display: flex; display: flex !important;
height: 100%; height: 100%;
font-family: "Montserrat", sans-serif; font-family: "Montserrat", sans-serif;
} }

View File

@@ -1,4 +1,5 @@
import type {DNDSettings} from "../../../../common/dnd-util"; import type {DNDSettings} from "../../../../common/dnd-util";
import {html} from "../../../../common/html";
import type {NavItem} from "../../../../common/types"; import type {NavItem} from "../../../../common/types";
import {ipcRenderer} from "../../typed-ipc-renderer"; import {ipcRenderer} from "../../typed-ipc-renderer";
@@ -9,51 +10,89 @@ import {initNetworkSection} from "./network-section";
import {initServersSection} from "./servers-section"; import {initServersSection} from "./servers-section";
import {initShortcutsSection} from "./shortcuts-section"; import {initShortcutsSection} from "./shortcuts-section";
export function initPreferenceView(): void { export class PreferenceView {
const $sidebarContainer = document.querySelector("#sidebar")!; readonly $view: HTMLElement;
const $settingsContainer = document.querySelector("#settings-container")!; private readonly $shadow: ShadowRoot;
private readonly $settingsContainer: Element;
private readonly nav: Nav;
const nav = new Nav({ constructor() {
$root: $sidebarContainer, this.$view = document.createElement("div");
onItemSelected: handleNavigation, this.$shadow = this.$view.attachShadow({mode: "open"});
}); this.$shadow.innerHTML = html`
<link
rel="stylesheet"
href="${require.resolve("../../../css/fonts.css")}"
/>
<link
rel="stylesheet"
href="${require.resolve("../../../css/preference.css")}"
/>
<link
rel="stylesheet"
href="${require.resolve("@yaireo/tagify/dist/tagify.css")}"
/>
<!-- Initially hidden to prevent FOUC -->
<div id="content" hidden>
<div id="sidebar"></div>
<div id="settings-container"></div>
</div>
`.html;
const navItem = const $sidebarContainer = this.$shadow.querySelector("#sidebar")!;
nav.navItems.find((navItem) => window.location.hash === `#${navItem}`) ?? this.$settingsContainer = this.$shadow.querySelector(
"General"; "#settings-container",
)!;
handleNavigation(navItem); this.nav = new Nav({
$root: $sidebarContainer,
onItemSelected: this.handleNavigation,
});
function handleNavigation(navItem: NavItem): void { const navItem =
nav.select(navItem); this.nav.navItems.find(
(navItem) => window.location.hash === `#${navItem}`,
) ?? "General";
this.handleNavigation(navItem);
ipcRenderer.on("switch-settings-nav", this.handleSwitchSettingsNav);
ipcRenderer.on("toggle-sidebar-setting", this.handleToggleSidebar);
ipcRenderer.on("toggle-menubar-setting", this.handleToggleMenubar);
ipcRenderer.on("toggle-tray", this.handleToggleTray);
ipcRenderer.on("toggle-dnd", this.handleToggleDnd);
}
handleNavigation = (navItem: NavItem): void => {
this.nav.select(navItem);
switch (navItem) { switch (navItem) {
case "AddServer": case "AddServer":
initServersSection({ initServersSection({
$root: $settingsContainer, $root: this.$settingsContainer,
}); });
break; break;
case "General": case "General":
initGeneralSection({ initGeneralSection({
$root: $settingsContainer, $root: this.$settingsContainer,
}); });
break; break;
case "Organizations": case "Organizations":
initConnectedOrgSection({ initConnectedOrgSection({
$root: $settingsContainer, $root: this.$settingsContainer,
}); });
break; break;
case "Network": case "Network":
initNetworkSection({ initNetworkSection({
$root: $settingsContainer, $root: this.$settingsContainer,
}); });
break; break;
case "Shortcuts": { case "Shortcuts": {
initShortcutsSection({ initShortcutsSection({
$root: $settingsContainer, $root: this.$settingsContainer,
}); });
break; break;
} }
@@ -63,44 +102,57 @@ export function initPreferenceView(): void {
} }
window.location.hash = `#${navItem}`; window.location.hash = `#${navItem}`;
};
destroy(): void {
ipcRenderer.off("switch-settings-nav", this.handleSwitchSettingsNav);
ipcRenderer.off("toggle-sidebar-setting", this.handleToggleSidebar);
ipcRenderer.off("toggle-menubar-setting", this.handleToggleMenubar);
ipcRenderer.off("toggle-tray", this.handleToggleTray);
ipcRenderer.off("toggle-dnd", this.handleToggleDnd);
} }
// Handle toggling and reflect changes in preference page // Handle toggling and reflect changes in preference page
function handleToggle(elementName: string, state = false): void { private handleToggle(elementName: string, state = false): void {
const inputSelector = `#${elementName} .action .switch input`; const inputSelector = `#${elementName} .action .switch input`;
const input: HTMLInputElement = document.querySelector(inputSelector)!; const input: HTMLInputElement = this.$shadow.querySelector(inputSelector)!;
if (input) { if (input) {
input.checked = state; input.checked = state;
} }
} }
ipcRenderer.on("switch-settings-nav", (_event: Event, navItem: NavItem) => { private readonly handleSwitchSettingsNav = (
handleNavigation(navItem); _event: Event,
}); navItem: NavItem,
) => {
this.handleNavigation(navItem);
};
ipcRenderer.on("toggle-sidebar-setting", (_event: Event, state: boolean) => { private readonly handleToggleSidebar = (_event: Event, state: boolean) => {
handleToggle("sidebar-option", state); this.handleToggle("sidebar-option", state);
}); };
ipcRenderer.on("toggle-menubar-setting", (_event: Event, state: boolean) => { private readonly handleToggleMenubar = (_event: Event, state: boolean) => {
handleToggle("menubar-option", state); this.handleToggle("menubar-option", state);
}); };
ipcRenderer.on("toggle-tray", (_event: Event, state: boolean) => { private readonly handleToggleTray = (_event: Event, state: boolean) => {
handleToggle("tray-option", state); this.handleToggle("tray-option", state);
}); };
ipcRenderer.on( private readonly handleToggleDnd = (
"toggle-dnd", _event: Event,
(_event: Event, _state: boolean, newSettings: Partial<DNDSettings>) => { _state: boolean,
handleToggle("show-notification-option", newSettings.showNotification); newSettings: Partial<DNDSettings>,
handleToggle("silent-option", newSettings.silent); ) => {
this.handleToggle("show-notification-option", newSettings.showNotification);
this.handleToggle("silent-option", newSettings.silent);
if (process.platform === "win32") { if (process.platform === "win32") {
handleToggle("flash-taskbar-option", newSettings.flashTaskbarOnMessage); this.handleToggle(
} "flash-taskbar-option",
}, newSettings.flashTaskbarOnMessage,
); );
}
};
} }
window.addEventListener("load", initPreferenceView);

View File

@@ -23,6 +23,10 @@ export const ipcRenderer: {
channel: Channel, channel: Channel,
listener: RendererListener<Channel>, listener: RendererListener<Channel>,
): void; ): void;
off<Channel extends keyof RendererMessage>(
channel: Channel,
listener: RendererListener<Channel>,
): void;
removeListener<Channel extends keyof RendererMessage>( removeListener<Channel extends keyof RendererMessage>(
channel: Channel, channel: Channel,
listener: RendererListener<Channel>, listener: RendererListener<Channel>,

View File

@@ -5,24 +5,20 @@
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<title>Zulip - Settings</title> <title>Zulip - Settings</title>
<link rel="stylesheet" href="css/fonts.css" /> <link rel="stylesheet" href="css/fonts.css" />
<link <style>
rel="stylesheet" html,
href="css/preference.css" body,
type="text/css" body > div {
media="screen" margin: 0;
/> height: 100%;
<link id="tagify-css" rel="stylesheet" href="data:text/css," /> }
</style>
</head> </head>
<body> <body>
<div id="content"> <zd-preference-view></zd-preference-view>
<div id="sidebar"></div> <script>
<div id="settings-container"></div> const {PreferenceView} = require("./js/pages/preference/preference.js");
</div> document.body.append(new PreferenceView().$view);
</script>
</body> </body>
<script>
document.querySelector("#tagify-css").href = require.resolve(
"@yaireo/tagify/dist/tagify.css",
);
require("./js/pages/preference/preference.js");
</script>
</html> </html>