jamiebuilds / tinykeys

A tiny (~400 B) & modern library for keybindings.

Home Page:https://jamiebuilds.github.io/tinykeys/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Shortcut conflicts

rockwotj opened this issue · comments

If I have a shortcut that is g a (go to all mail) and a (archive) then they are both fired when I press g a I would expect only g a to fire in this case.

Hm, yeah this is an unfortunate side effect of not breaking out of the matching when a match is found.

There are a couple ways that this could be fixed:

  1. Use the order of keys to break the search:
  • Pro: Probably the cheapest (size-wise) to implement
  • Con: Can create some unexpected results based on the order
  • Neutral: Can't say if its easier or harder to understand
// when typing "g" then "a"
tinykeys({
  "g a": () => console.log("matches"),
  "a": () => console.log("doesnt match"),
})

// and inversely

// when typing "g" then "a"
tinykeys({
  "a": () => console.log("matches"),
  "g a": () => console.log("doesnt match"),
})
  1. Create some other API for sequences that "scopes" them
  • Neutral: Would add a little bit of overhead to implement
  • Pro: People's expectations would probably be correct
  • Neutral: It might be a bit confusing?
// when typing "g" then "a"
tinykeys({
  "a": () => console.log("doesnt match"),
  "g": {
    "a": () => console.log("matches"),
  },
})

Right now I'm leaning more towards the second option... thoughts?

Other downside of Option 2 is that you can't have just a "g" keybinding:

// when typing "g" then "a"
tinykeys({
  "a": () => console.log("doesnt match"),
  "g": () => console.log("doesnt match"), // technically this just gets overwritten and forgotten
  "g": {
    "a": () => console.log("matches"),
  },
})

Maybe it could just be:

// when typing "g" then "a"
tinykeys({
  "a": () => console.log("doesnt match"),
  "g": {
    "": () => console.log("matches"),
    "a": () => console.log("matches"),
  },
})

Yeah I think that Option 2 is the most preferred. We've been working around this by ordering them correctly and ignoring events that have already been handled (because in the case below the default is that both handlers run). Our code looks something like:

const handled = new WeakSet();
function wrap(handler) {
  return (evt) => {
    if (handled.has(evt)) return;
    handler();
    handled.add(evt);
  };
}

tinykeys({
  "g a": wrap(() => console.log("matches")),
  "a": wrap(() => console.log("doesnt match")),
})