Skip to content

Commit

Permalink
Migrate from [hotkeys](https://github.com/jaywcjlove/hotkeys-js/) to …
Browse files Browse the repository at this point in the history
…[tinykeys](https://github.com/jamiebuilds/tinykeys)

BC: replacing comma (`,`) separated hotkeys with `|`
  • Loading branch information
OzzyCzech committed Dec 6, 2023
1 parent ad81938 commit a60eb12
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 72 deletions.
37 changes: 27 additions & 10 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@
<body class="bg-neutral-800 text-neutral-300 antialiased leading-normal tracking-normal">
<main class="flex h-screen items-center justify-center">


<code class="text-[8rem] font-bold">⌘+K</code>

<cmd-dialog
placeholder="Search for actions"
hotkey="ctrl+k,cmd+k"
hotkey="$mod+k|$mod+j"
note="<a href=https://github.com/OzzyCzech/cmd-dialog>cmd-dialog demo</a>"
></cmd-dialog>

Expand All @@ -32,38 +33,54 @@
hotkey: '/',
onAction(event) {
window.location.href = '/';
event.preventDefault();
},
},
{
title: 'GitHub',
img: `<svg viewBox="0 0 256 249" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet"><g fill="currentColor"><path d="M127.505 0C57.095 0 0 57.085 0 127.505c0 56.336 36.534 104.13 87.196 120.99 6.372 1.18 8.712-2.766 8.712-6.134 0-3.04-.119-13.085-.173-23.739-35.473 7.713-42.958-15.044-42.958-15.044-5.8-14.738-14.157-18.656-14.157-18.656-11.568-7.914.872-7.752.872-7.752 12.804.9 19.546 13.14 19.546 13.14 11.372 19.493 29.828 13.857 37.104 10.6 1.144-8.242 4.449-13.866 8.095-17.05-28.32-3.225-58.092-14.158-58.092-63.014 0-13.92 4.981-25.295 13.138-34.224-1.324-3.212-5.688-16.18 1.235-33.743 0 0 10.707-3.427 35.073 13.07 10.17-2.826 21.078-4.242 31.914-4.29 10.836.048 21.752 1.464 31.942 4.29 24.337-16.497 35.029-13.07 35.029-13.07 6.94 17.563 2.574 30.531 1.25 33.743 8.175 8.929 13.122 20.303 13.122 34.224 0 48.972-29.828 59.756-58.22 62.912 4.573 3.957 8.648 11.717 8.648 23.612 0 17.06-.148 30.791-.148 34.991 0 3.393 2.295 7.369 8.759 6.117 50.634-16.879 87.122-64.656 87.122-120.973C255.009 57.085 197.922 0 127.505 0"/><path d="M47.755 181.634c-.28.633-1.278.823-2.185.389-.925-.416-1.445-1.28-1.145-1.916.275-.652 1.273-.834 2.196-.396.927.415 1.455 1.287 1.134 1.923M54.027 187.23c-.608.564-1.797.302-2.604-.589-.834-.889-.99-2.077-.373-2.65.627-.563 1.78-.3 2.616.59.834.899.996 2.08.36 2.65M58.33 194.39c-.782.543-2.06.034-2.849-1.1-.781-1.133-.781-2.493.017-3.038.792-.545 2.05-.055 2.85 1.07.78 1.153.78 2.513-.019 3.069M65.606 202.683c-.699.77-2.187.564-3.277-.488-1.114-1.028-1.425-2.487-.724-3.258.707-.772 2.204-.555 3.302.488 1.107 1.026 1.445 2.496.7 3.258M75.01 205.483c-.307.998-1.741 1.452-3.185 1.028-1.442-.437-2.386-1.607-2.095-2.616.3-1.005 1.74-1.478 3.195-1.024 1.44.435 2.386 1.596 2.086 2.612M85.714 206.67c.036 1.052-1.189 1.924-2.705 1.943-1.525.033-2.758-.818-2.774-1.852 0-1.062 1.197-1.926 2.721-1.951 1.516-.03 2.758.815 2.758 1.86M96.228 206.267c.182 1.026-.872 2.08-2.377 2.36-1.48.27-2.85-.363-3.039-1.38-.184-1.052.89-2.105 2.367-2.378 1.508-.262 2.857.355 3.049 1.398"/></g></svg>`,
hotkey: 'cmd+g',
hotkey: '$mod+g',
description: 'Check source code on GitHub',
onAction() {
onAction(event) {
window.open('https://github.com/OzzyCzech/cmd-dialog', '_blank');
event.preventDefault();
},
},
{
title: 'Email me',
hotkey: 'cmd+e',
hotkey: '$mod+e',
description: 'at roman@ozana.cz',
img: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M0 0h24v24H0z" fill="none"/><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg>`,
tags: ['email'],
onAction() {
onAction(event) {
window.location.href = 'mailto:roman@ozana.cz';
event.preventDefault()
},
},
);

actions.unshift(
{
// action that is triggered outside the dialog
actions.push({
title: 'Outside action',
hotkey: {key: 'x', scope: 'outside'},
description: 'Open dialog',
hotkey: 'x',
description: 'Do something only outside the dialog',
onAction() {
alert('Default oustide action triggered with hotkey "x"');
if (dialog.isOpen) {
console.log('Dialog is open: do nothing...');
} else {
alert('Dialog is closed: open it with ⌘+K');
}
},
},
{
title: 'The "A" action',
hotkey: 'a',
description: 'Close dialog',
onAction(event) {
console.log('You activate the "A" action');
event.preventDefault();
},
}
);

dialog.actions = actions;
Expand Down
2 changes: 1 addition & 1 deletion src/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export type Action = {
/**
* Handler of the action (optional)
*/
onAction?: (action: Action, isDialogOpen?: boolean) => {keepOpen?: boolean};
onAction?: (event?: KeyboardEvent | CustomEvent) => boolean;

/**
* Tags of the action (optional)
Expand Down
12 changes: 7 additions & 5 deletions src/cmd-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@ export class CmdAction extends LitElement {
*/
private get hotkeys() {
if (this.action?.hotkey) {
const hotkeys = (typeof this.action.hotkey === 'object' ? this.action.hotkey?.key || '' : this.action.hotkey)
.replace('cmd', '⌘')
.replace('shift', '⇧')
.replace('alt', '⌥')
.replace('ctrl', '⌃')
const hotkeys = this.action.hotkey
.replace('$mod', /Mac|iPod|iPhone|iPad/.test(window.navigator.userAgent) ? '⌘' : 'Ctrl')
.replace('Meta', '⌘')
.replace('Control', '⌃')
.replace('Shift', '⇧')
.replace('Alt', '⌥')
.replace('Control', '⌃')
.split('+');
return hotkeys.length > 0 ? html`<span>${repeat(hotkeys, hotkey => html`<kbd part="kbd">${hotkey}</kbd>`)}</span>` : '';
}
Expand Down
115 changes: 59 additions & 56 deletions src/cmd-dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import {live} from 'lit/directives/live.js';
import {repeat} from 'lit/directives/repeat.js';
import {unsafeHTML} from 'lit/directives/unsafe-html.js';
import Fuse from 'fuse.js';
import hotkeys from 'hotkeys-js';
import {type Hotkeys} from 'hotkeys-js';
import {tinykeys} from 'tinykeys';
import {type Action} from './action.js';
import {type CmdAction} from './cmd-action.js';
import './cmd-action.js'; // eslint-disable-line import/no-unassigned-import
Expand All @@ -32,8 +31,10 @@ export class CmdDialog extends LitElement {

/**
* Open dialog hotkey
* Meta+K (Mac) or Ctrl+K (Windows)
* @see https://github.com/jamiebuilds/tinykeys
*/
@property({type: String}) hotkey = 'cmd+k,ctrl+k';
@property({type: String}) hotkey = '$mod+k';

/**
* Callback when dialog is closed (optional)
Expand All @@ -55,12 +56,6 @@ export class CmdDialog extends LitElement {
},
}) actions = [] as Action[];

/**
* Hotkeys instance
* @private
*/
@state() private readonly _hotkeys: Hotkeys = hotkeys.noConflict();

/**
* Search input value
* @private
Expand All @@ -85,13 +80,6 @@ export class CmdDialog extends LitElement {
*/
private fuse: Fuse<Action> | undefined;

/**
* Return the hotkeys instance.
*/
get hotkeys(): Hotkeys {
return this._hotkeys;
}

/**
* Return the dialog element.
*/
Expand All @@ -114,6 +102,13 @@ export class CmdDialog extends LitElement {
return this._selected ? this._results.indexOf(this._selected) : -1;
}

/**
* Return if the dialog is open.
*/
get isOpen() {
return this.dialog.open;
}

/**
* Open the dialog.
*/
Expand All @@ -129,63 +124,71 @@ export class CmdDialog extends LitElement {
*/
public close() {
this.input.value = '';
this._hotkeys.setScope('outside');
this.dialog.close();
this.onClose();
}

/**
* Toggle the dialog.
*/
public toggle() {
if (this.dialog.open) {
this.close();
} else {
this.open();
}
}

override connectedCallback() {
super.connectedCallback();

// Open dialog
this._hotkeys(this.hotkey, 'all', event => {
this.open();
this.hotkeys.setScope('dialog');
// Activate/deactivate dialog
const toggleDialog = (event: KeyboardEvent) => {
this.toggle();
event.preventDefault();
});
};

// Select next
this._hotkeys('down,tab', 'dialog', event => {
this._selected = this._selectedIndex >= this._results.length - 1 ? this._results[0] : this._results[this._selectedIndex + 1];
event.preventDefault();
});
for (const hotkey of this.hotkey.split('|')) {
tinykeys(window, {[hotkey]: toggleDialog});
}

// Select previous
this._hotkeys('up,shift+tab', 'dialog', event => {
const navigate = {
next: (event: KeyboardEvent) => {
this._selected = this._selectedIndex >= this._results.length - 1 ? this._results[0] : this._results[this._selectedIndex + 1];
event.preventDefault();
},
prev: (event: KeyboardEvent) => {
this._selected = this._selectedIndex === 0 ? this._results[this._results.length - 1] : this._results[this._selectedIndex - 1];
event.preventDefault();
},
);

// Trigger action
this._hotkeys('enter', 'dialog', event => {
this._triggerAction(this._results[this._selectedIndex]);
event.preventDefault();
open: (event: KeyboardEvent) => {
this._triggerAction(this._results[this._selectedIndex], event);
event.preventDefault();
},
};

// Navigate through actions
tinykeys(this, {
ArrowUp: navigate.prev,
'Shift+Tab': navigate.prev,
ArrowDown: navigate.next,
Tab: navigate.next,
Enter: navigate.open,
});
}

override disconnectedCallback() {
super.disconnectedCallback();
// Unregister hotkeys
this._hotkeys.unbind(this.hotkey, 'all');
this._hotkeys.unbind('down,tab', 'dialog');
this._hotkeys.unbind('up,shift+tab', 'dialog');
this._hotkeys.unbind('enter', 'dialog');
}

override update(changedProperties: PropertyValues<this>) {
if (changedProperties.has('actions')) {
// Register action hotkeys
for (const action of this.actions.filter(item => Boolean(item.hotkey))) {
const {key, ...options} = typeof action.hotkey === 'object' ? action.hotkey : {key: action.hotkey!, scope: 'all'};

hotkeys(
key,
options,
(event: KeyboardEvent) => {
event.preventDefault();
this._triggerAction(action);
const hotkeys = (action.hotkey!).split('|');
for (const hotkey of hotkeys) {
tinykeys(window, {
[hotkey]: (event: KeyboardEvent) => {
this._triggerAction(action, event);
},
});
}
}

// Setup fuse search
Expand Down Expand Up @@ -241,7 +244,7 @@ export class CmdDialog extends LitElement {
this._actionFocused(action, event);
}}
@actionSelected=${(event: CustomEvent<Action>) => {
this._triggerAction(event.detail);
this._triggerAction(event.detail, event);
}}
></cmd-action>
`)}
Expand Down Expand Up @@ -321,9 +324,10 @@ export class CmdDialog extends LitElement {
/**
* Trigger the action.
* @param action
* @param event
* @private
*/
private _triggerAction(action?: Action) {
private _triggerAction(action?: Action, event?: KeyboardEvent | CustomEvent) {
this._selected = action;

// Fire selected event even when action is empty/not selected,
Expand All @@ -339,8 +343,7 @@ export class CmdDialog extends LitElement {
// Trigger action
if (action) {
if (action.onAction) {
const result = action.onAction(action);
if (!result?.keepOpen) {
if (action.onAction(event)) {
this.close();
}
} else if (action.url) {
Expand Down

1 comment on commit a60eb12

@vercel
Copy link

@vercel vercel bot commented on a60eb12 Dec 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

cmd-dialog – ./

cmd-dialog-git-main-ozzyczech.vercel.app
cmd-dialog-ozzyczech.vercel.app
cmd-dialog.vercel.app

Please sign in to comment.