-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
⌨️ Keyboard shortcuts/navigation for create and upload #736
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
import React from 'react'; | ||
import React, { useEffect } from 'react'; | ||
import _ from 'lodash'; | ||
import { addShortcut, removeShortcut } from 'app/features/global/services/shortcut-service'; | ||
|
||
export type ButtonTheme = 'primary' | 'secondary' | 'danger' | 'default' | 'outline' | 'dark' | 'white' | 'green'; | ||
|
||
|
@@ -12,6 +13,7 @@ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> { | |
loading?: boolean; | ||
disabled?: boolean; | ||
children?: React.ReactNode; | ||
shortcut?: string; | ||
} | ||
|
||
export const Button = (props: ButtonProps) => { | ||
|
@@ -42,9 +44,7 @@ export const Button = (props: ButtonProps) => { | |
className = | ||
'text-zinc-300 border-0 bg-zinc-900 hover:bg-zinc-800 hover:text-white active:bg-zinc-900'; | ||
|
||
if (props.theme === 'green') | ||
className = | ||
'text-zinc-300 border-0 bg-green-700'; | ||
if (props.theme === 'green') className = 'text-zinc-300 border-0 bg-green-700'; | ||
|
||
if (disabled) className += ' opacity-50 pointer-events-none'; | ||
|
||
|
@@ -58,6 +58,46 @@ export const Button = (props: ButtonProps) => { | |
else className = className + ' w-9 !p-0 justify-center'; | ||
} | ||
|
||
// translate shortcuts | ||
const translateDynamicShortcut = (shortcut: string): string => | ||
shortcut.startsWith('CmdOrCtrl+Shift') | ||
? `⇧⌘${shortcut.replace('CmdOrCtrl+Shift+', '')}` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this symbol means nothing to non mac users =/ i think we should use https://developer.mozilla.org/en-US/docs/Web/API/Navigator/platform to show control if it isn't mac |
||
: shortcut.startsWith('CmdOrCtrl') | ||
? `⌘${shortcut.replace('CmdOrCtrl+', '')}` | ||
: shortcut; | ||
|
||
if (props.shortcut) { | ||
useEffect(() => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i think the if (shortcut) should be inside the useEffect callback, because useEffect and friends need to always be called in the same exact order and sequence |
||
const handler = (event?: KeyboardEvent) => { | ||
// Disable default behavior for the shortcut | ||
if (event) { | ||
event.preventDefault(); | ||
event.stopPropagation(); | ||
} | ||
props.onClick && props.onClick({} as any); | ||
}; | ||
|
||
const shortcut = props.shortcut || ''; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is inside |
||
|
||
// Add shortcut with default behavior prevention | ||
addShortcut({ | ||
shortcut, | ||
handler: event => { | ||
handler(event as KeyboardEvent); | ||
}, | ||
}); | ||
|
||
return () => { | ||
removeShortcut({ | ||
shortcut, | ||
handler: event => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function instance will not be the same as the one when added. If the event source tests for the handler it will fail, if it doesn't, this is useless |
||
handler(event as KeyboardEvent); | ||
}, | ||
}); | ||
}; | ||
}, [props.onClick, props.shortcut]); | ||
} | ||
|
||
return ( | ||
<button | ||
type="button" | ||
|
@@ -103,6 +143,15 @@ export const Button = (props: ButtonProps) => { | |
/> | ||
)} | ||
{props.children} | ||
{props.shortcut && props.theme !== 'white' && ( | ||
<span | ||
className={`ml-2 text-xs ${ | ||
props.theme === 'primary' ? 'text-gray-200' : 'text-gray-400' | ||
}`} | ||
> | ||
{translateDynamicShortcut(props.shortcut)} | ||
</span> | ||
)} | ||
</button> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -168,10 +168,19 @@ export const CreateModal = ({ | |
}; | ||
|
||
const CreateModalOption = (props: { icon: ReactNode; text: string; onClick: () => void }) => { | ||
// on press enter | ||
const handleKeyPress = (e: React.KeyboardEvent<HTMLDivElement>) => { | ||
if (e.key === 'Enter') { | ||
props.onClick(); | ||
} | ||
}; | ||
|
||
return ( | ||
<div | ||
onClick={props.onClick} | ||
className="flex flex-row p-4 dark:bg-zinc-900 dark:text-white bg-zinc-100 hover:bg-opacity-75 cursor-pointer rounded-md m-2" | ||
className="flex flex-row p-4 dark:bg-zinc-900 dark:text-white bg-zinc-100 hover:bg-opacity-75 cursor-pointer rounded-md m-2 focus:bg-zinc-800 dark:focus:bg-zinc-800 outline-none focus:border-none" | ||
tabIndex={0} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. tabIndex is a number that's why. It is required for tab to work/navigate the modal items. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. html dom attribute values are always strings anyway, see https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute . what's happening here is just making react type it first and maybe not guess its a constant it could optimise out |
||
onKeyUp={handleKeyPress} | ||
> | ||
<div className="flex items-center justify-center">{props.icon}</div> | ||
<div className="grow flex items-center ml-2"> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -162,6 +162,7 @@ export default () => { | |
|
||
<Button | ||
onClick={() => uploadItemModal()} | ||
shortcut='CmdOrCtrl+U' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is the modifier necessary ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes |
||
size="lg" | ||
theme="primary" | ||
className="w-full mb-2 justify-center" | ||
|
@@ -171,6 +172,7 @@ export default () => { | |
</Button> | ||
<Button | ||
onClick={() => openItemModal()} | ||
shortcut='CmdOrCtrl+C' | ||
size="lg" | ||
theme="secondary" | ||
className="w-full mb-2 justify-center" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function should probably be in
app/features/global/services/shortcut-service