+
{#if drill}
diff --git a/src/lib/keys.svelte.ts b/src/lib/keys.svelte.ts
index 7ad48e0..6b2be9a 100644
--- a/src/lib/keys.svelte.ts
+++ b/src/lib/keys.svelte.ts
@@ -13,14 +13,57 @@ const SEQUENCE_MS = 1200;
const EDITABLE =
"input, textarea, select, [contenteditable=''], [contenteditable='true']";
+const LATIN_LETTER = /^[a-z]$/i;
+const ANY_LETTER = /^\p{L}$/u;
+const US_KEYS: Record = {
+ Backquote: ["`", "~"],
+ Backslash: ["\\", "|"],
+ BracketLeft: ["[", "{"],
+ BracketRight: ["]", "}"],
+ Comma: [",", "<"],
+ Digit0: ["0", ")"],
+ Digit1: ["1", "!"],
+ Digit2: ["2", "@"],
+ Digit3: ["3", "#"],
+ Digit4: ["4", "$"],
+ Digit5: ["5", "%"],
+ Digit6: ["6", "^"],
+ Digit7: ["7", "&"],
+ Digit8: ["8", "*"],
+ Digit9: ["9", "("],
+ Equal: ["=", "+"],
+ Minus: ["-", "_"],
+ Period: [".", ">"],
+ Quote: ["'", '"'],
+ Semicolon: [";", ":"],
+ Slash: ["/", "?"],
+};
+
+let foreignLayout = false;
+
+function physicalKey({ code, shiftKey }: KeyboardEvent): string | undefined {
+ if (code.startsWith("Key")) {
+ const letter = code.slice(3);
+ return shiftKey ? letter : letter.toLowerCase();
+ }
+ return US_KEYS[code]?.[shiftKey ? 1 : 0];
+}
+
function baseKey(event: KeyboardEvent): string {
- if (event.altKey && event.code.startsWith("Key")) {
- return event.code.slice(3).toLowerCase();
+ const { key } = event;
+ if (key === " ") {
+ return "Space";
}
- if (event.altKey && event.code.startsWith("Digit")) {
- return event.code.slice(5);
+ if (key.length !== 1) {
+ return key;
}
- return event.key === " " ? "Space" : event.key;
+ const foreign = !LATIN_LETTER.test(key) && ANY_LETTER.test(key);
+ const plain = !(event.altKey || event.ctrlKey || event.metaKey);
+ if (plain && (foreign || LATIN_LETTER.test(key))) {
+ foreignLayout = foreign;
+ }
+ const positional = event.altKey || foreign || (plain && foreignLayout);
+ return (positional && physicalKey(event)) || key;
}
function token(event: KeyboardEvent): string {
diff --git a/src/lib/list-nav.svelte.ts b/src/lib/list-nav.svelte.ts
index c174a3b..115c923 100644
--- a/src/lib/list-nav.svelte.ts
+++ b/src/lib/list-nav.svelte.ts
@@ -1,5 +1,7 @@
import { type Binding, useKeys } from "#lib/keys.svelte.js";
+const TOP_REACH = 0.6;
+
interface Options {
edit?: (item: T) => void;
extra?: () => Binding[];
@@ -56,9 +58,22 @@ export class ListNav {
return;
}
this.cursor = Math.max(0, Math.min(count - 1, to));
- document
- .getElementById(this.rowId(this.cursor))
- ?.scrollIntoView({ block: "nearest" });
+ this.reveal("nearest");
+ }
+
+ reveal(block: ScrollLogicalPosition) {
+ const row = document.getElementById(this.rowId(this.cursor));
+ if (!row) {
+ return;
+ }
+ const nearTop =
+ row.getBoundingClientRect().top + window.scrollY <
+ window.innerHeight * TOP_REACH;
+ if (this.cursor === 0 && block === "nearest" && nearTop) {
+ window.scrollTo({ top: 0 });
+ return;
+ }
+ row.scrollIntoView({ block });
}
toggle(item: T | undefined = this.current) {
@@ -164,6 +179,24 @@ export class ListNav {
label: "Last row",
run: () => this.move(this.items.length - 1),
},
+ {
+ group: "List",
+ keys: "z z",
+ label: "Scroll row to the middle",
+ run: () => this.reveal("center"),
+ },
+ {
+ group: "List",
+ keys: "z t",
+ label: "Scroll row to the top",
+ run: () => this.reveal("start"),
+ },
+ {
+ group: "List",
+ keys: "z b",
+ label: "Scroll row to the bottom",
+ run: () => this.reveal("end"),
+ },
{
group: "List",
hidden: true,
diff --git a/src/routes/groups/+page.svelte b/src/routes/groups/+page.svelte
index 00c6a38..5636f29 100644
--- a/src/routes/groups/+page.svelte
+++ b/src/routes/groups/+page.svelte
@@ -139,27 +139,13 @@
},
{
group: "Groups",
- keys: "alt+j",
+ keys: "J",
label: "Move group down",
run: () => move(1),
},
{
group: "Groups",
- keys: "alt+k",
- label: "Move group up",
- run: () => move(-1),
- },
- {
- group: "Groups",
- hidden: true,
- keys: "alt+∆",
- label: "Move group down",
- run: () => move(1),
- },
- {
- group: "Groups",
- hidden: true,
- keys: "alt+˚",
+ keys: "K",
label: "Move group up",
run: () => move(-1),
},
diff --git a/src/routes/piggy-banks/+page.svelte b/src/routes/piggy-banks/+page.svelte
index 27912b7..01dabdb 100644
--- a/src/routes/piggy-banks/+page.svelte
+++ b/src/routes/piggy-banks/+page.svelte
@@ -132,27 +132,13 @@
},
{
group: "Piggy banks",
- keys: "alt+j",
+ keys: "J",
label: "Move down in group",
run: () => move(1),
},
{
group: "Piggy banks",
- keys: "alt+k",
- label: "Move up in group",
- run: () => move(-1),
- },
- {
- group: "Piggy banks",
- hidden: true,
- keys: "alt+∆",
- label: "Move down in group",
- run: () => move(1),
- },
- {
- group: "Piggy banks",
- hidden: true,
- keys: "alt+˚",
+ keys: "K",
label: "Move up in group",
run: () => move(-1),
},
diff --git a/src/routes/subscriptions/+page.svelte b/src/routes/subscriptions/+page.svelte
index 3188b85..07eb162 100644
--- a/src/routes/subscriptions/+page.svelte
+++ b/src/routes/subscriptions/+page.svelte
@@ -181,27 +181,13 @@
extra: () => [
{
group: "Subscriptions",
- keys: "alt+j",
+ keys: "J",
label: "Move down in group",
run: () => move(1),
},
{
group: "Subscriptions",
- keys: "alt+k",
- label: "Move up in group",
- run: () => move(-1),
- },
- {
- group: "Subscriptions",
- hidden: true,
- keys: "alt+∆",
- label: "Move down in group",
- run: () => move(1),
- },
- {
- group: "Subscriptions",
- hidden: true,
- keys: "alt+˚",
+ keys: "K",
label: "Move up in group",
run: () => move(-1),
},