Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 37 additions & 7 deletions web/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const DEFAULT_PREFS: Prefs = {
direction: "DOWN",
enums: true,
incoming: true,
sidebar: true,
};

interface Example {
Expand Down Expand Up @@ -74,6 +75,21 @@ export class App implements AppCtx {
private sourceCache = new Map<number, Promise<string | null>>();
private _cmp: Compare | null = null;
private onHash = () => this.readHash();
private onKey = (ev: KeyboardEvent) => {
const target = ev.composedPath()[0] as HTMLElement | undefined;
if (ev.ctrlKey || ev.metaKey || ev.altKey || ev.defaultPrevented) return;
if (target && (target.closest?.("input, select, textarea, [contenteditable]") || target.isContentEditable)) return;
// The Changes view has its own "/" (its filter box).
if (ev.key === "/" && this.side && this._route.view !== "diff" && this._route.view !== "difff") {
ev.preventDefault();
if (this.isNarrow()) this.side.el.classList.add("open");
else if (!this._prefs.sidebar) this.toggleSidebar();
this.side.focusSearch();
} else if (ev.key === "b" && this.side) {
ev.preventDefault();
this.toggleSidebar();
}
};

constructor(root: HTMLElement, opts: AppOptions) {
this.root = root;
Expand All @@ -87,17 +103,14 @@ export class App implements AppCtx {
root.appendChild(this.shellEl);
this.applyTheme();
if (opts.routing) window.addEventListener("hashchange", this.onHash);
root.addEventListener("keydown", (ev) => {
if (ev.key === "/" && !(ev.target instanceof HTMLInputElement) && !(ev.target instanceof HTMLTextAreaElement)) {
ev.preventDefault();
this.side?.focusSearch();
}
});
// The standalone app owns the page, so its keys work wherever the focus is.
(opts.routing ? window : root).addEventListener("keydown", this.onKey as EventListener);
this.wireDrop();
}

destroy(): void {
window.removeEventListener("hashchange", this.onHash);
window.removeEventListener("keydown", this.onKey);
}

// ---- AppCtx -------------------------------------------------------------
Expand Down Expand Up @@ -540,6 +553,23 @@ export class App implements AppCtx {
this.side = sidebar(this);
this.bodyEl.insertBefore(this.side.el, this.mainEl);
this.bodyEl.classList.remove("no-sidebar");
this.bodyEl.classList.toggle("sidebar-hidden", !this._prefs.sidebar);
}

private isNarrow(): boolean {
return !!window.matchMedia?.("(max-width: 860px)").matches;
}

/** Show or hide the sidebar: a drawer on a phone, a remembered choice on a wide screen. */
private toggleSidebar(): void {
if (!this.side) return;
if (this.isNarrow()) {
this.side.el.classList.toggle("open");
return;
}
this.setPrefs({ sidebar: !this._prefs.sidebar });
this.bodyEl.classList.toggle("sidebar-hidden", !this._prefs.sidebar);
this.renderTopbar();
}

// ---- rendering ----------------------------------------------------------
Expand Down Expand Up @@ -604,7 +634,7 @@ export class App implements AppCtx {
const t = this.topbar;
t.replaceChildren();
const m = this.model;
if (m) t.appendChild(button("", () => this.side?.el.classList.toggle("open"), { icon: "menu", cls: "ghost icon-only menu-btn", title: "Types, elements and search" }));
if (m && !this.opts.minimal) t.appendChild(button("", () => this.toggleSidebar(), { icon: "menu", cls: "ghost icon-only menu-btn", title: `${this._prefs.sidebar ? "Hide" : "Show"} the sidebar: types, elements and search (b)` }));
t.appendChild(h("a", { class: "title", href: this.hrefRoute({ view: "home" }), onClick: (ev: Event) => {
ev.preventDefault();
if (this.model) this.goRoute({ view: "home" });
Expand Down
2 changes: 2 additions & 0 deletions web/src/ctx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export interface Prefs {
direction: "DOWN" | "RIGHT";
enums: boolean;
incoming: boolean;
/** Show the sidebar (types, elements and search) on wide screens. */
sidebar: boolean;
}

export interface AppCtx {
Expand Down
12 changes: 7 additions & 5 deletions web/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -234,13 +234,15 @@ code,
.topbar > select {
flex-shrink: 0;
}
.btn.menu-btn {
display: none;
@media (min-width: 861px) {
.body.sidebar-hidden {
grid-template-columns: 1fr;
}
.body.sidebar-hidden > .sidebar {
display: none;
}
}
@media (max-width: 860px) {
.btn.menu-btn {
display: inline-flex;
}
/* Room for the title on a phone: the share button keeps its icon, About moves to the footer. */
.topbar .share-btn span,
.topbar .btn.about-btn {
Expand Down
3 changes: 2 additions & 1 deletion web/src/views/dialogs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ export function openHelp(host: HTMLElement): void {
"Finding things",
false,
["Type in the ", b("search"), " box (", k("/"), " jumps to it) to match names of types, elements, attributes and groups, and words of their documentation and enumeration values. Use ", k("↑"), " ", k("↓"), " and ", k("Enter"), " to open a result."],
["The tabs under the search box list ", b("Types"), " (complex types), ", b("Simple"), " types, global ", b("Elements"), ", ", b("Groups"), " (model groups, attribute groups, global attributes) and ", b("Files"), ". The namespace list narrows them to one namespace."]
["The tabs under the search box list ", b("Types"), " (complex types), ", b("Simple"), " types, global ", b("Elements"), ", ", b("Groups"), " (model groups, attribute groups, global attributes) and ", b("Files"), ". The namespace list narrows them to one namespace."],
["The menu button at the top left (or ", k("b"), ") hides the sidebar to give the page the full width, and shows it again. The choice is remembered."]
),
section(
"Type pages",
Expand Down
Loading