cmdk hotkeys work if input if focsed

This commit is contained in:
Nikita Galaiko 2023-04-24 12:23:19 +02:00
parent 70d61314a6
commit 0d21f17509
3 changed files with 153 additions and 153 deletions

View File

@ -61,7 +61,7 @@
action.href.startsWith('http') || action.href.startsWith('mailto') action.href.startsWith('http') || action.href.startsWith('mailto')
? open(action.href) ? open(action.href)
: goto(action.href); : goto(action.href);
modal?.hide(); modal?.close();
} else if (Action.isGroup(action)) { } else if (Action.isGroup(action)) {
selectedGroup.set(action); selectedGroup.set(action);
} }
@ -115,11 +115,9 @@
if (command.hotkey) { if (command.hotkey) {
unregisterCommandHotkeys.push( unregisterCommandHotkeys.push(
tinykeys(window, { tinykeys(window, {
[command.hotkey]: (event: KeyboardEvent) => { [command.hotkey]: () => {
const target = event.target as HTMLElement; if (!modal?.isOpen()) return;
if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA') return; trigger(command.action);
// only trigger if the modal is visible
modal?.isOpen() && trigger(command.action);
} }
}) })
); );

View File

@ -5,182 +5,184 @@ import type { SvelteComponent } from 'svelte';
import { format, startOfISOWeek, startOfMonth, subDays, subMonths, subWeeks } from 'date-fns'; import { format, startOfISOWeek, startOfMonth, subDays, subMonths, subWeeks } from 'date-fns';
type ActionLink = { type ActionLink = {
href: string; href: string;
}; };
interface Newable<ReturnType> { interface Newable<ReturnType> {
new (...args: any[]): ReturnType; new(...args: any[]): ReturnType;
} }
export type Action = ActionLink | Group; export type Action = ActionLink | Group;
export namespace Action { export namespace Action {
export const isLink = (action: Action): action is ActionLink => 'href' in action; export const isLink = (action: Action): action is ActionLink => 'href' in action;
export const isGroup = (action: Action): action is Group => 'commands' in action; export const isGroup = (action: Action): action is Group => 'commands' in action;
} }
export type Command = { export type Command = {
title: string; title: string;
hotkey?: string; hotkey?: string;
action: Action; action: Action;
icon?: Newable<SvelteComponent>; icon?: Newable<SvelteComponent>;
}; };
export type Group = { export type Group = {
title: string; title: string;
description?: string; description?: string;
commands: Command[]; commands: Command[];
}; };
const goToProjectGroup = ({ projects, input }: { projects: Project[]; input: string }): Group => ({ const goToProjectGroup = ({ projects, input }: { projects: Project[]; input: string }): Group => ({
title: 'Go to project', title: 'Go to project',
commands: projects commands: projects
.map((project) => ({ .filter(({ title }) => input.length === 0 || title.toLowerCase().includes(input.toLowerCase()))
title: project.title, .map((project, i) => ({
action: { title: project.title,
href: `/projects/${project.id}/` hotkey: `${i + 1}`,
}, action: {
icon: IconProject href: `/projects/${project.id}/`
})) },
.filter(({ title }) => input.length === 0 || title.toLowerCase().includes(input.toLowerCase())) icon: IconProject
}))
}); });
const actionsGroup = ({ project, input }: { project: Project; input: string }): Group => ({ const actionsGroup = ({ project, input }: { project: Project; input: string }): Group => ({
title: 'Actions', title: 'Actions',
commands: [ commands: [
{ {
title: 'Commit', title: 'Commit',
hotkey: 'Shift+C', hotkey: 'Shift+C',
action: { action: {
href: `/projects/${project.id}/commit/` href: `/projects/${project.id}/commit/`
}, },
icon: GitCommitIcon icon: GitCommitIcon
}, },
{ {
title: 'Terminal', title: 'Terminal',
hotkey: 'Shift+T', hotkey: 'Shift+T',
action: { action: {
href: `/projects/${project?.id}/terminal/` href: `/projects/${project?.id}/terminal/`
}, },
icon: IconTerminal icon: IconTerminal
}, },
{ {
title: 'Replay History', title: 'Replay History',
action: { hotkey: 'Shift+R',
title: 'Replay working history', action: {
commands: [ title: 'Replay working history',
{ commands: [
title: 'Eralier today', {
icon: RewindIcon, title: 'Eralier today',
hotkey: '1', icon: RewindIcon,
action: { hotkey: '1',
href: `/projects/${project.id}/player/${format(new Date(), 'yyyy-MM-dd')}/` action: {
} href: `/projects/${project.id}/player/${format(new Date(), 'yyyy-MM-dd')}/`
}, }
{ },
title: 'Yesterday', {
icon: RewindIcon, title: 'Yesterday',
hotkey: '2', icon: RewindIcon,
action: { hotkey: '2',
href: `/projects/${project.id}/player/${format( action: {
subDays(new Date(), 1), href: `/projects/${project.id}/player/${format(
'yyyy-MM-dd' subDays(new Date(), 1),
)}/` 'yyyy-MM-dd'
} )}/`
}, }
{ },
title: 'The day before yesterday', {
icon: RewindIcon, title: 'The day before yesterday',
hotkey: '3', icon: RewindIcon,
action: { hotkey: '3',
href: `/projects/${project.id}/player/${format( action: {
subDays(new Date(), 2), href: `/projects/${project.id}/player/${format(
'yyyy-MM-dd' subDays(new Date(), 2),
)}/` 'yyyy-MM-dd'
} )}/`
}, }
{ },
title: 'The beginning of last week', {
icon: RewindIcon, title: 'The beginning of last week',
hotkey: '4', icon: RewindIcon,
action: { hotkey: '4',
href: `/projects/${project.id}/player/${format( action: {
startOfISOWeek(subWeeks(new Date(), 1)), href: `/projects/${project.id}/player/${format(
'yyyy-MM-dd' startOfISOWeek(subWeeks(new Date(), 1)),
)}/` 'yyyy-MM-dd'
} )}/`
}, }
{ },
title: 'The beginning of last month', {
icon: RewindIcon, title: 'The beginning of last month',
hotkey: '5', icon: RewindIcon,
action: { hotkey: '5',
href: `/projects/${project.id}/player/${format( action: {
startOfMonth(subMonths(new Date(), 1)), href: `/projects/${project.id}/player/${format(
'yyyy-MM-dd' startOfMonth(subMonths(new Date(), 1)),
)}/` 'yyyy-MM-dd'
} )}/`
} }
] }
}, ]
icon: RewindIcon },
} icon: RewindIcon
].filter(({ title }) => input.length === 0 || title.toLowerCase().includes(input.toLowerCase())) }
].filter(({ title }) => input.length === 0 || title.toLowerCase().includes(input.toLowerCase()))
}); });
const fileGroup = ({ const fileGroup = ({
project, project,
input input
}: { }: {
project: Project; project: Project;
input: string; input: string;
}): Group | Promise<Group> => }): Group | Promise<Group> =>
input.length === 0 input.length === 0
? { ? {
title: 'Files', title: 'Files',
description: 'type part of a file name', description: 'type part of a file name',
commands: [] commands: []
} }
: matchFiles({ projectId: project.id, matchPattern: input }).then((files) => ({ : matchFiles({ projectId: project.id, matchPattern: input }).then((files) => ({
title: 'Files', title: 'Files',
description: files.length === 0 ? `no files containing '${input}'` : '', description: files.length === 0 ? `no files containing '${input}'` : '',
commands: files.map((file) => ({ commands: files.map((file) => ({
title: file, title: file,
action: { action: {
href: '/' href: '/'
}, },
icon: IconFile icon: IconFile
})) }))
})); }));
const supportGroup = ({ input }: { input: string }): Group => ({ const supportGroup = ({ input }: { input: string }): Group => ({
title: 'Help & Support', title: 'Help & Support',
commands: [ commands: [
{ {
title: 'Documentation', title: 'Documentation',
action: { action: {
href: `https://docs.gitbutler.com` href: `https://docs.gitbutler.com`
}, },
icon: FileIcon icon: FileIcon
}, },
{ {
title: 'Discord', title: 'Discord',
action: { action: {
href: `https://discord.gg/MmFkmaJ42D` href: `https://discord.gg/MmFkmaJ42D`
}, },
icon: GitCommitIcon icon: GitCommitIcon
} }
].filter(({ title }) => input.length === 0 || title.toLowerCase().includes(input.toLowerCase())) ].filter(({ title }) => input.length === 0 || title.toLowerCase().includes(input.toLowerCase()))
}); });
export default (params: { projects: Project[]; project?: Project; input: string }) => { export default (params: { projects: Project[]; project?: Project; input: string }) => {
const { projects, input, project } = params; const { projects, input, project } = params;
const groups = []; const groups = [];
!project && groups.push(goToProjectGroup({ projects, input })); !project && groups.push(goToProjectGroup({ projects, input }));
project && groups.push(actionsGroup({ project, input })); project && groups.push(actionsGroup({ project, input }));
project && groups.push(fileGroup({ project, input })); project && groups.push(fileGroup({ project, input }));
groups.push(supportGroup({ input })); groups.push(supportGroup({ input }));
return groups; return groups;
}; };

View File

@ -12,7 +12,7 @@
}; };
export const isOpen = () => open; export const isOpen = () => open;
const close = () => { export const close = () => {
open = false; open = false;
dialog.close(); dialog.close();
}; };