106 lines
3.5 KiB
Svelte
106 lines
3.5 KiB
Svelte
<script lang="ts">
|
|
import { fade, fly } from "svelte/transition";
|
|
import { cubicOut } from "svelte/easing";
|
|
import { GROUPS, SHORTCUTS } from "../lib/keymap";
|
|
import type { Group } from "../lib/keymap";
|
|
import { useView } from "./context";
|
|
|
|
const view = useView();
|
|
const store = view.store;
|
|
|
|
function close(): void {
|
|
store.helpOpen = false;
|
|
}
|
|
|
|
function grab(node: HTMLElement) {
|
|
node.focus({ preventScroll: true });
|
|
return { destroy: () => view.focusRoot() };
|
|
}
|
|
|
|
function onkeydown(event: KeyboardEvent): void {
|
|
if (event.key !== "Escape" && event.key !== "?") return;
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
close();
|
|
}
|
|
|
|
function rows(group: Group) {
|
|
return SHORTCUTS.filter((shortcut) => shortcut.group === group);
|
|
}
|
|
|
|
/**
|
|
* Only Moving needs the qualifier: it is the one group where the same key
|
|
* does two things. The other groups say their scope in the heading.
|
|
*/
|
|
function scope(shortcut: (typeof SHORTCUTS)[number]): string {
|
|
if (shortcut.group !== "Moving") return "";
|
|
if (shortcut.scopes.includes("task")) return "on a task";
|
|
if (shortcut.scopes.includes("day")) return "on a day";
|
|
return "";
|
|
}
|
|
</script>
|
|
|
|
<div
|
|
class="fixed inset-0 z-50 grid place-items-center bg-black/45 p-4 outline-none backdrop-blur-[2px]"
|
|
onclick={close}
|
|
{onkeydown}
|
|
role="presentation"
|
|
tabindex="-1"
|
|
transition:fade={{ duration: 120 }}
|
|
use:grab
|
|
>
|
|
<div
|
|
class="max-h-full w-full max-w-5xl overflow-y-auto rounded-xl border border-border bg-popover text-popover-foreground shadow-[0_24px_64px_-16px_rgb(0_0_0/55%)] @container"
|
|
onclick={(event) => event.stopPropagation()}
|
|
onkeydown={() => {}}
|
|
role="presentation"
|
|
transition:fly={{ duration: 180, easing: cubicOut, y: 8 }}
|
|
>
|
|
<div class="flex items-baseline gap-3 px-5 pt-4 pb-3">
|
|
<h2 class="font-semibold text-base">Keyboard</h2>
|
|
<p class="text-muted-foreground text-xs">
|
|
The vim block reads the physical key, so it answers on a Cyrillic layout too.
|
|
</p>
|
|
<button
|
|
class="ml-auto shrink-0 rounded-md px-2 py-1 text-muted-foreground text-xs outline-none transition-colors hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring"
|
|
onclick={close}
|
|
type="button"
|
|
>
|
|
esc
|
|
</button>
|
|
</div>
|
|
|
|
<div class="gap-x-8 px-5 pb-5 @min-[34rem]:columns-2 @min-[58rem]:columns-3">
|
|
{#each GROUPS as group (group)}
|
|
<section class="mb-4 min-w-0 break-inside-avoid">
|
|
<h3
|
|
class="pb-1.5 font-semibold text-[10px] text-muted-foreground uppercase tracking-[0.09em]"
|
|
>
|
|
{group}
|
|
</h3>
|
|
<dl class="grid grid-cols-[1fr_auto] items-center gap-x-3">
|
|
{#each rows(group) as shortcut, index (`${shortcut.label}-${index}`)}
|
|
<dt
|
|
class="min-w-0 truncate border-border/40 border-t py-1 text-xs first:border-t-0"
|
|
title={shortcut.label}
|
|
>
|
|
{shortcut.label}
|
|
{#if scope(shortcut)}
|
|
<span class="text-muted-foreground/70">{scope(shortcut)}</span>
|
|
{/if}
|
|
</dt>
|
|
<dd
|
|
class="flex shrink-0 items-center gap-1 border-border/40 border-t py-1 [&:nth-child(2)]:border-t-0"
|
|
>
|
|
{#each shortcut.keys as key (key)}
|
|
<kbd class="kbd">{key}</kbd>
|
|
{/each}
|
|
</dd>
|
|
{/each}
|
|
</dl>
|
|
</section>
|
|
{/each}
|
|
</div>
|
|
</div>
|
|
</div>
|