Files
metabase/patches/kbar+0.1.0-beta.45.patch
Oleh K 7a2e92e21d Refactor command palette to distinguish between live and debounced search queries (#73921)
* Surface command palette static actions without waiting for debounce (GDGT-2409)

* Add test that static actions surface before search debounce

* Update palette tests for new processResults signature and PaletteResultList prop

* Rename appliedRemoteSearchTerm to debouncedSearchTerm, refactor debounce test

Address PR review feedback:
- Rename `appliedRemoteSearchTerm` to `debouncedSearchTerm` for clarity
- Replace the 200ms timeout in the static-actions test with an assertion
  on `/api/search` call count, decoupling the test from the debounce
  duration

* Fix command palette getting stuck on whitespace-suffixed queries

kbar feeds the raw search query to Fuse.js, whose Bitap-based scoring
penalizes the per-character length of the query. A query with trailing
whitespace (e.g. "AD.        ") tanks the score for every action below
the 0.3 threshold, leaving the panel stuck on its loading skeleton.

Set the `keywords` field on every search-derived action to kbar's raw
`searchQuery` so Fuse can match it verbatim. Combined per-key scores in
Fuse are multiplicative — a single perfect-match key drives the total
score to ~EPSILON regardless of how poorly other keys match — so a
single keyword identical to the input is enough to surface the action.

* Fix matching on leading spaces
2026-05-13 13:08:31 +02:00

1248 lines
49 KiB
Diff

diff --git a/node_modules/kbar/lib/InternalEvents.js b/node_modules/kbar/lib/InternalEvents.js
index 7900f50..a298b91 100644
--- a/node_modules/kbar/lib/InternalEvents.js
+++ b/node_modules/kbar/lib/InternalEvents.js
@@ -1,7 +1,11 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
@@ -18,13 +22,10 @@ var __importStar = (this && this.__importStar) || function (mod) {
__setModuleDefault(result, mod);
return result;
};
-var __importDefault = (this && this.__importDefault) || function (mod) {
- return (mod && mod.__esModule) ? mod : { "default": mod };
-};
Object.defineProperty(exports, "__esModule", { value: true });
exports.InternalEvents = void 0;
var React = __importStar(require("react"));
-var tinykeys_1 = __importDefault(require("./tinykeys"));
+var tinykeys_1 = require("./tinykeys");
var types_1 = require("./types");
var useKBar_1 = require("./useKBar");
var utils_1 = require("./utils");
@@ -61,7 +62,7 @@ function useToggleHandler() {
return;
}
var shortcut = options.toggleShortcut || "$mod+k";
- var unsubscribe = (0, tinykeys_1.default)(window, (_a = {},
+ var unsubscribe = (0, tinykeys_1.tinykeys)(window, (_a = {},
_a[shortcut] = function (event) {
var _a, _b, _c, _d;
if (event.defaultPrevented)
@@ -221,7 +222,7 @@ function useShortcuts() {
(_c = (_b = options.callbacks) === null || _b === void 0 ? void 0 : _b.onOpen) === null || _c === void 0 ? void 0 : _c.call(_b);
}
else {
- (_d = action.command) === null || _d === void 0 ? void 0 : _d.perform();
+ (_d = action.command) === null || _d === void 0 ? void 0 : _d.perform(event);
(_f = (_e = options.callbacks) === null || _e === void 0 ? void 0 : _e.onSelectAction) === null || _f === void 0 ? void 0 : _f.call(_e, action);
}
});
@@ -230,7 +231,7 @@ function useShortcuts() {
var action = actionsWithShortcuts_1[_b];
_loop_1(action);
}
- var unsubscribe = (0, tinykeys_1.default)(window, shortcutsMap, {
+ var unsubscribe = (0, tinykeys_1.tinykeys)(window, shortcutsMap, {
timeout: 400,
});
return function () {
diff --git a/node_modules/kbar/lib/KBarAnimator.js b/node_modules/kbar/lib/KBarAnimator.js
index 5734700..5d7ebd1 100644
--- a/node_modules/kbar/lib/KBarAnimator.js
+++ b/node_modules/kbar/lib/KBarAnimator.js
@@ -12,7 +12,11 @@ var __assign = (this && this.__assign) || function () {
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
@@ -100,10 +104,10 @@ var KBarAnimator = function (_a) {
}
outer_1.animate([
{
- height: previousHeight.current + "px",
+ height: "".concat(previousHeight.current, "px"),
},
{
- height: cr.height + "px",
+ height: "".concat(cr.height, "px"),
},
], {
duration: enterMs / 2,
diff --git a/node_modules/kbar/lib/KBarContextProvider.js b/node_modules/kbar/lib/KBarContextProvider.js
index 2d86fe5..81f08ca 100644
--- a/node_modules/kbar/lib/KBarContextProvider.js
+++ b/node_modules/kbar/lib/KBarContextProvider.js
@@ -1,7 +1,11 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
diff --git a/node_modules/kbar/lib/KBarPortal.js b/node_modules/kbar/lib/KBarPortal.js
index fb5b022..a9d1dd0 100644
--- a/node_modules/kbar/lib/KBarPortal.js
+++ b/node_modules/kbar/lib/KBarPortal.js
@@ -1,7 +1,11 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
diff --git a/node_modules/kbar/lib/KBarPositioner.js b/node_modules/kbar/lib/KBarPositioner.js
index dbbafae..0a00c4c 100644
--- a/node_modules/kbar/lib/KBarPositioner.js
+++ b/node_modules/kbar/lib/KBarPositioner.js
@@ -12,7 +12,11 @@ var __assign = (this && this.__assign) || function () {
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
diff --git a/node_modules/kbar/lib/KBarResults.js b/node_modules/kbar/lib/KBarResults.js
index a622591..6d650a6 100644
--- a/node_modules/kbar/lib/KBarResults.js
+++ b/node_modules/kbar/lib/KBarResults.js
@@ -12,7 +12,11 @@ var __assign = (this && this.__assign) || function () {
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
@@ -145,7 +149,7 @@ var KBarResults = function (props) {
overflow: "auto",
} },
React.createElement("div", { role: "listbox", id: KBarSearch_1.KBAR_LISTBOX, style: {
- height: rowVirtualizer.totalSize + "px",
+ height: "".concat(rowVirtualizer.totalSize, "px"),
width: "100%",
} }, rowVirtualizer.virtualItems.map(function (virtualRow) {
var item = itemsRef.current[virtualRow.index];
@@ -164,7 +168,7 @@ var KBarResults = function (props) {
top: 0,
left: 0,
width: "100%",
- transform: "translateY(" + virtualRow.start + "px)",
+ transform: "translateY(".concat(virtualRow.start, "px)"),
} }, handlers), React.cloneElement(props.onRender({
item: item,
active: active,
diff --git a/node_modules/kbar/lib/KBarSearch.js b/node_modules/kbar/lib/KBarSearch.js
index de5506a..a649382 100644
--- a/node_modules/kbar/lib/KBarSearch.js
+++ b/node_modules/kbar/lib/KBarSearch.js
@@ -12,7 +12,11 @@ var __assign = (this && this.__assign) || function () {
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
@@ -46,7 +50,7 @@ var React = __importStar(require("react"));
var types_1 = require("./types");
var useKBar_1 = require("./useKBar");
exports.KBAR_LISTBOX = "kbar-listbox";
-var getListboxItemId = function (id) { return "kbar-listbox-item-" + id; };
+var getListboxItemId = function (id) { return "kbar-listbox-item-".concat(id); };
exports.getListboxItemId = getListboxItemId;
function KBarSearch(props) {
var _a = (0, useKBar_1.useKBar)(function (state) { return ({
diff --git a/node_modules/kbar/lib/__tests__/useMatches.test.js b/node_modules/kbar/lib/__tests__/useMatches.test.js
index 9788f03..5f64608 100644
--- a/node_modules/kbar/lib/__tests__/useMatches.test.js
+++ b/node_modules/kbar/lib/__tests__/useMatches.test.js
@@ -15,7 +15,11 @@ var __assign = (this && this.__assign) || function () {
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
diff --git a/node_modules/kbar/lib/action/ActionImpl.js b/node_modules/kbar/lib/action/ActionImpl.js
index 42fddf4..05557e3 100644
--- a/node_modules/kbar/lib/action/ActionImpl.js
+++ b/node_modules/kbar/lib/action/ActionImpl.js
@@ -13,7 +13,7 @@ var utils_1 = require("../utils");
*/
var extendKeywords = function (_a) {
var _b = _a.keywords, keywords = _b === void 0 ? "" : _b, _c = _a.section, section = _c === void 0 ? "" : _c;
- return (keywords + " " + (typeof section === "string" ? section : section.name)).trim();
+ return "".concat(keywords, " ").concat(typeof section === "string" ? section : section.name).trim();
};
var ActionImpl = /** @class */ (function () {
function ActionImpl(action, options) {
@@ -30,7 +30,7 @@ var ActionImpl = /** @class */ (function () {
this.command =
perform &&
new Command_1.Command({
- perform: function () { return perform(_this); },
+ perform: function (e) { return perform(_this, e); },
}, {
history: options.history,
});
@@ -38,7 +38,7 @@ var ActionImpl = /** @class */ (function () {
this.perform = (_a = this.command) === null || _a === void 0 ? void 0 : _a.perform;
if (action.parent) {
var parentActionImpl = options.store[action.parent];
- (0, tiny_invariant_1.default)(parentActionImpl, "attempted to create an action whos parent: " + action.parent + " does not exist in the store.");
+ (0, tiny_invariant_1.default)(parentActionImpl, "attempted to create an action whos parent: ".concat(action.parent, " does not exist in the store."));
parentActionImpl.addChild(this);
}
}
diff --git a/node_modules/kbar/lib/action/ActionInterface.js b/node_modules/kbar/lib/action/ActionInterface.js
index 05b2b78..4ed855c 100644
--- a/node_modules/kbar/lib/action/ActionInterface.js
+++ b/node_modules/kbar/lib/action/ActionInterface.js
@@ -29,7 +29,7 @@ var ActionInterface = /** @class */ (function () {
for (var i = 0; i < actions.length; i++) {
var action = actions[i];
if (action.parent) {
- (0, tiny_invariant_1.default)(this.actions[action.parent], "Attempted to create action \"" + action.name + "\" without registering its parent \"" + action.parent + "\" first.");
+ (0, tiny_invariant_1.default)(this.actions[action.parent], "Attempted to create action \"".concat(action.name, "\" without registering its parent \"").concat(action.parent, "\" first."));
}
this.actions[action.id] = ActionImpl_1.ActionImpl.create(action, {
history: this.options.historyManager,
diff --git a/node_modules/kbar/lib/action/Command.js b/node_modules/kbar/lib/action/Command.js
index 8003f6d..9f49978 100644
--- a/node_modules/kbar/lib/action/Command.js
+++ b/node_modules/kbar/lib/action/Command.js
@@ -3,10 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
exports.Command = void 0;
var Command = /** @class */ (function () {
function Command(command, options) {
- var _this = this;
if (options === void 0) { options = {}; }
- this.perform = function () {
- var negate = command.perform();
+ var _this = this;
+ this.perform = function (e) {
+ var negate = command.perform(e);
// no need for history if non negatable
if (typeof negate !== "function")
return;
diff --git a/node_modules/kbar/lib/action/index.js b/node_modules/kbar/lib/action/index.js
index 5c43cfe..ed5855d 100644
--- a/node_modules/kbar/lib/action/index.js
+++ b/node_modules/kbar/lib/action/index.js
@@ -1,7 +1,11 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
diff --git a/node_modules/kbar/lib/index.js b/node_modules/kbar/lib/index.js
index 71c46a0..e989022 100644
--- a/node_modules/kbar/lib/index.js
+++ b/node_modules/kbar/lib/index.js
@@ -1,7 +1,11 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
diff --git a/node_modules/kbar/lib/tinykeys.d.ts b/node_modules/kbar/lib/tinykeys.d.ts
index 72c30c7..288e8bd 100644
--- a/node_modules/kbar/lib/tinykeys.d.ts
+++ b/node_modules/kbar/lib/tinykeys.d.ts
@@ -1,26 +1,74 @@
+/**
+ * A single press of a keybinding sequence
+ */
+export type KeyBindingPress = [mods: string[], key: string | RegExp];
/**
* A map of keybinding strings to event handlers.
*/
export interface KeyBindingMap {
[keybinding: string]: (event: KeyboardEvent) => void;
}
+export interface KeyBindingHandlerOptions {
+ /**
+ * Keybinding sequences will wait this long between key presses before
+ * cancelling (default: 1000).
+ *
+ * **Note:** Setting this value too low (i.e. `300`) will be too fast for many
+ * of your users.
+ */
+ timeout?: number;
+}
/**
* Options to configure the behavior of keybindings.
*/
-export interface KeyBindingOptions {
+export interface KeyBindingOptions extends KeyBindingHandlerOptions {
/**
* Key presses will listen to this event (default: "keydown").
*/
event?: "keydown" | "keyup";
/**
- * Keybinding sequences will wait this long between key presses before
- * cancelling (default: 1000).
- *
- * **Note:** Setting this value too low (i.e. `300`) will be too fast for many
- * of your users.
+ * Key presses will use a capture listener (default: false)
*/
- timeout?: number;
+ capture?: boolean;
}
+/**
+ * Parses a "Key Binding String" into its parts
+ *
+ * grammar = `<sequence>`
+ * <sequence> = `<press> <press> <press> ...`
+ * <press> = `<key>` or `<mods>+<key>`
+ * <mods> = `<mod>+<mod>+...`
+ * <key> = `<KeyboardEvent.key>` or `<KeyboardEvent.code>` (case-insensitive)
+ * <key> = `(<regex>)` -> `/^<regex>$/` (case-sensitive)
+ */
+export declare function parseKeybinding(str: string): KeyBindingPress[];
+/**
+ * This tells us if a single keyboard event matches a single keybinding press.
+ */
+export declare function matchKeyBindingPress(event: KeyboardEvent, [mods, key]: KeyBindingPress): boolean;
+/**
+ * Creates an event listener for handling keybindings.
+ *
+ * @example
+ * ```js
+ * import { createKeybindingsHandler } from "../src/keybindings"
+ *
+ * let handler = createKeybindingsHandler({
+ * "Shift+d": () => {
+ * alert("The 'Shift' and 'd' keys were pressed at the same time")
+ * },
+ * "y e e t": () => {
+ * alert("The keys 'y', 'e', 'e', and 't' were pressed in order")
+ * },
+ * "$mod+d": () => {
+ * alert("Either 'Control+d' or 'Meta+d' were pressed")
+ * },
+ * })
+ *
+ * window.addEvenListener("keydown", handler)
+ * ```
+ */
+export declare function createKeybindingsHandler(keyBindingMap: KeyBindingMap, options?: KeyBindingHandlerOptions): EventListener;
/**
* Subscribes to keybindings.
*
@@ -28,9 +76,9 @@ export interface KeyBindingOptions {
*
* @example
* ```js
- * import keybindings from "../src/keybindings"
+ * import { tinykeys } from "../src/tinykeys"
*
- * keybindings(window, {
+ * tinykeys(window, {
* "Shift+d": () => {
* alert("The 'Shift' and 'd' keys were pressed at the same time")
* },
@@ -43,4 +91,4 @@ export interface KeyBindingOptions {
* })
* ```
*/
-export default function keybindings(target: Window | HTMLElement, keyBindingMap: KeyBindingMap, options?: KeyBindingOptions): () => void;
+export declare function tinykeys(target: Window | HTMLElement, keyBindingMap: KeyBindingMap, { event, capture, timeout }?: KeyBindingOptions): () => void;
diff --git a/node_modules/kbar/lib/tinykeys.js b/node_modules/kbar/lib/tinykeys.js
index 97d1fb3..0596765 100644
--- a/node_modules/kbar/lib/tinykeys.js
+++ b/node_modules/kbar/lib/tinykeys.js
@@ -1,7 +1,6 @@
"use strict";
-// Fixes special character issues; `?` -> `shift+/` + build issue
-// https://github.com/jamiebuilds/tinykeys
Object.defineProperty(exports, "__esModule", { value: true });
+exports.tinykeys = exports.createKeybindingsHandler = exports.matchKeyBindingPress = exports.parseKeybinding = void 0;
/**
* These are the modifier keys that change the meaning of keybindings.
*
@@ -17,20 +16,33 @@ var DEFAULT_TIMEOUT = 1000;
* Keybinding sequences should bind to this event by default.
*/
var DEFAULT_EVENT = "keydown";
+/**
+ * Platform detection code.
+ * @see https://github.com/jamiebuilds/tinykeys/issues/184
+ */
+var PLATFORM = typeof navigator === "object" ? navigator.platform : "";
+var APPLE_DEVICE = /Mac|iPod|iPhone|iPad/.test(PLATFORM);
/**
* An alias for creating platform-specific keybinding aliases.
*/
-var MOD = typeof navigator === "object" &&
- /Mac|iPod|iPhone|iPad/.test(navigator.platform)
- ? "Meta"
- : "Control";
+var MOD = APPLE_DEVICE ? "Meta" : "Control";
+/**
+ * Meaning of `AltGraph`, from MDN:
+ * - Windows: Both Alt and Ctrl keys are pressed, or AltGr key is pressed
+ * - Mac: ⌥ Option key pressed
+ * - Linux: Level 3 Shift key (or Level 5 Shift key) pressed
+ * - Android: Not supported
+ * @see https://github.com/jamiebuilds/tinykeys/issues/185
+ */
+var ALT_GRAPH_ALIASES = PLATFORM === "Win32" ? ["Control", "Alt"] : APPLE_DEVICE ? ["Alt"] : [];
/**
* There's a bug in Chrome that causes event.getModifierState not to exist on
* KeyboardEvent's for F1/F2/etc keys.
*/
function getModifierState(event, mod) {
return typeof event.getModifierState === "function"
- ? event.getModifierState(mod)
+ ? event.getModifierState(mod) ||
+ (ALT_GRAPH_ALIASES.includes(mod) && event.getModifierState("AltGraph"))
: false;
}
/**
@@ -40,55 +52,58 @@ function getModifierState(event, mod) {
* <sequence> = `<press> <press> <press> ...`
* <press> = `<key>` or `<mods>+<key>`
* <mods> = `<mod>+<mod>+...`
+ * <key> = `<KeyboardEvent.key>` or `<KeyboardEvent.code>` (case-insensitive)
+ * <key> = `(<regex>)` -> `/^<regex>$/` (case-sensitive)
*/
-function parse(str) {
+function parseKeybinding(str) {
return str
.trim()
.split(" ")
.map(function (press) {
var mods = press.split(/\b\+/);
var key = mods.pop();
+ var match = key.match(/^\((.+)\)$/);
+ if (match) {
+ key = new RegExp("^".concat(match[1], "$"));
+ }
mods = mods.map(function (mod) { return (mod === "$mod" ? MOD : mod); });
return [mods, key];
});
}
+exports.parseKeybinding = parseKeybinding;
/**
- * This tells us if a series of events matches a key binding sequence either
- * partially or exactly.
+ * This tells us if a single keyboard event matches a single keybinding press.
*/
-function match(event, press) {
- // Special characters; `?` `!`
- if (/^[^A-Za-z0-9]$/.test(event.key) && press[1] === event.key) {
- return true;
- }
+function matchKeyBindingPress(event, _a) {
+ var mods = _a[0], key = _a[1];
// prettier-ignore
return !(
// Allow either the `event.key` or the `event.code`
// MDN event.key: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
// MDN event.code: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code
- (press[1].toUpperCase() !== event.key.toUpperCase() &&
- press[1] !== event.code) ||
+ (key instanceof RegExp ? !(key.test(event.key) || key.test(event.code)) :
+ (key.toUpperCase() !== event.key.toUpperCase() &&
+ key !== event.code)) ||
// Ensure all the modifiers in the keybinding are pressed.
- press[0].find(function (mod) {
+ mods.find(function (mod) {
return !getModifierState(event, mod);
}) ||
// KEYBINDING_MODIFIER_KEYS (Shift/Control/etc) change the meaning of a
// keybinding. So if they are pressed but aren't part of the current
// keybinding press, then we don't have a match.
KEYBINDING_MODIFIER_KEYS.find(function (mod) {
- return !press[0].includes(mod) && press[1] !== mod && getModifierState(event, mod);
+ return !mods.includes(mod) && key !== mod && getModifierState(event, mod);
}));
}
+exports.matchKeyBindingPress = matchKeyBindingPress;
/**
- * Subscribes to keybindings.
- *
- * Returns an unsubscribe method.
+ * Creates an event listener for handling keybindings.
*
* @example
* ```js
- * import keybindings from "../src/keybindings"
+ * import { createKeybindingsHandler } from "../src/keybindings"
*
- * keybindings(window, {
+ * let handler = createKeybindingsHandler({
* "Shift+d": () => {
* alert("The 'Shift' and 'd' keys were pressed at the same time")
* },
@@ -99,19 +114,20 @@ function match(event, press) {
* alert("Either 'Control+d' or 'Meta+d' were pressed")
* },
* })
+ *
+ * window.addEvenListener("keydown", handler)
* ```
*/
-function keybindings(target, keyBindingMap, options) {
- var _a, _b;
+function createKeybindingsHandler(keyBindingMap, options) {
+ var _a;
if (options === void 0) { options = {}; }
var timeout = (_a = options.timeout) !== null && _a !== void 0 ? _a : DEFAULT_TIMEOUT;
- var event = (_b = options.event) !== null && _b !== void 0 ? _b : DEFAULT_EVENT;
var keyBindings = Object.keys(keyBindingMap).map(function (key) {
- return [parse(key), keyBindingMap[key]];
+ return [parseKeybinding(key), keyBindingMap[key]];
});
var possibleMatches = new Map();
var timer = null;
- var onKeyEvent = function (event) {
+ return function (event) {
// Ensure and stop any event that isn't a full keyboard event.
// Autocomplete option navigation and selection would fire a instanceof Event,
// instead of the expected KeyboardEvent
@@ -124,7 +140,7 @@ function keybindings(target, keyBindingMap, options) {
var prev = possibleMatches.get(sequence);
var remainingExpectedPresses = prev ? prev : sequence;
var currentExpectedPress = remainingExpectedPresses[0];
- var matches = match(event, currentExpectedPress);
+ var matches = matchKeyBindingPress(event, currentExpectedPress);
if (!matches) {
// Modifier keydown events shouldn't break sequences
// Note: This works because:
@@ -146,12 +162,38 @@ function keybindings(target, keyBindingMap, options) {
if (timer) {
clearTimeout(timer);
}
- // @ts-ignore
timer = setTimeout(possibleMatches.clear.bind(possibleMatches), timeout);
};
- target.addEventListener(event, onKeyEvent);
+}
+exports.createKeybindingsHandler = createKeybindingsHandler;
+/**
+ * Subscribes to keybindings.
+ *
+ * Returns an unsubscribe method.
+ *
+ * @example
+ * ```js
+ * import { tinykeys } from "../src/tinykeys"
+ *
+ * tinykeys(window, {
+ * "Shift+d": () => {
+ * alert("The 'Shift' and 'd' keys were pressed at the same time")
+ * },
+ * "y e e t": () => {
+ * alert("The keys 'y', 'e', 'e', and 't' were pressed in order")
+ * },
+ * "$mod+d": () => {
+ * alert("Either 'Control+d' or 'Meta+d' were pressed")
+ * },
+ * })
+ * ```
+ */
+function tinykeys(target, keyBindingMap, _a) {
+ var _b = _a === void 0 ? {} : _a, _c = _b.event, event = _c === void 0 ? DEFAULT_EVENT : _c, capture = _b.capture, timeout = _b.timeout;
+ var onKeyEvent = createKeybindingsHandler(keyBindingMap, { timeout: timeout });
+ target.addEventListener(event, onKeyEvent, capture);
return function () {
- target.removeEventListener(event, onKeyEvent);
+ target.removeEventListener(event, onKeyEvent, capture);
};
}
-exports.default = keybindings;
+exports.tinykeys = tinykeys;
diff --git a/node_modules/kbar/lib/types.d.ts b/node_modules/kbar/lib/types.d.ts
index 0f55bc8..ee411f2 100644
--- a/node_modules/kbar/lib/types.d.ts
+++ b/node_modules/kbar/lib/types.d.ts
@@ -1,12 +1,12 @@
import * as React from "react";
import { ActionImpl } from "./action/ActionImpl";
-export declare type ActionId = string;
-export declare type Priority = number;
-export declare type ActionSection = string | {
+export type ActionId = string;
+export type Priority = number;
+export type ActionSection = string | {
name: string;
priority: Priority;
};
-export declare type Action = {
+export type Action = {
id: ActionId;
name: string;
shortcut?: string[];
@@ -14,12 +14,12 @@ export declare type Action = {
section?: ActionSection;
icon?: string | React.ReactElement | React.ReactNode;
subtitle?: string;
- perform?: (currentActionImpl: ActionImpl) => any;
+ perform?: (currentActionImpl: ActionImpl, event?: KeyboardEvent) => any;
parent?: ActionId;
priority?: Priority;
};
-export declare type ActionStore = Record<ActionId, ActionImpl>;
-export declare type ActionTree = Record<string, ActionImpl>;
+export type ActionStore = Record<ActionId, ActionImpl>;
+export type ActionTree = Record<string, ActionImpl>;
export interface ActionGroup {
name: string;
actions: ActionImpl[];
diff --git a/node_modules/kbar/lib/types.js b/node_modules/kbar/lib/types.js
index 5904550..15e5c1f 100644
--- a/node_modules/kbar/lib/types.js
+++ b/node_modules/kbar/lib/types.js
@@ -7,4 +7,4 @@ var VisualState;
VisualState["showing"] = "showing";
VisualState["animatingOut"] = "animating-out";
VisualState["hidden"] = "hidden";
-})(VisualState = exports.VisualState || (exports.VisualState = {}));
+})(VisualState || (exports.VisualState = VisualState = {}));
diff --git a/node_modules/kbar/lib/useKBar.d.ts b/node_modules/kbar/lib/useKBar.d.ts
index 659e886..837df41 100644
--- a/node_modules/kbar/lib/useKBar.d.ts
+++ b/node_modules/kbar/lib/useKBar.d.ts
@@ -3,6 +3,6 @@ interface BaseKBarReturnType {
query: KBarQuery;
options: KBarOptions;
}
-declare type useKBarReturnType<S = null> = S extends null ? BaseKBarReturnType : S & BaseKBarReturnType;
+type useKBarReturnType<S = null> = S extends null ? BaseKBarReturnType : S & BaseKBarReturnType;
export declare function useKBar<C = null>(collector?: (state: KBarState) => C): useKBarReturnType<C>;
export {};
diff --git a/node_modules/kbar/lib/useKBar.js b/node_modules/kbar/lib/useKBar.js
index fbe81e5..71406f7 100644
--- a/node_modules/kbar/lib/useKBar.js
+++ b/node_modules/kbar/lib/useKBar.js
@@ -12,7 +12,11 @@ var __assign = (this && this.__assign) || function () {
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
diff --git a/node_modules/kbar/lib/useMatches.js b/node_modules/kbar/lib/useMatches.js
index 0d3b4e7..0571622 100644
--- a/node_modules/kbar/lib/useMatches.js
+++ b/node_modules/kbar/lib/useMatches.js
@@ -1,7 +1,11 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
@@ -51,7 +55,7 @@ var fuseOptions = {
ignoreLocation: true,
includeScore: true,
includeMatches: true,
- threshold: 0.2,
+ threshold: 0.3,
minMatchCharLength: 1,
};
function order(a, b) {
@@ -190,12 +194,12 @@ function useInternalMatches(filtered, search, fuse) {
}
var matches = [];
// Use Fuse's `search` method to perform the search efficiently
- var searchResults = fuse.search(throttledSearch);
+ var searchResults = fuse.search(throttledSearch.trim());
// Format the search results to match the existing structure
matches = searchResults.map(function (_a) {
var action = _a.item, score = _a.score;
return ({
- score: 1 / ((score !== null && score !== void 0 ? score : 0) + 1),
+ score: 1 / ((score !== null && score !== void 0 ? score : 0) + 1), // Convert the Fuse score to the format used in the original code
action: action,
});
});
diff --git a/node_modules/kbar/lib/useRegisterActions.js b/node_modules/kbar/lib/useRegisterActions.js
index 1385827..03045ec 100644
--- a/node_modules/kbar/lib/useRegisterActions.js
+++ b/node_modules/kbar/lib/useRegisterActions.js
@@ -1,7 +1,11 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
diff --git a/node_modules/kbar/lib/useStore.d.ts b/node_modules/kbar/lib/useStore.d.ts
index 43e6778..7db7045 100644
--- a/node_modules/kbar/lib/useStore.d.ts
+++ b/node_modules/kbar/lib/useStore.d.ts
@@ -1,4 +1,4 @@
import type { IKBarContext, KBarProviderProps } from "./types";
-declare type useStoreProps = KBarProviderProps;
+type useStoreProps = KBarProviderProps;
export declare function useStore(props: useStoreProps): IKBarContext;
export {};
diff --git a/node_modules/kbar/lib/useStore.js b/node_modules/kbar/lib/useStore.js
index 3209604..7861e41 100644
--- a/node_modules/kbar/lib/useStore.js
+++ b/node_modules/kbar/lib/useStore.js
@@ -12,7 +12,11 @@ var __assign = (this && this.__assign) || function () {
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
diff --git a/node_modules/kbar/lib/utils.js b/node_modules/kbar/lib/utils.js
index d515424..2663d65 100644
--- a/node_modules/kbar/lib/utils.js
+++ b/node_modules/kbar/lib/utils.js
@@ -12,7 +12,11 @@ var __assign = (this && this.__assign) || function () {
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
diff --git a/node_modules/kbar/src/InternalEvents.tsx b/node_modules/kbar/src/InternalEvents.tsx
index 652ce9b..f1a7236 100644
--- a/node_modules/kbar/src/InternalEvents.tsx
+++ b/node_modules/kbar/src/InternalEvents.tsx
@@ -1,6 +1,6 @@
import * as React from "react";
import { ActionImpl } from "./action";
-import tinykeys from "./tinykeys";
+import {tinykeys} from "./tinykeys";
import { VisualState } from "./types";
import { useKBar } from "./useKBar";
import { getScrollbarWidth, shouldRejectKeystrokes } from "./utils";
@@ -218,7 +218,7 @@ function useShortcuts() {
query.toggle();
options.callbacks?.onOpen?.();
} else {
- action.command?.perform();
+ action.command?.perform(event);
options.callbacks?.onSelectAction?.(action);
}
});
diff --git a/node_modules/kbar/src/action/ActionImpl.ts b/node_modules/kbar/src/action/ActionImpl.ts
index 07a4d00..ef8b9a7 100644
--- a/node_modules/kbar/src/action/ActionImpl.ts
+++ b/node_modules/kbar/src/action/ActionImpl.ts
@@ -2,6 +2,7 @@ import invariant from "tiny-invariant";
import { Command } from "./Command";
import type { Action, ActionStore, History } from "../types";
import { Priority } from "../utils";
+import { KeyboardEvent } from "react";
interface ActionImplOptions {
store: ActionStore;
@@ -49,7 +50,7 @@ export class ActionImpl implements Action {
perform &&
new Command(
{
- perform: () => perform(this),
+ perform: (e?: KeyboardEvent) => perform(this, e),
},
{
history: options.history,
diff --git a/node_modules/kbar/src/action/Command.ts b/node_modules/kbar/src/action/Command.ts
index 34f954d..625a0b8 100644
--- a/node_modules/kbar/src/action/Command.ts
+++ b/node_modules/kbar/src/action/Command.ts
@@ -18,8 +18,8 @@ export class Command {
command: { perform: Command["perform"] },
options: CommandOptions = {}
) {
- this.perform = () => {
- const negate = command.perform();
+ this.perform = (e?: KeyboardEvent) => {
+ const negate = command.perform(e);
// no need for history if non negatable
if (typeof negate !== "function") return;
// return if no history enabled
diff --git a/node_modules/kbar/src/tinykeys.ts b/node_modules/kbar/src/tinykeys.ts
index 0ba405f..942f15d 100644
--- a/node_modules/kbar/src/tinykeys.ts
+++ b/node_modules/kbar/src/tinykeys.ts
@@ -1,32 +1,39 @@
-// Fixes special character issues; `?` -> `shift+/` + build issue
-// https://github.com/jamiebuilds/tinykeys
-
-type KeyBindingPress = [string[], string];
+/**
+ * A single press of a keybinding sequence
+ */
+export type KeyBindingPress = [mods: string[], key: string | RegExp]
/**
* A map of keybinding strings to event handlers.
*/
export interface KeyBindingMap {
- [keybinding: string]: (event: KeyboardEvent) => void;
+ [keybinding: string]: (event: KeyboardEvent) => void
+}
+
+export interface KeyBindingHandlerOptions {
+ /**
+ * Keybinding sequences will wait this long between key presses before
+ * cancelling (default: 1000).
+ *
+ * **Note:** Setting this value too low (i.e. `300`) will be too fast for many
+ * of your users.
+ */
+ timeout?: number
}
/**
* Options to configure the behavior of keybindings.
*/
-export interface KeyBindingOptions {
- /**
- * Key presses will listen to this event (default: "keydown").
- */
- event?: "keydown" | "keyup";
-
- /**
- * Keybinding sequences will wait this long between key presses before
- * cancelling (default: 1000).
- *
- * **Note:** Setting this value too low (i.e. `300`) will be too fast for many
- * of your users.
- */
- timeout?: number;
+export interface KeyBindingOptions extends KeyBindingHandlerOptions {
+ /**
+ * Key presses will listen to this event (default: "keydown").
+ */
+ event?: "keydown" | "keyup"
+
+ /**
+ * Key presses will use a capture listener (default: false)
+ */
+ capture?: boolean
}
/**
@@ -34,36 +41,51 @@ export interface KeyBindingOptions {
*
* Note: Ignoring "AltGraph" because it is covered by the others.
*/
-let KEYBINDING_MODIFIER_KEYS = ["Shift", "Meta", "Alt", "Control"];
+let KEYBINDING_MODIFIER_KEYS = ["Shift", "Meta", "Alt", "Control"]
/**
* Keybinding sequences should timeout if individual key presses are more than
* 1s apart by default.
*/
-let DEFAULT_TIMEOUT = 1000;
+let DEFAULT_TIMEOUT = 1000
/**
* Keybinding sequences should bind to this event by default.
*/
-let DEFAULT_EVENT = "keydown";
+let DEFAULT_EVENT = "keydown" as const
+
+/**
+ * Platform detection code.
+ * @see https://github.com/jamiebuilds/tinykeys/issues/184
+ */
+let PLATFORM = typeof navigator === "object" ? navigator.platform : ""
+let APPLE_DEVICE = /Mac|iPod|iPhone|iPad/.test(PLATFORM)
/**
* An alias for creating platform-specific keybinding aliases.
*/
-let MOD =
- typeof navigator === "object" &&
- /Mac|iPod|iPhone|iPad/.test(navigator.platform)
- ? "Meta"
- : "Control";
+let MOD = APPLE_DEVICE ? "Meta" : "Control"
+
+/**
+ * Meaning of `AltGraph`, from MDN:
+ * - Windows: Both Alt and Ctrl keys are pressed, or AltGr key is pressed
+ * - Mac: ⌥ Option key pressed
+ * - Linux: Level 3 Shift key (or Level 5 Shift key) pressed
+ * - Android: Not supported
+ * @see https://github.com/jamiebuilds/tinykeys/issues/185
+ */
+let ALT_GRAPH_ALIASES =
+ PLATFORM === "Win32" ? ["Control", "Alt"] : APPLE_DEVICE ? ["Alt"] : []
/**
* There's a bug in Chrome that causes event.getModifierState not to exist on
* KeyboardEvent's for F1/F2/etc keys.
*/
function getModifierState(event: KeyboardEvent, mod: string) {
- return typeof event.getModifierState === "function"
- ? event.getModifierState(mod)
- : false;
+ return typeof event.getModifierState === "function"
+ ? event.getModifierState(mod) ||
+ (ALT_GRAPH_ALIASES.includes(mod) && event.getModifierState("AltGraph"))
+ : false
}
/**
@@ -73,41 +95,45 @@ function getModifierState(event: KeyboardEvent, mod: string) {
* <sequence> = `<press> <press> <press> ...`
* <press> = `<key>` or `<mods>+<key>`
* <mods> = `<mod>+<mod>+...`
+ * <key> = `<KeyboardEvent.key>` or `<KeyboardEvent.code>` (case-insensitive)
+ * <key> = `(<regex>)` -> `/^<regex>$/` (case-sensitive)
*/
-function parse(str: string): KeyBindingPress[] {
- return str
- .trim()
- .split(" ")
- .map((press) => {
- let mods = press.split(/\b\+/);
- let key = mods.pop() as string;
- mods = mods.map((mod) => (mod === "$mod" ? MOD : mod));
- return [mods, key];
- });
+export function parseKeybinding(str: string): KeyBindingPress[] {
+ return str
+ .trim()
+ .split(" ")
+ .map(press => {
+ let mods = press.split(/\b\+/)
+ let key: string | RegExp = mods.pop() as string
+ let match = key.match(/^\((.+)\)$/)
+ if (match) {
+ key = new RegExp(`^${match[1]}$`)
+ }
+ mods = mods.map(mod => (mod === "$mod" ? MOD : mod))
+ return [mods, key]
+ })
}
/**
- * This tells us if a series of events matches a key binding sequence either
- * partially or exactly.
+ * This tells us if a single keyboard event matches a single keybinding press.
*/
-function match(event: KeyboardEvent, press: KeyBindingPress): boolean {
- // Special characters; `?` `!`
- if (/^[^A-Za-z0-9]$/.test(event.key) && press[1] === event.key) {
- return true;
- }
-
- // prettier-ignore
- return !(
+export function matchKeyBindingPress(
+ event: KeyboardEvent,
+ [mods, key]: KeyBindingPress,
+): boolean {
+ // prettier-ignore
+ return !(
// Allow either the `event.key` or the `event.code`
// MDN event.key: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
// MDN event.code: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code
(
- press[1].toUpperCase() !== event.key.toUpperCase() &&
- press[1] !== event.code
+ key instanceof RegExp ? !(key.test(event.key) || key.test(event.code)) :
+ (key.toUpperCase() !== event.key.toUpperCase() &&
+ key !== event.code)
) ||
// Ensure all the modifiers in the keybinding are pressed.
- press[0].find(mod => {
+ mods.find(mod => {
return !getModifierState(event, mod)
}) ||
@@ -115,11 +141,89 @@ function match(event: KeyboardEvent, press: KeyBindingPress): boolean {
// keybinding. So if they are pressed but aren't part of the current
// keybinding press, then we don't have a match.
KEYBINDING_MODIFIER_KEYS.find(mod => {
- return !press[0].includes(mod) && press[1] !== mod && getModifierState(event, mod)
+ return !mods.includes(mod) && key !== mod && getModifierState(event, mod)
})
)
}
+/**
+ * Creates an event listener for handling keybindings.
+ *
+ * @example
+ * ```js
+ * import { createKeybindingsHandler } from "../src/keybindings"
+ *
+ * let handler = createKeybindingsHandler({
+ * "Shift+d": () => {
+ * alert("The 'Shift' and 'd' keys were pressed at the same time")
+ * },
+ * "y e e t": () => {
+ * alert("The keys 'y', 'e', 'e', and 't' were pressed in order")
+ * },
+ * "$mod+d": () => {
+ * alert("Either 'Control+d' or 'Meta+d' were pressed")
+ * },
+ * })
+ *
+ * window.addEvenListener("keydown", handler)
+ * ```
+ */
+export function createKeybindingsHandler(
+ keyBindingMap: KeyBindingMap,
+ options: KeyBindingHandlerOptions = {},
+): EventListener {
+ let timeout = options.timeout ?? DEFAULT_TIMEOUT
+
+ let keyBindings = Object.keys(keyBindingMap).map(key => {
+ return [parseKeybinding(key), keyBindingMap[key]] as const
+ })
+
+ let possibleMatches = new Map<KeyBindingPress[], KeyBindingPress[]>()
+ let timer: number | null = null
+
+ return event => {
+ // Ensure and stop any event that isn't a full keyboard event.
+ // Autocomplete option navigation and selection would fire a instanceof Event,
+ // instead of the expected KeyboardEvent
+ if (!(event instanceof KeyboardEvent)) {
+ return
+ }
+
+ keyBindings.forEach(keyBinding => {
+ let sequence = keyBinding[0]
+ let callback = keyBinding[1]
+
+ let prev = possibleMatches.get(sequence)
+ let remainingExpectedPresses = prev ? prev : sequence
+ let currentExpectedPress = remainingExpectedPresses[0]
+
+ let matches = matchKeyBindingPress(event, currentExpectedPress)
+
+ if (!matches) {
+ // Modifier keydown events shouldn't break sequences
+ // Note: This works because:
+ // - non-modifiers will always return false
+ // - if the current keypress is a modifier then it will return true when we check its state
+ // MDN: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/getModifierState
+ if (!getModifierState(event, event.key)) {
+ possibleMatches.delete(sequence)
+ }
+ } else if (remainingExpectedPresses.length > 1) {
+ possibleMatches.set(sequence, remainingExpectedPresses.slice(1))
+ } else {
+ possibleMatches.delete(sequence)
+ callback(event)
+ }
+ })
+
+ if (timer) {
+ clearTimeout(timer)
+ }
+
+ timer = setTimeout(possibleMatches.clear.bind(possibleMatches), timeout)
+ }
+}
+
/**
* Subscribes to keybindings.
*
@@ -127,9 +231,9 @@ function match(event: KeyboardEvent, press: KeyBindingPress): boolean {
*
* @example
* ```js
- * import keybindings from "../src/keybindings"
+ * import { tinykeys } from "../src/tinykeys"
*
- * keybindings(window, {
+ * tinykeys(window, {
* "Shift+d": () => {
* alert("The 'Shift' and 'd' keys were pressed at the same time")
* },
@@ -142,67 +246,14 @@ function match(event: KeyboardEvent, press: KeyBindingPress): boolean {
* })
* ```
*/
-export default function keybindings(
- target: Window | HTMLElement,
- keyBindingMap: KeyBindingMap,
- options: KeyBindingOptions = {}
+export function tinykeys(
+ target: Window | HTMLElement,
+ keyBindingMap: KeyBindingMap,
+ { event = DEFAULT_EVENT, capture, timeout }: KeyBindingOptions = {},
): () => void {
- let timeout = options.timeout ?? DEFAULT_TIMEOUT;
- let event = options.event ?? DEFAULT_EVENT;
-
- let keyBindings = Object.keys(keyBindingMap).map((key) => {
- return [parse(key), keyBindingMap[key]] as const;
- });
-
- let possibleMatches = new Map<KeyBindingPress[], KeyBindingPress[]>();
- let timer: number | null = null;
-
- let onKeyEvent: EventListener = (event) => {
- // Ensure and stop any event that isn't a full keyboard event.
- // Autocomplete option navigation and selection would fire a instanceof Event,
- // instead of the expected KeyboardEvent
- if (!(event instanceof KeyboardEvent)) {
- return;
- }
-
- keyBindings.forEach((keyBinding) => {
- let sequence = keyBinding[0];
- let callback = keyBinding[1];
-
- let prev = possibleMatches.get(sequence);
- let remainingExpectedPresses = prev ? prev : sequence;
- let currentExpectedPress = remainingExpectedPresses[0];
-
- let matches = match(event, currentExpectedPress);
-
- if (!matches) {
- // Modifier keydown events shouldn't break sequences
- // Note: This works because:
- // - non-modifiers will always return false
- // - if the current keypress is a modifier then it will return true when we check its state
- // MDN: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/getModifierState
- if (!getModifierState(event, event.key)) {
- possibleMatches.delete(sequence);
- }
- } else if (remainingExpectedPresses.length > 1) {
- possibleMatches.set(sequence, remainingExpectedPresses.slice(1));
- } else {
- possibleMatches.delete(sequence);
- callback(event);
- }
- });
-
- if (timer) {
- clearTimeout(timer);
- }
-
- // @ts-ignore
- timer = setTimeout(possibleMatches.clear.bind(possibleMatches), timeout);
- };
-
- target.addEventListener(event, onKeyEvent);
-
- return () => {
- target.removeEventListener(event, onKeyEvent);
- };
+ let onKeyEvent = createKeybindingsHandler(keyBindingMap, { timeout })
+ target.addEventListener(event, onKeyEvent, capture)
+ return () => {
+ target.removeEventListener(event, onKeyEvent, capture)
+ }
}
diff --git a/node_modules/kbar/src/types.ts b/node_modules/kbar/src/types.ts
index 0910420..eb0bf8c 100644
--- a/node_modules/kbar/src/types.ts
+++ b/node_modules/kbar/src/types.ts
@@ -20,7 +20,7 @@ export type Action = {
section?: ActionSection;
icon?: string | React.ReactElement | React.ReactNode;
subtitle?: string;
- perform?: (currentActionImpl: ActionImpl) => any;
+ perform?: (currentActionImpl: ActionImpl, event?: KeyboardEvent) => any;
parent?: ActionId;
priority?: Priority;
};